如何绑定 DataGridColumn.Visibility?
Posted
技术标签:
【中文标题】如何绑定 DataGridColumn.Visibility?【英文标题】:How to bind DataGridColumn.Visibility? 【发布时间】:2010-11-05 21:52:59 【问题描述】:我有一个类似以下帖子的问题:
Silverlight DataGridTextColumn Binding Visibility
我需要根据 ViewModel 中的值使 Silverlight DataGrid 中的列可见/折叠。为此,我尝试将 Visibility 属性绑定到 ViewModel。但是我很快发现 Visibility 属性不是 DependencyProperty,因此无法绑定。
为了解决这个问题,我尝试子类化我自己的 DataGridTextColumn。通过这个新类,我创建了一个 DependencyProperty,它最终将更改推送到 DataGridTextColumn.Visibility 属性。如果我不进行数据绑定,这很好用。当我将数据绑定到我的新属性时,它会失败,并出现 AG_E_PARSER_BAD_PROPERTY_VALUE 异常。
public class MyDataGridTextColumn : DataGridTextColumn
#region public Visibility MyVisibility
public static readonly DependencyProperty MyVisibilityProperty =
DependencyProperty.Register("MyVisibility", typeof(Visibility), typeof(MyDataGridTextColumn), new PropertyMetadata(Visibility.Visible, OnMyVisibilityPropertyChanged));
private static void OnMyVisibilityPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
var @this = d as MyDataGridTextColumn;
if (@this != null)
@this.OnMyVisibilityChanged((Visibility)e.OldValue, (Visibility)e.NewValue);
private void OnMyVisibilityChanged(Visibility oldValue, Visibility newValue)
Visibility = newValue;
public Visibility MyVisibility
get return (Visibility)GetValue(MyVisibilityProperty);
set SetValue(MyVisibilityProperty, value);
#endregion public Visibility MyVisibility
这是 XAML 的一个小 sn-p。
<DataGrid ....>
<DataGrid.Columns>
<MyDataGridTextColumn Header="User Name"
Foreground="#FFFFFFFF"
Binding="Binding User.UserName"
MinWidth="150"
CanUserSort="True"
CanUserResize="False"
CanUserReorder="True"
MyVisibility="Binding Converter=StaticResource BoolToVisibilityConverter, Path=ShouldShowUser"/>
<DataGridTextColumn .../>
</DataGrid.Columns>
</DataGrid>
几个重要的事实。
转换器确实在上面的本地资源中定义。 转换器是正确的,它在解决方案中的许多其他地方都使用过。 如果我将 MyVisibility 属性的 Binding 语法替换为“Collapsed”,则 Column 实际上会消失。 如果我创建一个新的 DependencyProperty(即字符串 Foo)并绑定到它,我也会收到 AG_E_PARSER_BAD_PROPERTY_VALUE 异常。有人知道为什么这不起作用吗?
【问题讨论】:
对此有何解决方案?我不确定发生了什么(dp 对我来说看起来很好......),但如果你不能添加一个新的字符串 DependencyProperty 并绑定到它,那似乎一定是问题所在。 【参考方案1】:这是我使用小技巧想出的解决方案。
首先,您需要从 DataGrid 继承。
public class DataGridEx : DataGrid
public IEnumerable<string> HiddenColumns
get return (IEnumerable<string>)GetValue(HiddenColumnsProperty);
set SetValue(HiddenColumnsProperty, value);
public static readonly DependencyProperty HiddenColumnsProperty =
DependencyProperty.Register ("HiddenColumns",
typeof (IEnumerable<string>),
typeof (DataGridEx),
new PropertyMetadata (HiddenColumnsChanged));
private static void HiddenColumnsChanged(object sender,
DependencyPropertyChangedEventArgs args)
var dg = sender as DataGrid;
if (dg==null || args.NewValue == args.OldValue)
return;
var hiddenColumns = (IEnumerable<string>)args.NewValue;
foreach (var column in dg.Columns)
if (hiddenColumns.Contains ((string)column.GetValue (NameProperty)))
column.Visibility = Visibility.Collapsed;
else
column.Visibility = Visibility.Visible;
DataGridEx 类添加了一个新的 DP,用于根据 DataGridColumn 及其后代的 x:Name 隐藏列。
在您的 XAML 中使用:
<my:DataGridEx x:Name="uiData"
DataContext="Binding SomeDataContextFromTheVM"
ItemsSource="Binding Whatever"
HiddenColumns="Binding HiddenColumns">
<sdk:DataGridTextColumn x:Name="uiDataCountOfItems">
Header="Count"
Binding=Binding CountOfItems"
</sdk:DataGridTextColumn>
</my:DataGridEx>
您需要将这些添加到您的 ViewModel 或您使用的任何数据上下文中。
private IEnumerable<string> _hiddenColumns;
public IEnumerable<string> HiddenColumns
get return _hiddenColumns;
private set
if (value == _hiddenColumns)
return;
_hiddenColumns = value;
PropertyChanged (this, new PropertyChangedEventArgs("HiddenColumns"));
public void SomeWhereInYourCode ()
HiddenColumns = new List<string> "uiDataCountOfItems";
要取消隐藏,您只需从列表中删除相应的名称或重新创建它而不使用未隐藏的名称。
【讨论】:
谢谢...我还没有实现它,但是看起来它可以解决问题! 与 Silverlight 完美搭配.. :) 非常感谢伙计,你让我很开心..!!【参考方案2】:对于这个问题,我有另一个解决方案,它使用类似于您在 DataGridTextColumn 上找到的“绑定”属性的方法。由于列类是 DependencyObjects,因此不能直接对它们进行数据绑定,但是如果添加对实现 INotifyPropertyChanged 的 FrameworkElement 的引用,则可以将数据绑定传递给元素,然后使用依赖属性通知 Column数据绑定已更改。
需要注意的一点是,在 Column 本身而不是 Grid 上进行绑定可能意味着您将需要使用 DataContextProxy 来访问您想要将 Visibility 绑定到的字段(列绑定将默认为 ItemSource 的范围)。
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace XYZ.Controls
public class ExtendedDataGridTextColumn : DataGridTextColumn
private readonly Notifier _e;
private Binding _visibilityBinding;
public Binding VisibilityBinding
get return _visibilityBinding;
set
_visibilityBinding = value;
_e.SetBinding(Notifier.MyVisibilityProperty, _visibilityBinding);
public ExtendedDataGridTextColumn()
_e = new Notifier();
_e.PropertyChanged += ToggleVisibility;
private void ToggleVisibility(object sender, PropertyChangedEventArgs e)
if (e.PropertyName == "Visibility")
this.Visibility = _e.MyVisibility;
//Notifier class is just used to pass the property changed event back to the column container Dependency Object, leaving it as a private inner class for now
private class Notifier : FrameworkElement, INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public Visibility MyVisibility
get return (Visibility)GetValue(MyVisibilityProperty);
private set SetValue(MyVisibilityProperty, value);
public static readonly DependencyProperty MyVisibilityProperty = DependencyProperty.Register("MyVisibility", typeof(Visibility), typeof(Notifier), new PropertyMetadata(MyVisibilityChanged));
private static void MyVisibilityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
var n = d as Notifier;
if (n != null)
n.MyVisibility = (Visibility) e.NewValue;
n.PropertyChanged(n, new PropertyChangedEventArgs("Visibility"));
【讨论】:
我不得不注释掉倒数第二行 (n.MyVisibility = ...) 以使后续切换工作。【参考方案3】:datagrid 列继承自 DependencyObject 而不是 FrameworkElement。在 WPF 中,这没什么大不了的……但在 silverlight 中,您只能绑定到 FrameworkElement 对象。因此,当您尝试时,您会收到 AG_E_PARSER_BAD_PROPERTY_VALUE 的描述性错误消息。
【讨论】:
谢谢扎卡里。如果你描述的是这种情况,你能描述为什么绑定到“Binding”属性有效吗? 抱歉...我希望我知道,但这就是我的 Silverlight 知识的终点。 这不是真的。 Silverlight 允许绑定到 DependencyObjects 的依赖属性。问题只是列的 Visibility 属性不是 DependencyProperties。【参考方案4】:我不知道这有多大帮助,但我自己在最新项目中遇到了数据网格列缺乏依赖属性的问题。我所做的就是在网格列视图模型中创建一个事件,然后在客户端中组装网格时,使用闭包将网格列订阅到列视图模型。我的特殊问题是宽度。它从网格列的视图模型类开始,类似于以下伪代码:
public delegate void ColumnResizedEvent(double width);
public class GridColumnViewModel : ViewModelBase
public event ColumnResizedEvent ColumnResized;
public void Resize(double newContainerWidth)
// some crazy custom sizing calculations -- don't ask...
ResizeColumn(newWidth);
public void ResizeColumn(double width)
var handler = ColumnResized;
if (handler != null)
handler(width);
然后是组装网格的代码:
public class CustomGrid
public CustomGrid(GridViewModel viewModel)
// some stuff that parses control metadata out of the view model.
// viewModel.Columns is a collection of GridColumnViewModels from above.
foreach(var column in viewModel.Columns)
var gridCol = new DataGridTextColumn( ... );
column.ColumnResized += delegate(double width) gridCol.Width = new DataGridLength(width); ;
当数据网格在应用程序中调整大小时,resize 事件被拾取并在网格绑定到的视图模型上调用 resize 方法。这反过来调用每个网格列视图模型的调整大小方法。然后网格列视图模型引发ColumnResized
事件,数据网格文本列已订阅该事件,并更新其宽度。
我意识到这并不能直接解决您的问题,但这是我可以在没有依赖属性时将视图模型“绑定”到数据网格列的一种方式。闭包是一个简单的构造,它很好地封装了我想要的行为,并且对于我身后的人来说是完全可以理解的。我认为不难想象如何对其进行修改以应对可见度的变化。您甚至可以在页面/用户控件的加载事件中连接事件处理程序。
【讨论】:
【参考方案5】:克里斯·曼奇尼,
您没有创建与数据网格列的“绑定”属性的绑定。好吧,您编写“Binding User.UserName”,但它不会创建绑定,因为(正如 zachary 所说)datagrid 列没有从 FrameworkElement 继承并且没有 SetBinding 方法。 因此表达式“Binding User.UserName”只是创建了 Binding 对象并将其分配给列的 Binding 属性(该属性是 Binding 的类型)。 然后 datagrid 列在生成单元格内容时(GenerateElement - 受保护的方法)使用此 Binding 对象设置对作为 FrameworkElements 的生成元素(例如,在生成的 TextBlock 的 Text 属性上)的绑定
【讨论】:
【参考方案6】:GreatTall1 的解决方案很棒,但需要稍作改动才能使其发挥作用。
var n = d as Notifier;
if (n != null)
//Assign value in the callback will break the binding.
//n.MyVisibility = (Visibility)e.NewValue;
n.PropertyChanged(n, new PropertyChangedEventArgs("Visibility"));
【讨论】:
【参考方案7】:请注意,问题不仅仅是“可见性”不是依赖属性那么简单。在 DataGrid 中,列不是可视“树”的一部分,因此即使在 WPF(或 Silverlight 5)中也不能使用 AncestorType。
这是几个与 WPF 相关的链接(如果其中任何一个适用于 Silverlight,请发表评论 - 抱歉,我现在没有时间测试)
对某些解决方案的问题和失败有一个非常好的解释(以及一个聪明的解决方案): http://tomlev2.wordpress.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/
还有几个 *** 问题:
WPF Hide DataGridColumn via a binding
Binding Visible property of a DataGridColumn in WPF DataGrid
【讨论】:
【参考方案8】:这适用于数据网格模板列:
public class ExtendedDataGridColumn : DataGridTemplateColumn
public static readonly DependencyProperty VisibilityProperty = DependencyProperty.Register("Visibility", typeof(Visibility), typeof(DataGridTemplateColumn), new PropertyMetadata(Visibility.Visible, VisibilityChanged));
public new Visibility Visibility
get return (Visibility)GetValue(VisibilityProperty);
set SetValue(VisibilityProperty, value);
private static void VisibilityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
if ((DataGridTemplateColumn)d != null)
((DataGridTemplateColumn)d).Visibility = (Visibility)e.NewValue;
【讨论】:
【参考方案9】:从您的 MyDataGridTextColumn 类中,您可以获得周围的 DataGrid。 然后,您将 ViewModel 从 DataGrid 的 DataContext 中取出,并向 ViewModel 的 PropertyChanged 事件添加一个处理程序。在处理程序中,您只需检查属性名称及其值并相应地更改列的可见性。 它不是最好的解决方案,但它应该可以工作;)
【讨论】:
以上是关于如何绑定 DataGridColumn.Visibility?的主要内容,如果未能解决你的问题,请参考以下文章