none
Hide specific context menu in datagrid upon selected row wpf mvvm RRS feed

  • Question

  • I got one issue on datagrid using WPF Mvvm. I set context menu on datagrid. here is my code.

    <DataGrid.ContextMenu>
                    <ContextMenu IsEnabled="{Binding IsEnableCaseRefNo}" 
                                 DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}"  >                                                       
                        <MenuItem Header=" - View Case" >
                            <i:Interaction.Triggers>
                                <i:EventTrigger EventName="Click">
                                    <i:InvokeCommandAction Command="{Binding ContextCommand}" CommandParameter="VCD"></i:InvokeCommandAction>
                                </i:EventTrigger>
                            </i:Interaction.Triggers>
                        </MenuItem>
                        <MenuItem Header=" - Cheque" Visibility="{Binding SyncColumnVisibility, Converter={StaticResource visibilityConverter}}">
                            <i:Interaction.Triggers>
                                <i:EventTrigger EventName="Click">
                                    <i:InvokeCommandAction Command="{Binding ContextCommand}" CommandParameter="BMK"></i:InvokeCommandAction>
                                </i:EventTrigger>
                            </i:Interaction.Triggers>
                        </MenuItem>
                        <MenuItem Header=" - Cash" Visibility="{Binding SyncColumnVisibility, Converter={StaticResource visibilityConverter}}">
                            <i:Interaction.Triggers>
                                <i:EventTrigger EventName="Click">
                                    <i:InvokeCommandAction Command="{Binding ContextCommand}" CommandParameter="UNBMK"></i:InvokeCommandAction>
                                </i:EventTrigger>
                            </i:Interaction.Triggers>
                        </MenuItem>
                   </ContextMenu>
                </DataGrid.ContextMenu>

    I can show and hide context menu using visibility property. but now i want to enable/disable "-Cheque" context menu upon user selection. how can I disable 'Cheque' context menu when there are 100 over dollars in donate columns(that already shown in datagrid).

    Here is my datagrid :

    <DataGrid Name="dgv" Background="WhiteSmoke" AutoGenerateColumns="False" CanUserAddRows="False" SelectedItem="{Binding SelectedItems,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
                          CanUserDeleteRows="False" Grid.Row="2" ItemsSource="{Binding LstcaseHearingModel,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"                              
                          Grid.Column="2" HorizontalAlignment="Stretch" >

    Thanks for any help.

    Tuesday, December 1, 2015 1:35 AM

