【问题标题】:Getting selected ItemsControl Item获取选中的ItemsControl Item
【发布时间】:2017-02-23 22:35:48
【问题描述】:

目前正在创建我的 windows 10 应用程序,我正在尝试按照 mvvm 模式在画布上移动一个矩形。下面的代码有效,但我通过在我的视图模型 PointerDragEvent 中使用 uielemnt 破坏了 mvvm。

   Dim rec = TryCast(e.OriginalSource, Button)
   Dim selrecitem = TryCast(rec.DataContext, RectItem)

问题

是否有一种非 hacky/正确的方式来做到这一点?

如何检索我单击的项目并将其传递给我的视图模型?

画布上的所有项目都将动态创建。

<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App11"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity" xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
x:Class="App11.MainPage"
mc:Ignorable="d">

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.DataContext>
        <local:myviewmodel/>
    </Grid.DataContext>
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="8*"/>
        <RowDefinition Height="*"/>

    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="11*"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <ItemsControl x:Name="itemsControl" Grid.Column="1" Grid.Row="1" ItemsSource="{Binding myrectangles, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
        <Interactivity:Interaction.Behaviors>
            <Core:EventTriggerBehavior EventName="ManipulationDelta">
                <Core:CallMethodAction TargetObject="{Binding Mode=OneWay}" MethodName="PointerDrag"/>
            </Core:EventTriggerBehavior>
        </Interactivity:Interaction.Behaviors>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas Background="White">

                </Canvas>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>

        <ItemsControl.ItemTemplate>

            <DataTemplate>
                <Button x:Name="PageItem" Background="Transparent" BorderBrush="DodgerBlue" Width="{Binding Width}" Height="{Binding Height}" ManipulationMode="TranslateX, TranslateY" IsHitTestVisible="{Binding IsChecked, ElementName=SelectToolButton, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Center" Command="{Binding SendMyDC, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
                    <Button.RenderTransform>
                        <CompositeTransform TranslateX="{Binding X}" TranslateY="{Binding Y}"/>
                    </Button.RenderTransform>

                </Button>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>

</Grid>

视图模型

Public Class myviewmodel
Implements INotifyPropertyChanged
Private Sub NotifyPropertyChanged(Optional propertyName As String = "")
    RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub

Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private _myrectangles As New ObservableCollection(Of RectItem)
Public Property myrectangles As ObservableCollection(Of RectItem)
    Get
        Return _myrectangles
    End Get
    Set(value As ObservableCollection(Of RectItem))
        _myrectangles = value
        NotifyPropertyChanged()
    End Set
End Property

Public Sub New()
    Dim newrect As New RectItem
    newrect.Height = 100
    newrect.Width = 150
    newrect.X = 50
    newrect.Y = 50
    _myrectangles.Add(newrect)
End Sub

Public Sub PointerDrag(sender As Object, e As ManipulationDeltaRoutedEventArgs)
    Dim dx_point = e.Delta.Translation.X
    NotifyPropertyChanged()
    Dim dy_point = e.Delta.Translation.Y
    NotifyPropertyChanged()
    Dim rec = TryCast(e.OriginalSource, Button)
    Dim selrecitem = TryCast(rec.DataContext, RectItem)

    selrecitem.X += dx_point
    selrecitem.Y += dy_point
End Sub
End Class

矩形项目类

Public Class RectItem
Implements INotifyPropertyChanged
Private Sub NotifyPropertyChanged(Optional propertyName As String = "")
    RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub

Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

Public Property X As Double
    Get
        Return m_X
    End Get
    Set
        m_X = Value
        NotifyPropertyChanged()
    End Set
End Property
Private m_X As Double
Public Property Y As Double
    Get
        Return m_Y
    End Get
    Set
        m_Y = Value
        NotifyPropertyChanged()
    End Set
End Property
Private m_Y As Double
Public Property Width As Double
    Get
        Return m_Width
    End Get
    Set
        m_Width = Value
        NotifyPropertyChanged()
    End Set
End Property
Private m_Width As Double
Public Property Height As Double
    Get
        Return m_Height
    End Get
    Set
        m_Height = Value
        NotifyPropertyChanged()
    End Set
End Property
Private m_Height As Double
End Class

编辑 #1 我没有使用项目控件,而是使用 ListView/Listbox 并将所选项目绑定到我的 vewimodel 中的属性。使用 listview 时,让 item Presenter 与它所展示的 item 对齐存在一些问题,并且与 listbox 对齐,指针向上事件不会触发。

