【问题标题】:Disable a cell in WPF datagrid when combo value is set to a value - MVVM当组合值设置为一个值时禁用 WPF 数据网格中的单元格 - MVVM
【发布时间】:2016-09-09 03:22:03
【问题描述】:

我有一个带有 DataGridComboBoxColumn 列的 DataGrid,我需要启用或禁用另一列中的复选框单元格,但我的代码似乎不起作用。

我有以下代码定义我的组合列:

<DataGridComboBoxColumn Header="Location"
 SelectedValueBinding="{Binding LocationId, Mode=TwoWay}"
 SelectedValuePath="LocationId"
 DisplayMemberPath="LocationText"
 TextBinding="{Binding Location, Mode=TwoWay}">

这是我的复选框列的代码:

<DataGridCheckBoxColumn Header="Active" 
 Binding="{Binding IsActive}">
   <DataGridCheckBoxColumn.CellStyle>
     <Style TargetType="DataGridCell">
       <Style.Triggers>
         <DataTrigger Binding="{Binding SelectedLineItem.Location}" 
          Value="01">
            <Setter Property="IsEnabled" Value="False" />
        </DataTrigger>                                        
       </Style.Triggers>
     </Style>
  </DataGridCheckBoxColumn.CellStyle>
</DataGridCheckBoxColumn>

我添加了一个 DataTrigger 来尝试检测 Location 字段何时更改为 01,并基于此尝试禁用所选行中的复选框单元格,但它不起作用。

我还需要根据其他值选中/取消选中组合。

知道如何实现这一目标吗?

更新 1:

好的,我已经取得了一些进展(靠侥幸!)现在我的触发器正在启动,但由于某种原因,每当我更改所选单元格下拉列表中的位置时,它都会更改所有行的值到相同的值。

这是我的下拉列的完整代码:

<DataGridComboBoxColumn Header="Location"
    SelectedItemBinding="{Binding DataContext.SelectedLineItem.SelectedLocation, 
     RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, 
    Mode=TwoWay}"
    SelectedValuePath="LocationId"
                        DisplayMemberPath="LocationText"
                        TextBinding="{Binding Location, Mode=TwoWay}">
    <DataGridComboBoxColumn.ElementStyle>
        <Style TargetType="{x:Type ComboBox}">
            <Setter Property="ItemsSource" Value="{Binding DataContext.Locations, 
          RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
            <Setter Property="IsReadOnly" Value="True"/>
            <Setter Property="FontSize" Value="14"/>
        </Style>
    </DataGridComboBoxColumn.ElementStyle>
    <DataGridComboBoxColumn.EditingElementStyle>
        <Style TargetType="{x:Type ComboBox}">
            <Setter Property="ItemsSource" Value="{Binding DataContext.Locations, 
             RelativeSource={RelativeSource FindAncestor, 
             AncestorType={x:Type Window}}}"/>
            <Setter Property="SelectedItem" Value="{Binding DataContext.SelectedLocation, 
             RelativeSource={RelativeSource FindAncestor, 
             AncestorType={x:Type Window}}}"/>
            <Setter Property="IsReadOnly" Value="False"/>
            <Setter Property="IsEditable" Value="True"/>
            <Setter Property="FontSize" Value="14"/>
            <EventSetter Event="KeyUp" Handler="LocationColumn_KeyUp"></EventSetter>
            <Style.Triggers>
                <Trigger Property="IsKeyboardFocusWithin" Value="True">
                    <Setter Property="IsDropDownOpen" Value="true" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>

这是我的复选框列的完整代码(不要担心触发器的作用 - 它们是测试触发器):

<DataGridCheckBoxColumn Header="Active" Binding="{Binding IsActive}">
    <DataGridCheckBoxColumn.CellStyle>
        <Style TargetType="DataGridCell">
            <Style.Triggers>
                <DataTrigger Binding="{Binding SelectedLocation.LocationId}" Value="1">
                    <Setter Property="Background" Value="Green" />
                </DataTrigger>
                <DataTrigger Binding="{Binding SelectedLocation.LocationText}" Value="" >
                    <Setter Property="IsEnabled" Value="False" />
                </DataTrigger>
                <DataTrigger Binding="{Binding SelectedLocation.LocationText}" Value="01" >
                    <Setter Property="IsEnabled" Value="True" />
                </DataTrigger>
                <DataTrigger Binding="{Binding SelectedLocation.LocationText}" Value="02" >
                    <Setter Property="IsEnabled" Value="False" />
                </DataTrigger>
                <DataTrigger Binding="{Binding SelectedLocation.LocationText}" Value="03" >
                    <Setter Property="IsEnabled" Value="False" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </DataGridCheckBoxColumn.CellStyle>
