Search code examples
c#wpf

Set isselected properties of TreeViewItem in TreeView WPF, MVVM


i am a newbie to WPF mvvm, i created a treeview and i want to set isSelected value of a specific treeviewitem in this tree (for example, item with "19-ASDFDSSD") but i dont know how to do it. Can you help me? Any help will be appreciated.

treeview wpf code

JobsDTOClass


Solution

  • TreeView.SelectedItem is read only, so a value can't be pushed from the ViewModel.

    The way I've handled this is via the use of a Behavior class.

    1 - Create a base class for TreeView item data, that includes IsSelected and IsExpanded properties.

    public class perTreeViewItemViewModelBase : perViewModelBase
    {
        private bool _isSelected;
    
        public bool IsSelected
        {
            get => _isSelected;
            set => Set(nameof(IsSelected), ref _isSelected, value);
        }
    
        private bool _isExpanded;
    
        public bool IsExpanded
        {
            get => _isSelected;
            set => Set(nameof(IsExpanded), ref _isExpanded, value);
        }
    
    
        ...
    }
    

    2 - Create a helper class for TreeView controls to implement the behavior, with a BoundSelectedItem attached property.

    public class perTreeViewHelper : Behavior<TreeView>
    {
        public object BoundSelectedItem
        {
            get => GetValue(BoundSelectedItemProperty);
            set => SetValue(BoundSelectedItemProperty, value);
        }
    
        public static readonly DependencyProperty BoundSelectedItemProperty =
            DependencyProperty.Register("BoundSelectedItem",
                typeof(object),
                typeof(perTreeViewHelper),
                new FrameworkPropertyMetadata(null,
                    FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
                    OnBoundSelectedItemChanged));
    
        private static void OnBoundSelectedItemChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            if (args.NewValue is perTreeViewItemViewModelBase item)
            {
                item.IsSelected = true;
            }
        }
    
        protected override void OnAttached()
        {
            base.OnAttached();
            AssociatedObject.SelectedItemChanged += OnTreeViewSelectedItemChanged;
        }
    
        protected override void OnDetaching()
        {
            AssociatedObject.SelectedItemChanged -= OnTreeViewSelectedItemChanged;
            base.OnDetaching();
        }
    
        private void OnTreeViewSelectedItemChanged(object obj, RoutedPropertyChangedEventArgs<object> args)
        {
            BoundSelectedItem = args.NewValue;
        }
    }
    

    3 - Create styles for TreeViewItem and TreeView to bind the IsSelected property of the perTreeViewItemViewModelBase instance to the corresponding TreeViewItem property

    <Style x:Key="perTreeViewItemContainerStyle" TargetType="{x:Type TreeViewItem}">
        <!--  Link the properties of perTreeViewItemViewModelBase to the corresponding ones on the TreeViewItem  -->
        <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
        <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
    
        ...
    
    </Style>
    
    <Style TargetType="{x:Type TreeView}">
        <Setter Property="ItemContainerStyle" Value="{StaticResource perTreeViewItemContainerStyle}" />
    </Style>
    

    4 - In the View, use the behavior class to bind the BoundSelectedItem attached proprety to a corresponding property in the ViewModel

    <TreeView ItemsSource="{Binding ...}">
    
        <i:Interaction.Behaviors>
            <vhelp:perTreeViewHelper BoundSelectedItem="{Binding SelectedItem}" />
        </i:Interaction.Behaviors>
    

    For more details on my take on how to handle TreeView controls in a MVVM context (including check boxes for item selection and lazy loading of item data), check out my blog post.