locked
TreeView ContextMenu Command Binding RRS feed

  • Question

  • I have a UserControl with a TreeView on it which now contains a ContextMenu resource.
    The TreeView HierachicalDataTemplates have different Models assigned to each ItemsSouce and a different StaticResource ContextMenu associated with each one.

    I have spent the last 3 days finding out how ContextMenus work and how to either use the default ItemsSource Model for the command or now using static resource, placement targets and tags the command can call a command property in the UserControls ViewModel.

    My question is Which way is the correct way ?

    Should I have commands in my Models or just in the ViewModel of the control ?

    The Models have properties to allow me to bind directly to the data otherwise I am duplicating all my Model Properties in the ViewModel to be able to bind to them. Does this make them view models ?

    Thank you

    Wayne

    Wednesday, October 11, 2017 10:58 AM

Answers

  • Hi,

    >> Should I have commands in my Models or just in the ViewModel of the control ?

    You should have commands in ViewModel. ViewModel should  expose methods, commands, and other points that help maintain the state of the view, manipulate the model as the result of actions on the view, and trigger events in the view itself.

    Below test Demo, please check.

    <UserControl x:Class="wpfAppDemo.wpfTreeView.MyTreeView"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                 xmlns:local="clr-namespace:wpfAppDemo.wpfTreeView"
                 mc:Ignorable="d" 
                 d:DesignHeight="300" d:DesignWidth="300">
        <StackPanel Background="AliceBlue" Orientation="Vertical">
            <TextBlock Height="30" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="20">This is my Test Demo</TextBlock>
            <TreeView Margin="10" ItemsSource="{Binding FamilyLists}">
                <TreeView.Resources>
                    <ContextMenu x:Key="contextMenu"  StaysOpen="true">
                        <MenuItem Header="Add" Command="{Binding DataContext.AddCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
                        <MenuItem Header="Rename" Command="{Binding RenameCommand,RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
                        <MenuItem Header="Remove" Command="{Binding DataContext.RemoveCommand,RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
                        <Separator/>
                        <MenuItem Header="Copy" Command="{Binding CopyCommand,RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
                        <MenuItem Header="Cut" Command="{Binding CutCommand,RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
                        <MenuItem Header="Paste" Command="{Binding PasteCommand,RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
                        <MenuItem Header="Move" Command="{Binding MoveCommand,RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
                    </ContextMenu>
                    <Style x:Key="FamilyTemplateItemContainerStyle" TargetType="{x:Type TreeViewItem}">
                        <Setter Property="ContextMenu" Value="{DynamicResource contextMenu}"/>
                    </Style>
                </TreeView.Resources>
                <TreeView.ItemTemplate>
                    <HierarchicalDataTemplate ItemContainerStyle="{StaticResource FamilyTemplateItemContainerStyle}" ItemsSource="{Binding Members}">
                        <StackPanel Orientation="Horizontal">
                            <Image Source="/WpfAppDemo;component/wpfTreeView/Images/16_16/emoticon_smile.png" Margin="0,0,5,0" />
                            <TextBlock Text="{Binding FamilyName}" />
                            <TextBlock Text=" [" Foreground="Blue" />
                            <TextBlock Text="{Binding Members.Count}" Foreground="Blue" />
                            <TextBlock Text="]" Foreground="Blue" />
                        </StackPanel>
                        <HierarchicalDataTemplate.ItemTemplate>
                            <DataTemplate>
                                <StackPanel Orientation="Horizontal">
                                    <Image Source="/WpfAppDemo;component/wpfTreeView/Images/16_16/emoticon_smile.png" Margin="0,0,5,0" />
                                    <TextBlock Text="{Binding Name}" />
                                    <TextBlock Text=" (" Foreground="Green" />
                                    <TextBlock Text="{Binding Age}" Foreground="Green" />
                                    <TextBlock Text=" years)" Foreground="Green" />
                                </StackPanel>
                            </DataTemplate>
                        </HierarchicalDataTemplate.ItemTemplate>
                    </HierarchicalDataTemplate>
    
                </TreeView.ItemTemplate>
            </TreeView>
        </StackPanel>
    </UserControl>

     public class FamilyViewModel
        {
            public ObservableCollection<Family> FamilyLists { get; set; }
    
            public FamilyViewModel()
            {
                FamilyLists = new ObservableCollection<Family>() { };
                Family family1 = new Family() { FamilyName = "The Doe's" };
                family1.Members.Add(new FamilyMember() { Name = "John Doe", Age = 42 });
                family1.Members.Add(new FamilyMember() { Name = "Jane Doe", Age = 39 });
                family1.Members.Add(new FamilyMember() { Name = "Sammy Doe", Age = 13 });
                FamilyLists.Add(family1);
    
                Family family2 = new Family() { FamilyName = "The Moe's" };
                family2.Members.Add(new FamilyMember() { Name = "Mark Moe", Age = 31 });
                family2.Members.Add(new FamilyMember() { Name = "Norma Moe", Age = 28 });
                FamilyLists.Add(family2);
    
                Family family3 = new Family() { FamilyName = "The Bob's" };
                family3.Members.Add(new FamilyMember() { Name = "Mark Bob", Age = 45 });
                family3.Members.Add(new FamilyMember() { Name = "Norma Bob", Age = 21 });
                FamilyLists.Add(family3);
            }
    
        
            private ICommand _addCommand;
            public ICommand AddCommand
            {
                get
                {
                    if (_addCommand == null)
                    {
                        _addCommand = new RelayCommand(
                           Add,
                        p => true);
                    }
                    return _addCommand;
                }
            }
            private void Add(object obj)
            {
                MessageBox.Show("Add");
            }
            private ICommand _removeCommand;
            public ICommand RemoveCommand
            {
                get
                {
                    if (_removeCommand == null)
                    {
                        _removeCommand = new RelayCommand(
                           Remove,
                        p => true);
                    }
                    return _removeCommand;
                }
            }
            private void Remove(object obj)
            {
                MessageBox.Show("Remove");
            }
        }
    
        public class Family
        {
            public Family()
            {
                this.Members = new ObservableCollection<FamilyMember>();
            }
    
            public string FamilyName { get; set; }
    
            public ObservableCollection<FamilyMember> Members { get; set; }
        }
    
        public class FamilyMember
        {
            public string Name { get; set; }
    
            public int Age { get; set; }
        }
     public partial class Hierarchicalsample1 : Window
        {
            public Hierarchicalsample1()
            {
                InitializeComponent();
                FamilyViewModel vm = new FamilyViewModel();
               this.DataContext= vm;
            }
        }
     <Grid>
            <local:MyTreeView></local:MyTreeView>
        </Grid>


    Best Regards,

    Bob


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    • Proposed as answer by Bob Ding Thursday, October 19, 2017 7:51 AM
    • Marked as answer by Wayne Richmond Thursday, October 19, 2017 2:21 PM
    Thursday, October 12, 2017 7:51 AM