</DataGridCheckBoxColumn>

谁能弄清楚为什么当我更改组合中的值时我的所有行现在都被设置了?

谢谢。

【问题讨论】:

  • 该绑定的数据上下文可能不是您认为的那样;它可以与RelativeSource AncestorType=DataGrid 一起使用吗?
  • @EdPlunkett 我已经更新了我的答案。走得更远,但仍然不是 100%。有什么想法吗?

标签: c# wpf mvvm datagrid


【解决方案1】:

感谢@Freeman 指出正确的方向。

我最终弄明白了,我设法让它完全工作的唯一方法是在 XAML 中设置属性并在我的 ViewModel 中应用一些基本逻辑。

这是完整的解释:

我有一个 DataGridComboBoxColumn 绑定到我的 SelectionLocation 属性并属于我的行 ViewModel

get { return this._selectedLocation; }
set { Set(() => SelectedLocation, ref this._selectedLocation, value); }

现在关于 XAML 中的绑定:

<DataGridComboBoxColumn Header="Location"
    SelectedItemBinding="{Binding SelectedLocation, Mode=TwoWay}"
    SelectedValuePath="LocationId"
    DisplayMemberPath="LocationText">

这是我的第一个问题,因为每当我从组合中选择一个项目时,它都不会调用/触发我的 SelectedLocation ViewModel 的 setter 部分。已通过添加UpdateSourceTrigger=PropertyChanged 解决此问题:

<DataGridComboBoxColumn Header="Warehouse"
    SelectedItemBinding="{Binding SelectedLocation, Mode=TwoWay,
                          UpdateSourceTrigger=PropertyChanged}"
    SelectedValuePath="LocationId"
    DisplayMemberPath="LocationText">

关于复选框列,XAML 定义为:

<DataGridCheckBoxColumn Header="Active" Binding="{Binding IsActive, Mode=TwoWay>
    <DataGridCheckBoxColumn.ElementStyle>
        <Style TargetType="CheckBox">
            <Setter Property="HorizontalAlignment" Value="Center" />
            <Setter Property="VerticalAlignment" Value="Center" />
    </DataGridCheckBoxColumn.ElementStyle>
</DataGridCheckBoxColumn>

再次,在检查和取消选中我的复选框时,它没有触发我是ISACTION属性的SETTER,该属性属于与“SelectedLocation”属性相同的ViewModel。

为了解决这个问题,我最终添加了UpdateSourceTrigger=PropertyChanged

<DataGridCheckBoxColumn Header="Active" Binding="{Binding IsActive, Mode=TwoWay, 
     UpdateSourceTrigger=PropertyChanged}">
    <DataGridCheckBoxColumn.ElementStyle>
        <Style TargetType="CheckBox">
            <Setter Property="HorizontalAlignment" Value="Center" />
            <Setter Property="VerticalAlignment" Value="Center" />
    </DataGridCheckBoxColumn.ElementStyle>
</DataGridCheckBoxColumn>

现在 Combo 和复选框列的绑定问题已排序,还剩下一件事。有条件的选择。这是通过使用 XAML 中的 DataTrigger 和我的 ViewModel 中的代码的组合来排序的。

XAML 负责启用/禁用复选框,就像从 SelectedLocation 中进行选择一样:

