Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF Binding on multiple criterias

In my MVVM, in view I have a DataGrid where I want to enable the row elements based on 2 criteria. DataGrid has checkbox in 1 col (that is bound to IsRowChecked property) and a header checkbox in another col. I want to enable the row element if 1) IsRowChecked is true AND 2) Header checkbox is selected.

Is this multiple criteria setting possible ?

XAML

<DataGrid ItemsSource="{Binding SiList, Mode=TwoWay}" Name="dgvSiSelection">
      <DataGrid.Columns>                  
          <DataGridCheckBoxColumn Binding="{Binding IsRowChecked, Mode=TwoWay}"/>  
          <DataGridTextColumn Header="" Binding="{Binding Header}" MinWidth="108" IsReadOnly="True"/>
          <DataGridTemplateColumn>
              <DataGridTemplateColumn.Header>
                    <Grid>
                        <Grid.ColumnDefinitions>
                              <ColumnDefinition />
                        </Grid.ColumnDefinitions>
                        <TextBlock Text="Number of Chemicals" Grid.Column="0"  />
                    </Grid>                                        
               </DataGridTemplateColumn.Header>
               <DataGridTemplateColumn.CellTemplate>
                     <DataTemplate>
                          <TextBlock Text="{Binding Path=NumberOfCases}" />
                     </DataTemplate>
                     </DataGridTemplateColumn.CellTemplate>
                     <DataGridTemplateColumn.CellEditingTemplate>
                          <DataTemplate>
                              <TextBox IsEnabled="{Binding Path=IsRowChecked}" Text="{Binding Path=NumberOfCases, Mode=TwoWay}" />
                         </DataTemplate>
                     </DataGridTemplateColumn.CellEditingTemplate>
                 </DataGridTemplateColumn>


                 <!-- Value1  chkValue11-->
                 <DataGridTemplateColumn>
                     <DataGridTemplateColumn.Header>
                          <Grid>
                              <Grid.ColumnDefinitions>
                                    <ColumnDefinition/>
                                    <ColumnDefinition/>
                              </Grid.ColumnDefinitions>
                              <TextBlock Grid.Column="1" Text="Value1" IsEnabled="{Binding ElementName=chkValue11, Path=IsChecked}" />
                              <CheckBox Name="chkValue11"  Grid.Column="0" Width="16" />
                          </Grid>
                     </DataGridTemplateColumn.Header>
                     <DataGridTemplateColumn.CellTemplate>
                          <DataTemplate>
                                <TextBlock Text="{Binding Path=Value1}" />
                          </DataTemplate>
                     </DataGridTemplateColumn.CellTemplate>
                     <DataGridTemplateColumn.CellEditingTemplate>
                            <DataTemplate>
                                 <TextBox IsEnabled="{Binding ElementName=chkValue11, Path=IsChecked}" Text="{Binding Path=Value1, Mode=TwoWay}" />
                            </DataTemplate>
                      </DataGridTemplateColumn.CellEditingTemplate>
                  </DataGridTemplateColumn>

                  <!-- Value2  chkValue12-->
                  <DataGridTemplateColumn>
                      <DataGridTemplateColumn.Header>
                          <Grid>
                              <Grid.ColumnDefinitions>
                                   <ColumnDefinition/>
                                   <ColumnDefinition/>
                              </Grid.ColumnDefinitions>
                              <TextBlock Grid.Column="1" Text="Value2" IsEnabled="{Binding ElementName=chkValue12, Path=IsChecked}" />
                              <CheckBox Name="chkValue12"  Grid.Column="0" Width="16" />
                          </Grid>
                    </DataGridTemplateColumn.Header>
                    <DataGridTemplateColumn.CellTemplate>
                         <DataTemplate>
                                <TextBlock Text="{Binding Path=Value2}" />
                         </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                    <DataGridTemplateColumn.CellEditingTemplate>
                          <DataTemplate>
                               <TextBox IsEnabled="{Binding ElementName=chkValue12, Path=IsChecked}" Text="{Binding Path=Value2, Mode=TwoWay}" />
                          </DataTemplate>
                    </DataGridTemplateColumn.CellEditingTemplate>
                </DataGridTemplateColumn>

       </DataGrid.Columns>
   </DataGrid>

"Number of Chemicals" col need only single criteria binding which I could do. Value1 & Value2 needs 2 criteria i.e. of IsRowChecked and header checkbox selected. I could do with single, but can't get a way to implement 2 criteria's in these bindings.

How can I achieve this sort of binding ???

Any help is highly appreciated.

Thanks

like image 283
Tvd Avatar asked Aug 31 '13 11:08

Tvd


1 Answers

There're two ways to work with complex expressions on bindings: MultiBinding with a converter and MultiDataTrigger with specific cases. For your example, both work nicely.

1. MultiBinding

This method works for virtually any case, but requires implementing converters (they can be reused, obviously).

  1. Create AndConverter class implementing IMultiValueConverter interface. It needs to return true if all values passed to it are true (DependencyProperty.UnsetValue is a special value when value is not available).

    public class AndConverter : IMultiValueConverter
    {
        public object Convert (object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            if (values.Any(v => ReferenceEquals(v, DependencyProperty.UnsetValue)))
                return DependencyProperty.UnsetValue;
            return values.All(System.Convert.ToBoolean);
        }
    
        public object[] ConvertBack (object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }
    
  2. Add the converter to resources:

    <Window.Resources>
        <local:AndConverter x:Key="convAnd"/>
    </Window.Resources>
    
  3. Specify DataTemplate for Value1 column (original code is commented):

    <DataTemplate DataType="local:Item">
        <!--<TextBox IsEnabled="{Binding ElementName=chkValue11, Path=IsChecked}" Text="{Binding Path=Value1, Mode=TwoWay}"/>-->
        <TextBox Text="{Binding Path=Value1, Mode=TwoWay}">
            <TextBox.IsEnabled>
                <MultiBinding Converter="{StaticResource convAnd}">
                    <Binding ElementName="chkValue11" Path="IsChecked"/>
                    <Binding Path="IsRowChecked"/>
                </MultiBinding>
            </TextBox.IsEnabled>
        </TextBox>
    </DataTemplate>
    

2. MultiDataTrigger

This method works when only one property value configuration needs specific behavior. In your example, the text box is always disabled, except when both check boxes are checked. The syntax is more complex, but converters are not required.

  1. Specify DataTemplate for Value2 column (original code is commented):

    <DataTemplate DataType="local:Item">
        <!--<TextBox IsEnabled="{Binding ElementName=chkValue12, Path=IsChecked}" Text="{Binding Path=Value2, Mode=TwoWay}"/>-->
        <TextBox x:Name="txt" Text="{Binding Path=Value2, Mode=TwoWay}"
                 IsEnabled="False"/>
        <DataTemplate.Triggers>
            <MultiDataTrigger>
                <MultiDataTrigger.Conditions>
                    <Condition Binding="{Binding IsChecked, ElementName=chkValue12}" Value="True"/>
                    <Condition Binding="{Binding IsRowChecked}" Value="True"/>
                </MultiDataTrigger.Conditions>
                <Setter TargetName="txt" Property="IsEnabled" Value="True"/>
            </MultiDataTrigger>
        </DataTemplate.Triggers>
    </DataTemplate>
    
like image 169
Athari Avatar answered Sep 22 '22 21:09

Athari