<ListView x:Name="itemsControl" Grid.Column="1" Grid.Row="1" ItemsSource="{Binding myrectangles, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Background="#FFF2F2F2"  SelectedItem="{Binding selectedrec, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">

        <Interactivity:Interaction.Behaviors>
            <Core:EventTriggerBehavior EventName="ManipulationDelta">
                <Core:CallMethodAction TargetObject="{Binding Mode=OneWay}" MethodName="PointerDrag"/>
            </Core:EventTriggerBehavior>
            <Core:EventTriggerBehavior EventName="PointerPressed">
                <Core:CallMethodAction TargetObject="{Binding Mode=OneWay}" MethodName="PointerPressed"/>
            </Core:EventTriggerBehavior>
            <!--<Core:EventTriggerBehavior EventName="PointerReleased">
                <Core:CallMethodAction TargetObject="{Binding Mode=OneWay}" MethodName="Up"/>
            </Core:EventTriggerBehavior>-->
        </Interactivity:Interaction.Behaviors>



        <ListView.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas Background="White">

                </Canvas>
            </ItemsPanelTemplate>
        </ListView.ItemsPanel>
       <!--<ListView.ItemContainerStyle>
            <Style TargetType="ListViewItem">

                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate>
                            <ContentPresenter/>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>


            </Style>
        </ListView.ItemContainerStyle>-->
        <ListView.ItemTemplate>

            <DataTemplate>
                <Rectangle x:Name="PageItem" Width="{Binding Width}" Height="{Binding Height}"  Fill="Transparent" Stroke="DodgerBlue" ManipulationMode="TranslateX, TranslateY" IsHitTestVisible="{Binding IsChecked, ElementName=SelectToolButton, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Center" >


                    <Rectangle.RenderTransform>
                        <CompositeTransform TranslateX="{Binding X}" TranslateY="{Binding Y}"/>
                    </Rectangle.RenderTransform>

                </Rectangle>

            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>

</Grid>  

更新的视图模型

 Public _selectedrec As New RectItem
 Public Property selectedrec As RectItem
    Get
        Return _selectedrec
    End Get
    Set(value As RectItem)
        _selectedrec = value
        NotifyPropertyChanged()
    End Set
End Property

 Public Sub PointerDrag(sender As Object, e As ManipulationDeltaRoutedEventArgs)

    Dim dx_point = e.Delta.Translation.X
    NotifyPropertyChanged()
    Dim dy_point = e.Delta.Translation.Y
    NotifyPropertyChanged()


    selectedrec.X += dx_point
    NotifyPropertyChanged()
    selectedrec.Y += dy_point
    NotifyPropertyChanged()



End Sub

问题示例

没有 itemcontainerstyle - 尝试将对象拖动到画布的 0,0 时

MyHalfFix - 取消注释 ItemContainerStyle - 一切正常,我的目标是

在实际程序中实现时的总体问题

红色的Item是listviewitem Presenter。它被正确绘制,但定位与绘制的矩形不对齐,它总是捕捉到画布的 0,0 点。我可以让它透明,但你也可以在绘制矩形时看到它没有正确跟随光标