<DataGridCheckBoxColumn Header="Active" Binding="{Binding IsActive, Mode=TwoWay, 
     UpdateSourceTrigger=PropertyChanged}">
    <DataGridCheckBoxColumn.ElementStyle>
        <Style TargetType="CheckBox">
            <Setter Property="HorizontalAlignment" Value="Center" />
            <Setter Property="VerticalAlignment" Value="Center" />
            <Style.Triggers>
                <DataTrigger Binding="{Binding SelectedLocation.LocationText}" Value="01" >
                    <Setter Property="IsEnabled" Value="True" />
                </DataTrigger>
                <DataTrigger Binding="{Binding SelectedLocation.LocationText}" Value="02" >
                    <Setter Property="IsEnabled" Value="False" />
                </DataTrigger>
                <DataTrigger Binding="{Binding SelectedLocation.LocationText}" Value="03" >
                    <Setter Property="IsEnabled" Value="False" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </DataGridCheckBoxColumn.ElementStyle>
</DataGridCheckBoxColumn>

在我的 ViewModel 中,每当触发 SelectedLocation 设置器时,它现在都会应用相同的规则,即检查位置是 01、02 还是 03,它将设置我的 IsActive 属性,因此选择/取消选中复选框:

public LocationViewModel SelectedLocation
{
    get { return this._selectedLocation; }
    set
    {
        Set(() => SelectedLocation, ref this._selectedLocation, value);
        if (this._selectedLocation.LocationText == "01")
            this.IsActive = false;
        else if (this._selectedLocation.LocationText == "02")
            this.IsActive = true;
        else if (this._selectedLocation.LocationText == "03")
        {
            this.IsActive = false;
        }
    }
}

这显然并不理想,但它的工作方式与预期完全一致。理想的解决方案是根据 XAML 中的条件将 IsChecked 设置为 true 或 false:

<DataTrigger Binding="{Binding SelectedLocation.LocationText}" Value="02" >
<Setter Property="IsChecked" Value="True" />
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>

但是无论出于什么原因,当我使用这个方法时,当我手动选中复选框时它会触发IsActive setter,但它不会根据触发规则触发它,从而使这个属性无用,我不能拥有.

无论如何,我认为就是这样。希望这对其他人有所帮助。

更新

令我惊讶的是,我不得不发布更新,而复选框被禁用

<DataGridCheckBoxColumn Header="Active" Binding="{Binding IsActive, Mode=TwoWay, 
     UpdateSourceTrigger=PropertyChanged}">
    <DataGridCheckBoxColumn.ElementStyle>
        <Style TargetType="CheckBox">
            <Setter Property="HorizontalAlignment" Value="Center" />
            <Setter Property="VerticalAlignment" Value="Center" />
            <Style.Triggers>
                <DataTrigger Binding="{Binding SelectedLocation.LocationText}" Value="01" >
                    <Setter Property="IsEnabled" Value="True" />
                </DataTrigger>
                <DataTrigger Binding="{Binding SelectedLocation.LocationText}" Value="02" >
                    <Setter Property="IsEnabled" Value="False" />
                </DataTrigger>
                <DataTrigger Binding="{Binding SelectedLocation.LocationText}" Value="03" >
                    <Setter Property="IsEnabled" Value="False" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </DataGridCheckBoxColumn.ElementStyle>
</DataGridCheckBoxColumn>

它只是暂时禁用。双击单元格实际上启用了单元格,然后重新启用了复选框!让您禁用复选框然后在单元格进入编辑模式时重新启用它完全没有意义!!!

无论如何,幸运的是,有一种解决方法是禁用单元格而不是复选框。这可以通过使用CellStyle 而不是ElementStyle 来实现:

<DataGridCheckBoxColumn.CellStyle>
    <Style TargetType="DataGridCell">
        <Style.Triggers>
            <DataTrigger Binding="{Binding SelectedLocation.LocationText}" Value="01" >
                <Setter Property="IsEnabled" Value="True" />
            </DataTrigger>
            <DataTrigger Binding="{Binding SelectedLocation.LocationText}" Value="02" >
                <Setter Property="IsEnabled" Value="False" />
            </DataTrigger>
            <DataTrigger Binding="{Binding SelectedLocation.LocationText}" Value="03" >
                <Setter Property="IsEnabled" Value="False" />
            </DataTrigger>
        </Style.Triggers>
    </Style>
</DataGridCheckBoxColumn.CellStyle>