All replies

  • Hi,

    >> Should I have commands in my Models or just in the ViewModel of the control ?

    You should have commands in ViewModel. ViewModel should  expose methods, commands, and other points that help maintain the state of the view, manipulate the model as the result of actions on the view, and trigger events in the view itself.

    Below test Demo, please check.

    <UserControl x:Class="wpfAppDemo.wpfTreeView.MyTreeView"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                 xmlns:local="clr-namespace:wpfAppDemo.wpfTreeView"
                 mc:Ignorable="d" 
                 d:DesignHeight="300" d:DesignWidth="300">
        <StackPanel Background="AliceBlue" Orientation="Vertical">
            <TextBlock Height="30" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="20">This is my Test Demo</TextBlock>
            <TreeView Margin="10" ItemsSource="{Binding FamilyLists}">
                <TreeView.Resources>
                    <ContextMenu x:Key="contextMenu"  StaysOpen="true">
                        <MenuItem Header="Add" Command="{Binding DataContext.AddCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
                        <MenuItem Header="Rename" Command="{Binding RenameCommand,RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
                        <MenuItem Header="Remove" Command="{Binding DataContext.RemoveCommand,RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
                        <Separator/>
                        <MenuItem Header="Copy" Command="{Binding CopyCommand,RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
                        <MenuItem Header="Cut" Command="{Binding CutCommand,RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
                        <MenuItem Header="Paste" Command="{Binding PasteCommand,RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
                        <MenuItem Header="Move" Command="{Binding MoveCommand,RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
                    </ContextMenu>
                    <Style x:Key="FamilyTemplateItemContainerStyle" TargetType="{x:Type TreeViewItem}">
                        <Setter Property="ContextMenu" Value="{DynamicResource contextMenu}"/>
                    </Style>
                </TreeView.Resources>
                <TreeView.ItemTemplate>
                    <HierarchicalDataTemplate ItemContainerStyle="{StaticResource FamilyTemplateItemContainerStyle}" ItemsSource="{Binding Members}">
                        <StackPanel Orientation="Horizontal">
                            <Image Source="/WpfAppDemo;component/wpfTreeView/Images/16_16/emoticon_smile.png" Margin="0,0,5,0" />
                            <TextBlock Text="{Binding FamilyName}" />
                            <TextBlock Text=" [" Foreground="Blue" />
                            <TextBlock Text="{Binding Members.Count}" Foreground="Blue" />
                            <TextBlock Text="]" Foreground="Blue" />
                        </StackPanel>
                        <HierarchicalDataTemplate.ItemTemplate>
                            <DataTemplate>
                                <StackPanel Orientation="Horizontal">
                                    <Image Source="/WpfAppDemo;component/wpfTreeView/Images/16_16/emoticon_smile.png" Margin="0,0,5,0" />
                                    <TextBlock Text="{Binding Name}" />
                                    <TextBlock Text=" (" Foreground="Green" />
                                    <TextBlock Text="{Binding Age}" Foreground="Green" />
                                    <TextBlock Text=" years)" Foreground="Green" />
                                </StackPanel>
                            </DataTemplate>
                        </HierarchicalDataTemplate.ItemTemplate>
                    </HierarchicalDataTemplate>
    
                </TreeView.ItemTemplate>
            </TreeView>
        </StackPanel>
    </UserControl>

     public class FamilyViewModel
        {
            public ObservableCollection<Family> FamilyLists { get; set; }
    
            public FamilyViewModel()
            {
                FamilyLists = new ObservableCollection<Family>() { };
                Family family1 = new Family() { FamilyName = "The Doe's" };
                family1.Members.Add(new FamilyMember() { Name = "John Doe", Age = 42 });
                family1.Members.Add(new FamilyMember() { Name = "Jane Doe", Age = 39 });
                family1.Members.Add(new FamilyMember() { Name = "Sammy Doe", Age = 13 });
                FamilyLists.Add(family1);
    
                Family family2 = new Family() { FamilyName = "The Moe's" };
                family2.Members.Add(new FamilyMember() { Name = "Mark Moe", Age = 31 });
                family2.Members.Add(new FamilyMember() { Name = "Norma Moe", Age = 28 });
                FamilyLists.Add(family2);
    
                Family family3 = new Family() { FamilyName = "The Bob's" };
                family3.Members.Add(new FamilyMember() { Name = "Mark Bob", Age = 45 });
                family3.Members.Add(new FamilyMember() { Name = "Norma Bob", Age = 21 });
                FamilyLists.Add(family3);
            }
    
        
            private ICommand _addCommand;
            public ICommand AddCommand
            {
                get
                {
                    if (_addCommand == null)
                    {
                        _addCommand = new RelayCommand(
                           Add,
                        p => true);
                    }
                    return _addCommand;
                }
            }
            private void Add(object obj)
            {
                MessageBox.Show("Add");
            }
            private ICommand _removeCommand;
            public ICommand RemoveCommand
            {
                get
                {
                    if (_removeCommand == null)
                    {
                        _removeCommand = new RelayCommand(
                           Remove,
                        p => true);
                    }
                    return _removeCommand;
                }
            }
            private void Remove(object obj)
            {
                MessageBox.Show("Remove");
            }
        }
    
        public class Family
        {
            public Family()
            {
                this.Members = new ObservableCollection<FamilyMember>();
            }
    
            public string FamilyName { get; set; }
    
            public ObservableCollection<FamilyMember> Members { get; set; }
        }
    
        public class FamilyMember
        {
            public string Name { get; set; }
    
            public int Age { get; set; }
        }
     public partial class Hierarchicalsample1 : Window
        {
            public Hierarchicalsample1()
            {
                InitializeComponent();
                FamilyViewModel vm = new FamilyViewModel();
               this.DataContext= vm;
            }
        }
     <Grid>
            <local:MyTreeView></local:MyTreeView>
        </Grid>


    Best Regards,

    Bob


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    • Proposed as answer by Bob Ding Thursday, October 19, 2017 7:51 AM
    • Marked as answer by Wayne Richmond Thursday, October 19, 2017 2:21 PM
    Thursday, October 12, 2017 7:51 AM
  • Thank you this is very helpful.

    I have already done it though slightly different, I used a tag value to get the datacontext of the viewmodel. Your way may be better I will take a look to see if they do exactly the same thing.

    Thank you

    Thursday, October 19, 2017 2:25 PM