【问题讨论】:

    标签: vb.net xaml mvvm uwp windows-10-universal


    【解决方案1】:

    您可以将InvokeCommandAction 用于ManipulationDelta 事件将避免破坏MVVM 模式。 InvokeCommandAction 可以将参数传递给viewmodel,我们可以通过参数传递selecteditemManipulationDelta 的对象应该是按钮而不是 ItemsControlInvokeCommandAction 将起作用,您实际拖动的是按钮本身。更新代码如下:

    XAML

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Name="root">
      ...
           <ItemsControl.ItemTemplate>
               <DataTemplate>
                   <Button x:Name="PageItem" Background="Transparent" BorderBrush="DodgerBlue" Width="{Binding Width}" Height="{Binding Height}" ManipulationMode="TranslateX, TranslateY" IsHitTestVisible="{Binding IsChecked, ElementName=SelectToolButton, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Center" >
                       <Interactivity:Interaction.Behaviors>
                           <Core:EventTriggerBehavior EventName="ManipulationDelta" SourceObject="{Binding ElementName=PageItem}">
                               <Core:InvokeCommandAction Command="{Binding DataContext.SendMyDC, ElementName=root}" CommandParameter="{Binding}"></Core:InvokeCommandAction>
                               <Core:CallMethodAction TargetObject="{Binding DataContext, ElementName=root, Mode=OneWay}" MethodName="PointerDrag" />
                           </Core:EventTriggerBehavior>
                       </Interactivity:Interaction.Behaviors>
                       <Button.RenderTransform>
                           <CompositeTransform TranslateX="{Binding X}" TranslateY="{Binding Y}"/>
                       </Button.RenderTransform>
                   </Button>
               </DataTemplate>
           </ItemsControl.ItemTemplate>
       </ItemsControl>
    </Grid>
    

    我的视图模型

    Public Class myviewmodel  
    ...
    Private m_sendCommand As IDelegateCommand
    Public Property SendMyDC As IDelegateCommand
        Get
            Return m_sendCommand
        End Get
        Protected Set(value As IDelegateCommand)
            m_sendCommand = value
        End Set
    End Property
    
    Public Sub New()
        Me.SendMyDC = New DelegateCommand(AddressOf ExecuteSendMyDC)
        Dim newrect As New RectItem
        newrect.Height = 100
        newrect.Width = 150
        newrect.X = 50
        newrect.Y = 50
        _myrectangles.Add(newrect)
    End Sub
    
    Dim selrecitem As RectItem
    Public Sub PointerDrag(sender As Object, e As ManipulationDeltaRoutedEventArgs)
        Dim dx_point = e.Delta.Translation.X
        NotifyPropertyChanged()
        Dim dy_point = e.Delta.Translation.Y
        NotifyPropertyChanged()
        'Dim rec = TryCast(e.OriginalSource, Button)
        'Dim selrecitem = TryCast(rec.DataContext, RectItem)
        If selrecitem IsNot Nothing Then
            selrecitem.X += dx_point
            selrecitem.Y += dy_point
        End If
    End Sub
    
    Private Sub ExecuteSendMyDC(param As Object)
        selrecitem = CType(param, RectItem)
    End Sub 
    End Class
    

    DelegateCommand 类

    Public Class DelegateCommand
    Implements IDelegateCommand
    Private _execute As Action(Of Object)
    Private _canExecute As Func(Of Object, Boolean)
    #Region "Constructors"
    Public Sub New(execute As Action(Of Object), canExecute As Func(Of Object, Boolean))
        Me._execute = execute
        Me._canExecute = canExecute
    End Sub
    Public Sub New(execute As Action(Of Object))
        Me._execute = execute
        Me._canExecute = AddressOf Me.AlwaysCanExecute
    End Sub
    #End Region
    #Region "IDelegateCommand"
    Private Function AlwaysCanExecute(param As Object) As Boolean
        Return True
    End Function
    Public Function CanExecute(parameter As Object) As Boolean Implements System.Windows.Input.ICommand.CanExecute
        Return _canExecute(parameter)
    End Function
    Public Event CanExecuteChanged(sender As Object, e As EventArgs) Implements System.Windows.Input.ICommand.CanExecuteChanged
    Public Sub Execute(parameter As Object) Implements System.Windows.Input.ICommand.Execute
        _execute(parameter)
    End Sub
    
    Public Sub RaiseCanExecuteChanged() Implements IDelegateCommand.RaiseCanExecuteChanged
        RaiseEvent CanExecuteChanged(Me, EventArgs.Empty)
    End Sub
    #End Region
    End Class
    Public Interface IDelegateCommand
        Inherits ICommand
        Sub RaiseCanExecuteChanged()
    End Interface
    

    DelegateCommand 是一个通用类,供您进一步调用其他命令。更多详情请参考Command binding inside DataTemplate

    【讨论】:

    • 我尝试实现您提供的代码,但没有成功。当我开始拖动时,没有任何东西被提升。尽管我会更新问题,但我确实找到了解决方法
    • @Twinnaz 我把我测试过的完整demo上传到github你可以参考,如果还有问题请随时告诉我。
    • 在我提供的示例中效果很好,谢谢。不幸的是,它在我试图实现它的整个程序中不起作用。按钮将被动态绘制。我可以提供代码,但我认为这里发布的内容太多了。
    • @Twinnaz 如果方便,请您上传到 github 或 onedrive 或其他地方。你知道没有代码我很难猜出它有什么问题。否则我只能猜测。
    • @Twinnaz 我在我身边测试过它并且可以重现。这完全是一个关于风格的新问题。 ListBox 有自己的布局,你实际拖动的是你在项模板中定义的内部控件,而不是列表框项本身。你为什么使用列表框?正如你所发现的,然后用矩形添加一个全色就可以满足你的要求,对吧?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-08-29
    • 2011-06-30
    • 1970-01-01
    • 1970-01-01
    • 2015-09-14
    • 2011-09-05
    • 1970-01-01
    相关资源
    最近更新 更多