Get Selected TreeViewItem Using MVVM

后端 未结 5 681
挽巷
挽巷 2020-12-08 11:26

So someone suggested using a WPF TreeView, and I thought: \"Yeah, that seems like the right approach.\" Now, hours and hours later, I simply can\'t believe how

相关标签:
5条回答
  • 2020-12-08 11:38

    Also late to the party but as an alternative for MVVMLight users:

    1. Bind the TreeViewItem to your ViewModel to get changes of the IsSelected property.
    2. Create a MVVMLight Message (like PropertyChangeMessage) sending the SelectedItem ViewModel or Model item
    3. Register your TreeView host (or some other ViemModels if necessary) to listen for this message.

    The whole implementation is very fast and works fine.

    Here the IsSelected Property (SourceItem is the Model part of the selected ViewModel item):

           Public Property IsSelected As Boolean
            Get
                Return _isSelected
            End Get
            Set(value As Boolean)
                If Me.HasImages Then
                    _isSelected = value
                    OnPropertyChanged("IsSelected")
                    Messenger.Default.Send(Of SelectedImageFolderChangedMessage)(New SelectedImageFolderChangedMessage(Me, SourceItem, "SelectedImageFolder"))
                Else
                    Me.IsExpanded = Not Me.IsExpanded
                End If
            End Set
        End Property
    

    and here the VM host code:

        Messenger.Default.Register(Of SelectedImageFolderChangedMessage)(Me, AddressOf NewSelectedImageFolder)
    
        Private Sub NewSelectedImageFolder(msg As SelectedImageFolderChangedMessage)
            If msg.PropertyName = "SelectedImageFolder" Then
                Me.SelectedFolderItem = msg.NewValue
            End If
        End Sub
    
    0 讨论(0)
  • 2020-12-08 11:39

    Based on Martin's answer I made a simple application showing how to apply the proposed solution.

    The sample code uses the Cinch V2 framework to support MVVM but it can be easily changed to use the framework of you preference.

    For those interested, here is the code on GitHub

    Hope it helps.

    0 讨论(0)
  • 2020-12-08 11:42

    I would probably use the SelectedItemChanged event to set a respective property on your VM.

    0 讨论(0)
  • 2020-12-08 11:48

    To do what you want you can modify the ItemContainerStyle of the TreeView:

    <TreeView>
      <TreeView.ItemContainerStyle>
        <Style TargetType="TreeViewItem">
          <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
        </Style>
      </TreeView.ItemContainerStyle>
    </TreeView>
    

    Your view-model (the view-model for each item in the tree) then has to expose a boolean IsSelected property.

    If you want to be able to control if a particular TreeViewItem is expanded you can use a setter for that property too:

    <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
    

    Your view-model then has to expose a boolean IsExpanded property.

    Note that these properties work both ways so if the user selects a node in the tree the IsSelected property of the view-model will be set to true. On the other hand if you set IsSelected to true on a view-model the node in the tree for that view-model will be selected. And likewise with expanded.

    If you don't have a view-model for each item in the tree, well, then you should get one. Not having a view-model means that you are using your model objects as view-models, but for this to work these objects require an IsSelected property.

    To expose an SelectedItem property on your parent view-model (the one you bind to the TreeView and that has a collection of child view-models) you can implement it like this:

    public ChildViewModel SelectedItem {
      get { return Items.FirstOrDefault(i => i.IsSelected); }
    }
    

    If you don't want to track selection on each individual item on the tree you can still use the SelectedItem property on the TreeView. However, to be able to do it "MVVM style" you need to use a Blend behavior (available as various NuGet packages - search for "blend interactivity").

    Here I have added an EventTrigger that will invoke a command each time the selected item changes in the tree:

    <TreeView x:Name="treeView">
      <i:Interaction.Triggers>
        <i:EventTrigger EventName="SelectedItemChanged">
          <i:InvokeCommandAction
            Command="{Binding SetSelectedItemCommand}"
            CommandParameter="{Binding SelectedItem, ElementName=treeView}"/>
        </i:EventTrigger>
      </i:Interaction.Triggers>
    </TreeView>
    

    You will have to add a property SetSelectedItemCommand on the DataContext of the TreeView returning an ICommand. When the selected item of the tree view changes the Execute method on the command is called with the selected item as the parameter. The easiest way to create a command is probably to use a DelegateCommand (google it to get an implementation as it is not part of WPF).

    A perhaps better alternative that allows two-way binding without the clunky command is to use BindableSelectedItemBehavior provided by Steve Greatrex here on Stack Overflow.

    0 讨论(0)
  • 2020-12-08 11:55

    Somewhat late to the party but for those who are coming across this now, my solution was:

    1. Add a reference to 'System.Windows.Interactivity'
    2. Add the following code into your treeview element. <i:Interaction.Triggers> <i:EventTrigger EventName="SelectedItemChanged"> <i:InvokeCommandAction Command="{Binding SomeICommand}" CommandParameter="{Binding ElementName=treeviewName, Path=SelectedItem}" /> </i:EventTrigger> </i:Interaction.Triggers>

    This will allow you to use a standard MVVM ICommand binding to access the SelectedItem without having to use code behind or some long winded work around.

    0 讨论(0)
提交回复
热议问题