【问题标题】:How to bind the visibility of a Datagrid Column to a Checkbox in WPF如何将 Datagrid 列的可见性绑定到 WPF 中的复选框
【发布时间】:2014-08-24 01:57:24
【问题描述】:

我在 DataGrid 中有几列,我希望根据该列的复选框显示或隐藏它们。我事先进行了相当多的搜索,发现了许多不同的答案,但在每种情况下,它们要么不起作用,要么针对不同的情况。

如果我理解正确,ElementName 将无法工作,因为 Visual Tree 不可用。但是我尝试设置 Source 和 RelativeSource 没有任何运气。在其当前状态下,它简单地返回错误:未解析的引用“filterDuration”。该错误导致程序无法运行。

更新了以下建议和更多信息,但仍未解决

这是转换器:(它甚至从来没有进入过这个)

[ValueConversion(typeof(bool), typeof(Visibility))]
public class FilterVisibilityConverter : IValueConverter
{ 
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        bool isSelected = System.Convert.ToBoolean(value);

        return isSelected ? Visibility.Visible : Visibility.Hidden;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var v = (Visibility)value; 
        return v == Visibility.Hidden ? false : true;
    }
}

这里是复选框:

<DataTemplate x:Key="Settings">
        <DockPanel>                 
            <GroupBox Header="Filter Columns" DockPanel.Dock="Right">
                <ag:AutoGrid Columns="150, 150, 150" RowCount="7" RowHeight="20"> 
                    <CheckBox Content="Id" />
                    <CheckBox Content="Desc" />
                    <CheckBox x:Name="filterDuration"  Content="Duration" />                          
                </ag:AutoGrid>
            </GroupBox>
        </DockPanel>
    </DataTemplate>

这是 DataGrid:(为简洁起见,删除了额外的列和属性)

<DockPanel Grid.Row="0" Grid.Column="0">
        <Expander  DockPanel.Dock="Top" Header="{x:Static p:Resources.shSettings}"  ContentTemplate="{StaticResource Settings}" IsExpanded="True" />
        <Border DockPanel.Dock="Top" Background="#FF595959">
            <TextBlock Text="{x:Static p:Resources.shViolations}" Style="{StaticResource SectionHeaderText}" /> 
        </Border>
        <DataGrid IsReadOnly="True" ItemsSource="{Binding items}" SelectedItem="{Binding Path=selecteditem}" AutoGenerateColumns="False"    >
            <DataGrid.Columns>
                <DataGridTextColumn Header="Duration" Binding="{Binding Duration}" Visibility="{Binding Source={x:Reference filterDuration}, Path=IsChecked, Converter={StaticResource FilterVisibility}}" />
                <DataGridTextColumn Header="CTG Id" Binding="{Binding Id}" />
            </DataGrid.Columns>
        </DataGrid>
    </DockPanel>

【问题讨论】:

  • 在小样本中试用过,您的代码在我的最后工作正常。
  • CheckBox 本身位于 Expander 的 ContentTemplate 中,但 Expander 开始展开后可见。我认为这根本不重要,但我正在抓住稻草。

标签: c# wpf xaml


【解决方案1】:

DataTemplate 内的控件没有相同的名称范围,因此无法通过 ElementName 或 x:Reference 从其名称范围之外的控件绑定。

作为一种解决方法,您可以通过以下方式实现:

  1. 在资源部分声明一个虚拟复选框。
  2. 用这个虚拟复选框绑定 filterDuration 的 IsChecked。
  3. 使用虚拟 checkBox IsChecked 属性绑定 DataGridTextColumn 的可见性。

代码:

<DockPanel .....>
   <DockPanel.Resources>
      <CheckBox x:Key="DummyCheckBox"/>
   </DockPanel.Resources>
   .........
      <DataGridTextColumn Header="Duration" Binding="{Binding Duration}"
          Visibility="{Binding Source={StaticResource DummyCheckBox}, 
                  Path=IsChecked, Converter={StaticResource FilterVisibility}}"/>
   ......... 
</DockPanel>

DataTemplate中:

<CheckBox x:Name="filterDuration" Content="Duration"
         IsChecked="{Binding IsChecked, Source={StaticResource DummyCheckBox}}"/>

注意 - 您也可以在窗口资源部分声明资源,以防无法从 dataTemplate 中的 Dockpanel 资源部分访问。


显然理想的解决方案是:

  1. 在 ViewModel 中有 bool 属性。
  2. 将 filterDuration 复选框绑定到该绑定属性。
  3. 将 dataGrid 列的 Visibility 绑定到绑定的 bool 属性。

【讨论】:

  • 如果您想要一种“更简洁”的方式并且不介意编写一些代码,那么您还可以定义一个新类来保持 IsChecked 状态(而不是为此滥用虚拟 CheckBox)。跨度>
  • @Zarat - 你所说的abusing 到底是什么意思?
  • 您创建复选框的实例只是为了将其用作可绑定布尔属性的容器。不完全是复选框实例的重点。也许“滥用”这个词太强了,只是说您正在使用该对象来做它不打算做的事情。在这种情况下不会造成任何伤害,但恕我直言,这不是推荐的模式,因为您通常不知道控件属性附加了哪些副作用。
  • @Zarat - 这只是我提到的一种解决方法,从不推荐它作为理想的解决方案。同样创建任何 DO 只是为了持有布尔属性对我来说似乎不正确。我已经提到的理想解决方案是在 ViewModel 中绑定 bool 属性。但是,与 IsChecked 属性绑定不会产生任何副作用。如果它会,那么问题本身在 OP 想要与 IsChecked 属性绑定的地方是无效的。
  • 我相信我在这里学到了一些我不知道的非常关键的东西。我没有意识到 DataTemplate 有自己的范围,基本上看不到它的外部或其他 DataTemplate。这确实为我在这方面的理解奠定了基础。如果我能给你超过 +1,我会的。
【解决方案2】:

您的示例对我有用,您不应该让自己对设计器中的错误感到困惑,在运行时它可以工作 - x:Reference 绑定似乎让设计器感到困惑。

除此之外,您的值转换器中还有一些小错误:

  • ConvertBack 返回“值”是错误的,它应该将可见性转换为布尔值。 您应该正确实现这一点,因为您进行了双向绑定。 (虽然我不确定您为什么要这样做,但我想说单向绑定就足够了。列的可见性毕竟不是用户可以更改的,因此无需将其更改传播回去到复选框,对吧?)
  • 您应该在类的属性中声明值转换器处理的类型: [ValueConversion(typeof(bool), typeof(Visibility))]

更新

对于更新后的问题,您需要一种方法来链接不同的上下文。如果您无法通过向 ViewModel 添加属性来做到这一点,您仍然可以创建资源对象。 Rohit Vats 的另一个答案滥用 CheckBox 作为这样的资源对象。如果您想要一种“更清洁”的方式,只需为选中状态定义一个新的 DependencyObject 子类,并使用 DependencyProperty,然后将该类的一个实例放入静态资源中。然后可以使用此资源使控件相互通信。

【讨论】:

  • 我得到的错误不仅仅是设计师的问题,它在运行时实际上会抛出该错误。
  • 感谢推荐[ValueConversion]标签,之前没用过。此外,您的解释添加到@Rohit Vats 并给了我一些选择。有时,在真正点击之前,需要以不同的方式听到相同的信息。
猜你喜欢
  • 2018-07-28
  • 1970-01-01
  • 2016-06-11
  • 2010-12-01
  • 2017-11-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-06
  • 1970-01-01
相关资源
最近更新 更多