【讨论】:

    【解决方案2】:

    您将复选框绑定到 SelectedLineItem.Location。但我可以看到这个属性没有绑定到组合框的任何地方。您已将组合框的 TextBinding 绑定到“位置”。也尝试将其更改为 SelectedILineItem.Location。然后复选框将绑定到组合框中的键入值。

    如果您想根据组合框的选定值启用/禁用复选框,则应将组合框的 SelectedValuePath 绑定到此属性或将复选框绑定更改为 SelectedItem.LocationId 和触发条件,以匹配组合框的绑定。

    【讨论】:

      【解决方案3】:

      如果我正确理解了您的问题,我认为您可以尝试这种方式。我在一个组合框中绑定了一个国家列表,并根据选择的某些值禁用了另一列。

      Xaml 代码看起来像

      <Window.Resources>
              <staticData:CountryList x:Key="CountryList"/>
          </Window.Resources>
      
          <Grid>
              <DataGrid ItemsSource="{Binding SampleList}"  AutoGenerateColumns="False">
                  <DataGrid.Columns>
                      <DataGridTemplateColumn Header="Countries" Width="100">
                          <DataGridTemplateColumn.CellTemplate>
                              <DataTemplate>
                                  <TextBlock Text="{Binding SelectedCountry}"/>
                              </DataTemplate>
                          </DataGridTemplateColumn.CellTemplate>
      
                          <DataGridTemplateColumn.CellEditingTemplate>
                              <DataTemplate>
                                  <ComboBox Height="22" Grid.Row="0"
                                              ItemsSource="{StaticResource CountryList}"
                                              SelectedItem="{Binding SelectedCountry}">
                                  </ComboBox>
                              </DataTemplate>
                          </DataGridTemplateColumn.CellEditingTemplate>
      
                      </DataGridTemplateColumn>
      
                      <!-- Inputs -->
                      <DataGridTextColumn Width="SizeToCells" Header="Inputs" MinWidth="100" Binding="{Binding Input}" >
                          <DataGridTextColumn.CellStyle>
                              <Style TargetType="DataGridCell">
                                  <Style.Triggers>
                                      <DataTrigger Binding="{Binding SelectedCountry}" Value="UK">
                                          <Setter Property="IsEnabled" Value="false"/>
                                      </DataTrigger>
                                  </Style.Triggers>
                              </Style>
                          </DataGridTextColumn.CellStyle>
                      </DataGridTextColumn>
                  </DataGrid.Columns>
              </DataGrid>
          </Grid>
      

      Viewmodel 有

      public class MainViewModel : ObservableObject
          {
              private List<SampleData> sampleList;
      
              public List<SampleData> SampleList
              {
                  get { return sampleList; }
                  set { Set(ref sampleList, value); }
              }
      
              private Country selectedItem;
      
              public Country SelectedItem
              {
                  get { return selectedItem; }
                  set { selectedItem = value; }
              }
      
              private ObservableCollection<Country> countries;
      
              public ObservableCollection<Country> CountriesList
              {
                  get { return countries; }
                  set { this.Set(ref countries, value); }
              }
      
              public MainViewModel()
              {
                  CountriesList = new ObservableCollection<Country> { new Country { Id = 1, Name = "India" }, new Country { Id = 1, Name = "Australia" } };
      
                  SampleList = new List<SampleData>
                  {
                      new SampleData
                      {
                          Input = "shan", RowNum = 3, SelectedCountry = "India"
                      },
      
                      new SampleData
                      {
                          Input = "raj", RowNum = 1, SelectedCountry =  "India"
                      },
      
                      new SampleData
                      {
                          Input = "sfk", RowNum = 9,  SelectedCountry =  "India"
                      },
      
                      new SampleData
                      {
                          Input = "ShanSfk", RowNum = 7,  SelectedCountry =  "India"
                      }
                  };
              }
          }
      

      列表是

        public class CountryList : List<string>
          {
              public CountryList()
              {
                  this.Add("India");
                  this.Add("Australia");
                  this.Add("UK");
              }
          }
      

      根据组合框中选择的国家/地区,启用或禁用单元格。

      【讨论】:

      • 感谢您抽出宝贵时间提供如此详细的示例,但您为组合列使用了不同的控件类型,虽然我试图适应您的内容,但最终还是花了更多时间试图弄清楚这个问题。如果有任何帮助,我已经用更多信息更新了我的答案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-10
      • 2012-11-25
      相关资源
      最近更新 更多