Answers

  • Hi Andy,

    SyncColumnVisibility is static resource. It is not inside the LstcaseHearingModel that already bind to datagrid. so it can call it. I cannot call any property from LstcaseHearingModel from context menu.

    Yes you can.

    I'll do this both ways, in a simplified version for clarity.

    Say you mean the dollar total is for multiple records so if you have a total over 100 then disable cheque.

    I don't know if you're setting the datacontext of the datagrid or what, so I use the Mainwindow in this. It could be different in your code. This is showing principles only.

    <Window x:Class="wpf_Datagrid_Donations.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:wpf_Datagrid_Donations"
            mc:Ignorable="d"
            Title="MainWindow" Height="350" Width="525"
            DataContext="{DynamicResource VM}"
         >
        <Window.Resources>
            <local:MainWindowViewModel x:Key="VM"/>
            <ContextMenu  
                x:Key="DgContextMenu"
                DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}"  >
                <MenuItem Header=" - View Case" >
                </MenuItem>
                <MenuItem Header=" - Cheque" 
                                  IsEnabled="{Binding Path=IsChequeEnabledFromVM, Source={StaticResource VM}}"
                                  >
                </MenuItem>
            </ContextMenu>
        </Window.Resources>
        <Grid>
            <DataGrid ItemsSource="{Binding Donations}" ContextMenu="{StaticResource DgContextMenu}"/>
        </Grid>
    </Window>

    The IsEnabled is going to bind to a property in the window's viewmodel.

    Here is the viewmodel

        public class MainWindowViewModel :ViewModelBase
        {
            public bool IsChequeEnabledFromVM
            {
                get { return ( donations.Sum(x=>x.Amount) > 100)   ? false : true   ; }
            }
            private ObservableCollection<Donation> donations;
            public ObservableCollection<Donation> Donations
            {
                get { return donations; }
                set
                {
                    donations = value;
                    RaisePropertyChanged();
                }
            }
            public MainWindowViewModel()
            {
                Donations = new ObservableCollection<Donation>
                {
                    new Donation { Amount = 20 },
                    new Donation { Amount = 80 },
                    new Donation { Amount = 1 },
                };
                RaisePropertyChanged("IsChequeEnabledFromVM");
            }
        }

    The  total is over 100 so cheque is disabled on my context menu.

    If you can change the values on the amounts then you would also need a mechanism for this vm to raise propertychanged like it's doing in the constructor.

    Say you only want to do it on the row instead.

    Here's the Donation viewmodel. This could wrap whatever entity you have.

        public class Donation : ViewModelBase
        {
            private decimal amount;
    
            public decimal Amount
            {
                get { return amount; }
                set
                {
                    amount = value;
                    RaisePropertyChanged();
                }
            }
    
            public bool  IsChequeEnabledFromRow 
                {
                   get{ return (Amount > 100 ) ? false : true; }
                }
        }

    If this is instead about the individual selected item then:

    <Window x:Class="wpf_Datagrid_Donations.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:wpf_Datagrid_Donations"
            mc:Ignorable="d"
            Title="MainWindow" Height="350" Width="525"
            DataContext="{DynamicResource VM}"
         >
        <Window.Resources>
            <local:MainWindowViewModel x:Key="VM"/>
            <ContextMenu 
                
                x:Key="DgContextMenu"
                DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}"  >
                <MenuItem Header=" - View Case" >
                </MenuItem>
                <MenuItem Header=" - Cheque" 
                          DataContext="{Binding SelectedItem}"
                          IsEnabled="{Binding IsChequeEnabledFromRow}"
                                  >
                </MenuItem>
            </ContextMenu>
        </Window.Resources>
        <Grid>
            <DataGrid 
                SelectedItem="{Binding SelectedItem}"
                Name="dg"
                ItemsSource="{Binding Donations}" ContextMenu="{StaticResource DgContextMenu}"/>
        </Grid>
    </Window>

    and

        public class MainWindowViewModel :ViewModelBase
        {
            public bool IsChequeEnabledFromVM
            {
                get { return ( donations.Sum(x=>x.Amount) > 100)   ? false : true   ; }
            }
            private ObservableCollection<Donation> donations;
            public ObservableCollection<Donation> Donations
            {
                get { return donations; }
                set
                {
                    donations = value;
                    RaisePropertyChanged();
                }
            }
    
            public Donation SelectedItem { get; set; }
            public MainWindowViewModel()
            {
                Donations = new ObservableCollection<Donation>
                {
                    new Donation { Amount = 101 },
                    new Donation { Amount = 80 },
                    new Donation { Amount = 1 },
                };
                RaisePropertyChanged("IsChequeEnabledFromVM");
            }
        }


    Hope that helps.

    Technet articles: WPF: Layout Lab; All my Technet Articles

    Wednesday, December 2, 2015 2:37 PM
    Moderator
  • Set the ContextMenu property of the DataGridRow rather than setting it on the DataGrid itself then. The DataContext of a DataGridRow is the data object. You could then store the DataContext of the DataGrid in the Tag property of the DataGridRow:

            <DataGrid Name="dgv" Background="WhiteSmoke" AutoGenerateColumns="False" CanUserAddRows="False" SelectedItem="{Binding SelectedItems,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
                          CanUserDeleteRows="False" Grid.Row="2" ItemsSource="{Binding LstcaseHearingModel,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"                              
                          Grid.Column="2" HorizontalAlignment="Stretch" >
                <DataGrid.RowStyle>
                    <Style TargetType="DataGridRow">
                        <Setter Property="Tag" Value="{Binding Path=DataContext, ElementName=dgv}"/>
                        <Setter Property="ContextMenu">
                            <Setter.Value>
                                <ContextMenu IsEnabled="{Binding Tag.IsEnableCaseRefNo}" 
                                 DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}"  >
                                    <MenuItem Header=" - Cheque" Visibility="{Binding Tag.SyncColumnVisibility, Converter={StaticResource visibilityConverter}}"
                                              IsEnabled="{Binding DataContext.Donate, Converter={StaticResource conv}}">
                                        <i:Interaction.Triggers>
                                            <i:EventTrigger EventName="Click">
                                                <i:InvokeCommandAction Command="{Binding Tag.ContextCommand}" CommandParameter="BMK"></i:InvokeCommandAction>
                                            </i:EventTrigger>
                                        </i:Interaction.Triggers>
                                    </MenuItem>
                                </ContextMenu>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </DataGrid.RowStyle>
    ...
            </DataGrid>

    Hope that helps.

    Please remember to close your threads by marking helpful posts as answer and then start a new thread if you have a new question. Please don't ask several questions in the same thread.

    Wednesday, December 2, 2015 2:54 PM

