我遇到类似以下帖子的问题:
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的一小部分。
<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>
一些重要的事实。
有没有人知道为什么这不起作用?
答案 0 :(得分:7)
这是我用一点点黑客来解决的问题。
首先,您需要从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"};
}
要取消隐藏,您只需要从列表中删除相应的名称,或者在没有隐藏名称的情况下重新创建它。
答案 1 :(得分:6)
我有另一个解决此问题的方法,它使用类似于在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"));
}
}
}
}
}
答案 2 :(得分:3)
datagrid列继承自DependencyObject而不是FrameworkElement。在WPF中,这没什么大不了的......但在Silverlight中,您只能绑定到FrameworkElement对象。因此,当您尝试时,您会收到AG_E_PARSER_BAD_PROPERTY_VALUE的描述性错误消息。
答案 3 :(得分:2)
我不知道这会有多大帮助,但我在我的最新项目中遇到了数据网格列缺乏依赖属性问题。我做的是在网格列视图模型中创建一个事件,然后在客户端组装网格时,使用闭包将网格列订阅到列视图模型。我的特殊问题是宽度。它从网格列的视图模型类开始,看起来像这样的伪代码:
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事件并在网格绑定的viewmodel上调用resize方法。这又调用每个网格列视图模型的resize方法。然后,网格列视图模型会引发数据网格文本列订阅的ColumnResized
事件,并更新宽度。
我意识到这不是直接解决你的问题,但是当它没有依赖属性时,我可以将视图模型“绑定”到数据网格列。闭包是一个简单的结构,很好地封装了我想要的行为,并且对于跟在我后面的人来说是完全可以理解的。我认为不难想象如何修改它以应对能见度的变化。您甚至可以在页面/用户控件的加载事件中连接事件处理程序。
答案 4 :(得分:1)
Chris Mancini,
您没有创建绑定到数据网格列的“绑定”属性。好吧,你写“{Binding User.UserName}”,但它不会创建绑定,因为(如zachary所说)datagrid列不从FrameworkElement继承而且没有SetBinding方法。 因此表达式“{Binding User.UserName}”只是创建Binding对象并将其分配给column的Binding属性(此属性是Binding的类型)。 然后datagrid列生成单元格内容(GenerateElement - protected方法)使用此Binding对象设置生成元素的绑定(例如,生成的TextBlock的Text属性),这是FrameworkElements
答案 5 :(得分:1)
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"));
}
答案 6 :(得分:1)
请注意,问题不仅仅是“可见性”不是依赖属性。在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/
还有一些StackOverflow问题:
WPF Hide DataGridColumn via a binding
Binding Visible property of a DataGridColumn in WPF DataGrid
答案 7 :(得分:1)
这适用于数据网格模板列:
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;
}
}
}
答案 8 :(得分:0)
从MyDataGridTextColumn类中,您可以获取周围的DataGrid。 然后从DataGrid的DataContext中获取ViewModel,并为ViewModel的PropertyChanged事件添加处理程序。在处理程序中,您只需检查属性名称及其值,并相应地更改列的可见性。 它不是最好的解决方案,但它应该工作;)