All replies

  • You already have a binding there:

                <MenuItem Header=" - Cheque" Visibility="{Binding SyncColumnVisibility, Converter={StaticResource visibilityConverter}}">
                            <i:Interaction.Triggers>
                                <i:EventTrigger EventName="Click">
                                    <i:InvokeCommandAction Command="{Binding ContextCommand}" CommandParameter="BMK"></i:InvokeCommandAction>
                                </i:EventTrigger>
                            </i:Interaction.Triggers>
                        </MenuItem>
        

    Change whatever your viewmodel does to set it's SyncColumnVisibility so it takes into account cheque value.

    Seeing as how you bind the value, I don't really see why you have a converter there.

    Why isn't whatever logic  your converter does in the viewmodel?

    You could make SyncColumnVisibility a Visibility property if it's bool.

    Make another column a bool if you need a bool elsewhere.


    Hope that helps.

    Technet articles: WPF: MVVM Step 1; All my Technet Articles

    Tuesday, December 1, 2015 8:40 AM
    Moderator
  • >>how can I disable 'Cheque' context menu when there are 100 over dollars in donate columns(that already shown in datagrid).

    You could bind the IsEnabled property of the MenuItem to the Donate property and use a converter class that returns false when the value of the Donate property is > 100:

                            <MenuItem Header=" - Cheque" Visibility="{Binding SyncColumnVisibility, Converter={StaticResource visibilityConverter}}"
                                      IsEnabled="{Binding Donate, Converter={StaticResource conv}}">
                                <i:Interaction.Triggers>
                                    <i:EventTrigger EventName="Click">
                                        <i:InvokeCommandAction Command="{Binding ContextCommand}" CommandParameter="BMK"></i:InvokeCommandAction>
                                    </i:EventTrigger>
                                </i:Interaction.Triggers>
                            </MenuItem>
    
    
    <Window.Resources>
    <local:Conv x:Key="conv" />
    </Window.Resources>
    

    class Conv : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                int donate = Convert.ToInt32(value);
         return donate <= 100;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                return value;
     }
    }
    

    There is no good way of setting the IsEnabled property to false when a source property is > 100 in pure XAML though.

    Hope that helps.

    Please remember to close your threads by marking helpful posts as answer and then start a new thread if you have a new question. Please don't ask several questions in the same thread.

    Tuesday, December 1, 2015 5:49 PM
  • Hi Magnus,

    my problem is

     context menu cannot call any property in LstcaseHearingModel that already bind to datagrid but I can call it from datagrid row.


    • Edited by coemaw Wednesday, December 2, 2015 1:37 AM
    Wednesday, December 2, 2015 1:34 AM
  • Hi Andy,

    SyncColumnVisibility is static resource. It is not inside the LstcaseHearingModel that already bind to datagrid. so it can call it. I cannot call any property from LstcaseHearingModel from context menu.

    Wednesday, December 2, 2015 1:37 AM
  • Hi Andy,

    SyncColumnVisibility is static resource. It is not inside the LstcaseHearingModel that already bind to datagrid. so it can call it. I cannot call any property from LstcaseHearingModel from context menu.

    Yes you can.

    I'll do this both ways, in a simplified version for clarity.

    Say you mean the dollar total is for multiple records so if you have a total over 100 then disable cheque.

    I don't know if you're setting the datacontext of the datagrid or what, so I use the Mainwindow in this. It could be different in your code. This is showing principles only.

    <Window x:Class="wpf_Datagrid_Donations.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:wpf_Datagrid_Donations"
            mc:Ignorable="d"
            Title="MainWindow" Height="350" Width="525"
            DataContext="{DynamicResource VM}"
         >
        <Window.Resources>
            <local:MainWindowViewModel x:Key="VM"/>
            <ContextMenu  
                x:Key="DgContextMenu"
                DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}"  >
                <MenuItem Header=" - View Case" >
                </MenuItem>
                <MenuItem Header=" - Cheque" 
                                  IsEnabled="{Binding Path=IsChequeEnabledFromVM, Source={StaticResource VM}}"
                                  >
                </MenuItem>
            </ContextMenu>
        </Window.Resources>
        <Grid>
            <DataGrid ItemsSource="{Binding Donations}" ContextMenu="{StaticResource DgContextMenu}"/>
        </Grid>
    </Window>

    The IsEnabled is going to bind to a property in the window's viewmodel.

    Here is the viewmodel

        public class MainWindowViewModel :ViewModelBase
        {
            public bool IsChequeEnabledFromVM
            {
                get { return ( donations.Sum(x=>x.Amount) > 100)   ? false : true   ; }
            }
            private ObservableCollection<Donation> donations;
            public ObservableCollection<Donation> Donations
            {
                get { return donations; }
                set
                {
                    donations = value;
                    RaisePropertyChanged();
                }
            }
            public MainWindowViewModel()
            {
                Donations = new ObservableCollection<Donation>
                {
                    new Donation { Amount = 20 },
                    new Donation { Amount = 80 },
                    new Donation { Amount = 1 },
                };
                RaisePropertyChanged("IsChequeEnabledFromVM");
            }
        }

    The  total is over 100 so cheque is disabled on my context menu.

    If you can change the values on the amounts then you would also need a mechanism for this vm to raise propertychanged like it's doing in the constructor.

    Say you only want to do it on the row instead.

    Here's the Donation viewmodel. This could wrap whatever entity you have.

        public class Donation : ViewModelBase
        {
            private decimal amount;
    
            public decimal Amount
            {
                get { return amount; }
                set
                {
                    amount = value;
                    RaisePropertyChanged();
                }
            }
    
            public bool  IsChequeEnabledFromRow 
                {
                   get{ return (Amount > 100 ) ? false : true; }
                }
        }

    If this is instead about the individual selected item then:

    <Window x:Class="wpf_Datagrid_Donations.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:wpf_Datagrid_Donations"
            mc:Ignorable="d"
            Title="MainWindow" Height="350" Width="525"
            DataContext="{DynamicResource VM}"
         >
        <Window.Resources>
            <local:MainWindowViewModel x:Key="VM"/>
            <ContextMenu 
                
                x:Key="DgContextMenu"
                DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}"  >
                <MenuItem Header=" - View Case" >
                </MenuItem>
                <MenuItem Header=" - Cheque" 
                          DataContext="{Binding SelectedItem}"
                          IsEnabled="{Binding IsChequeEnabledFromRow}"
                                  >
                </MenuItem>
            </ContextMenu>
        </Window.Resources>
        <Grid>
            <DataGrid 
                SelectedItem="{Binding SelectedItem}"
                Name="dg"
                ItemsSource="{Binding Donations}" ContextMenu="{StaticResource DgContextMenu}"/>
        </Grid>
    </Window>

    and

        public class MainWindowViewModel :ViewModelBase
        {
            public bool IsChequeEnabledFromVM
            {
                get { return ( donations.Sum(x=>x.Amount) > 100)   ? false : true   ; }
            }
            private ObservableCollection<Donation> donations;
            public ObservableCollection<Donation> Donations
            {
                get { return donations; }
                set
                {
                    donations = value;
                    RaisePropertyChanged();
                }
            }
    
            public Donation SelectedItem { get; set; }
            public MainWindowViewModel()
            {
                Donations = new ObservableCollection<Donation>
                {
                    new Donation { Amount = 101 },
                    new Donation { Amount = 80 },
                    new Donation { Amount = 1 },
                };
                RaisePropertyChanged("IsChequeEnabledFromVM");
            }
        }


    Hope that helps.

    Technet articles: WPF: Layout Lab; All my Technet Articles

    Wednesday, December 2, 2015 2:37 PM
    Moderator
  • Set the ContextMenu property of the DataGridRow rather than setting it on the DataGrid itself then. The DataContext of a DataGridRow is the data object. You could then store the DataContext of the DataGrid in the Tag property of the DataGridRow:

            <DataGrid Name="dgv" Background="WhiteSmoke" AutoGenerateColumns="False" CanUserAddRows="False" SelectedItem="{Binding SelectedItems,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
                          CanUserDeleteRows="False" Grid.Row="2" ItemsSource="{Binding LstcaseHearingModel,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"                              
                          Grid.Column="2" HorizontalAlignment="Stretch" >
                <DataGrid.RowStyle>
                    <Style TargetType="DataGridRow">
                        <Setter Property="Tag" Value="{Binding Path=DataContext, ElementName=dgv}"/>
                        <Setter Property="ContextMenu">
                            <Setter.Value>
                                <ContextMenu IsEnabled="{Binding Tag.IsEnableCaseRefNo}" 
                                 DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}"  >
                                    <MenuItem Header=" - Cheque" Visibility="{Binding Tag.SyncColumnVisibility, Converter={StaticResource visibilityConverter}}"
                                              IsEnabled="{Binding DataContext.Donate, Converter={StaticResource conv}}">
                                        <i:Interaction.Triggers>
                                            <i:EventTrigger EventName="Click">
                                                <i:InvokeCommandAction Command="{Binding Tag.ContextCommand}" CommandParameter="BMK"></i:InvokeCommandAction>
                                            </i:EventTrigger>
                                        </i:Interaction.Triggers>
                                    </MenuItem>
                                </ContextMenu>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </DataGrid.RowStyle>
    ...
            </DataGrid>

    Hope that helps.

    Please remember to close your threads by marking helpful posts as answer and then start a new thread if you have a new question. Please don't ask several questions in the same thread.

    Wednesday, December 2, 2015 2:54 PM