是否可以在 mvvm 模式中获取 wpf 数据网格上的动态列?
Posted
技术标签:
【中文标题】是否可以在 mvvm 模式中获取 wpf 数据网格上的动态列?【英文标题】:Is it possible to get dynamic columns on wpf datagrid in mvvm pattern? 【发布时间】:2013-03-11 01:28:12 【问题描述】:我正在使用 wpf 开发产品(使用 MVVM 模式)。根据用户的自定义(用户选择列),我必须将一组数据显示到数据网格中。目前,我正在将带有一组属性的ObservableCollection
绑定到数据网格的ItemSource
。这将我限制在固定的列大小。
注意:列出了 n 个列名供用户选择。
如果它在后面的代码中完成,“datagrid.columns.add()”很容易。在这种情况下,任何人都可以帮助我吗?
我的 xaml:
<my:DataGrid
AutoGenerateColumns="False"
Margin="357,121.723,82,41"
Name="dataGrid3"
c:DataGridExtension.Columns="Binding ColumnCollection"
/>
我的命令类:
public static class DataGridExtension
public static ObservableCollection<DataGridColumn> GetColumns(DependencyObject obj)
return (ObservableCollection<DataGridColumn>)obj.GetValue(ColumnsProperty);
public static void SetColumns(
DependencyObject obj, ObservableCollection<DataGridColumn> value)
obj.SetValue(ColumnsProperty, value);
public static readonly DependencyProperty ColumnsProperty =
DependencyProperty.RegisterAttached(
"Columns",
typeof(ObservableCollection<DataGridColumn>),typeof(DataGridExtension),
new UIPropertyMetadata(new ObservableCollection<DataGridColumn>(),
OnDataGridColumnsPropertyChanged));
private static void OnDataGridColumnsPropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
if (d.GetType() == typeof(DataGrid))
DataGrid myGrid = d as DataGrid;
var Columns = (ObservableCollection<DataGridColumn>)e.NewValue;
if (Columns != null)
myGrid.Columns.Clear();
if (Columns != null && Columns.Count > 0)
foreach (DataGridColumn dataGridColumn in Columns)
myGrid.Columns.Add(dataGridColumn);
Columns.CollectionChanged +=
(object sender, NotifyCollectionChangedEventArgs args)
if (args.NewItems != null)
//foreach (DataGridColumn column in args.NewItems.Cast<DataGridColumn>())
// myGrid.Columns.Add(column);
if (args.OldItems != null)
//foreach (DataGridColumn column in args.OldItems.Cast<DataGridColumn>())
// myGrid.Columns.Remove(column);
;
以及我在视图模型中的属性:
private ObservableCollection<DataGridColumn> _columnCollection =
new ObservableCollection<DataGridColumn>();
public ObservableCollection<DataGridColumn> ColumnCollection
get
return this._columnCollection;
set
_columnCollection = value;
base.OnPropertyChanged("ColumnCollection");
//Error
//base.OnPropertyChanged<ObservableCollection<DataGridColumn>>(
// () => this.ColumnCollection);
【问题讨论】:
mvvm 不限制您使用代码隐藏 粗略方式 - 预先创建所有列并隐藏用户未选择的内容。 blindmeis,正如你所说的 mvvm 并没有限制我的代码隐藏,我的 TL 是...... 正如我所说,我使用 ObservableCollection谢谢你的努力家伙......终于我找到了解决方案......
在这里..(完整的 wpf mvvm)
在我的命令文件中:
public class DataGridColumnsBehavior
public static readonly DependencyProperty BindableColumnsProperty =
DependencyProperty.RegisterAttached("BindableColumns",
typeof(ObservableCollection<DataGridColumn>),
typeof(DataGridColumnsBehavior),
new UIPropertyMetadata(null, BindableColumnsPropertyChanged));
private static void BindableColumnsPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
DataGrid dataGrid = source as DataGrid;
ObservableCollection<DataGridColumn> columns = e.NewValue as ObservableCollection<DataGridColumn>;
dataGrid.Columns.Clear();
if (columns == null)
return;
foreach (DataGridColumn column in columns)
dataGrid.Columns.Add(column);
columns.CollectionChanged += (sender, e2) =>
NotifyCollectionChangedEventArgs ne = e2 as NotifyCollectionChangedEventArgs;
if (ne.Action == NotifyCollectionChangedAction.Reset)
dataGrid.Columns.Clear();
if (ne.NewItems != null)
foreach (DataGridColumn column in ne.NewItems)
dataGrid.Columns.Add(column);
else if (ne.Action == NotifyCollectionChangedAction.Add)
if (ne.NewItems != null)
foreach (DataGridColumn column in ne.NewItems)
dataGrid.Columns.Add(column);
else if (ne.Action == NotifyCollectionChangedAction.Move)
dataGrid.Columns.Move(ne.OldStartingIndex, ne.NewStartingIndex);
else if (ne.Action == NotifyCollectionChangedAction.Remove)
if (ne.OldItems != null)
foreach (DataGridColumn column in ne.OldItems)
dataGrid.Columns.Remove(column);
else if (ne.Action == NotifyCollectionChangedAction.Replace)
dataGrid.Columns[ne.NewStartingIndex] = ne.NewItems[0] as DataGridColumn;
;
public static void SetBindableColumns(DependencyObject element, ObservableCollection<DataGridColumn> value)
element.SetValue(BindableColumnsProperty, value);
public static ObservableCollection<DataGridColumn> GetBindableColumns(DependencyObject element)
return (ObservableCollection<DataGridColumn>)element.GetValue(BindableColumnsProperty);
在我的 xaml 中:
<my:DataGrid AutoGenerateColumns="False" Margin="357,121.723,82,41" Name="dataGrid3" ItemsSource="Binding Path=Datatable" c:DataGridColumnsBehavior.BindableColumns="Binding ColumnCollection" />
最后在我的视图模型中:
private ObservableCollection<DataGridColumn> _columnCollection = new ObservableCollection<DataGridColumn>();
public ObservableCollection<DataGridColumn> ColumnCollection
get
return this._columnCollection;
set
_columnCollection = value;
base.OnPropertyChanged("ColumnCollection");
//Error
//base.OnPropertyChanged<ObservableCollection<DataGridColumn>>(() => this.ColumnCollection);
private DataTable _datatable = new DataTable();
public DataTable Datatable
get
return _datatable;
set
if (_datatable != value)
_datatable = value;
base.OnPropertyChanged("Datatable");
在我的构造函数中:
public MainViewModel()
Datatable.Columns.Add("Name",typeof(string));
Datatable.Columns.Add("Color", typeof(string));
Datatable.Columns.Add("Phone", typeof(string));
Datatable.Rows.Add("Vinoth", "#00FF00", "456345654");
Datatable.Rows.Add("lkjasdgl", "Blue", "45654");
Datatable.Rows.Add("Vinoth", "#FF0000", "456456");
System.Windows.Data.Binding bindings = new System.Windows.Data.Binding("Name");
System.Windows.Data.Binding bindings1 = new System.Windows.Data.Binding("Phone");
System.Windows.Data.Binding bindings2 = new System.Windows.Data.Binding("Color");
DataGridTextColumn s = new DataGridTextColumn();
s.Header = "Name";
s.Binding = bindings;
DataGridTextColumn s1 = new DataGridTextColumn();
s1.Header = "Phone";
s1.Binding = bindings1;
DataGridTextColumn s2 = new DataGridTextColumn();
s2.Header = "Color";
s2.Binding = bindings2;
FrameworkElementFactory textblock = new FrameworkElementFactory(typeof(TextBlock));
textblock.Name = "text";
System.Windows.Data.Binding prodID = new System.Windows.Data.Binding("Name");
System.Windows.Data.Binding color = new System.Windows.Data.Binding("Color");
textblock.SetBinding(TextBlock.TextProperty, prodID);
textblock.SetValue(TextBlock.TextWrappingProperty, TextWrapping.Wrap);
//textblock.SetValue(TextBlock.BackgroundProperty, color);
textblock.SetValue(TextBlock.NameProperty, "textblock");
//FrameworkElementFactory border = new FrameworkElementFactory(typeof(Border));
//border.SetValue(Border.NameProperty, "border");
//border.AppendChild(textblock);
DataTrigger t = new DataTrigger();
t.Binding = new System.Windows.Data.Binding Path = new PropertyPath("Name"), Converter = new EnableConverter(), ConverterParameter ="Phone" ;
t.Value = 1;
t.Setters.Add(new Setter(TextBlock.BackgroundProperty, Brushes.LightGreen, textblock.Name));
t.Setters.Add(new Setter(TextBlock.ToolTipProperty, bindings, textblock.Name));
DataTrigger t1 = new DataTrigger();
t1.Binding = new System.Windows.Data.Binding Path = new PropertyPath("Name"), Converter = new EnableConverter(), ConverterParameter = "Phone" ;
t1.Value = 2;
t1.Setters.Add(new Setter(TextBlock.BackgroundProperty, Brushes.LightYellow, textblock.Name));
t1.Setters.Add(new Setter(TextBlock.ToolTipProperty, bindings, textblock.Name));
DataTemplate d = new DataTemplate();
d.VisualTree = textblock;
d.Triggers.Add(t);
d.Triggers.Add(t1);
DataGridTemplateColumn s3 = new DataGridTemplateColumn();
s3.Header = "Name 1";
s3.CellTemplate = d;
s3.Width = 140;
ColumnCollection.Add(s);
ColumnCollection.Add(s1);
ColumnCollection.Add(s2);
ColumnCollection.Add(s3);
【讨论】:
当您在 ViewModel 中使用 System.Windows.Controls 中的“DataGridColumn”时,这种纯 MVVM 方法如何? 注意 - 此代码已在另一个 Stack Overflow 答案中修复:***.com/questions/17986380/…。请参阅琼斯的回答。【参考方案2】:我想扩展前面的示例(答案)订阅和取消订阅事件 CollectionChanged 的能力。
行为(在 System.Windows.Interactivity 上添加参考):
public class ColumnsBindingBehaviour : Behavior<DataGrid>
public ObservableCollection<DataGridColumn> Columns
get return (ObservableCollection<DataGridColumn>) base.GetValue(ColumnsProperty);
set base.SetValue(ColumnsProperty, value);
public static readonly DependencyProperty ColumnsProperty = DependencyProperty.Register("Columns",
typeof(ObservableCollection<DataGridColumn>), typeof(ColumnsBindingBehaviour),
new PropertyMetadata(OnDataGridColumnsPropertyChanged));
private static void OnDataGridColumnsPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
var context = source as ColumnsBindingBehaviour;
var oldItems = e.OldValue as ObservableCollection<DataGridColumn>;
if (oldItems != null)
foreach (var one in oldItems)
context._datagridColumns.Remove(one);
oldItems.CollectionChanged -= context.collectionChanged;
var newItems = e.NewValue as ObservableCollection<DataGridColumn>;
if (newItems != null)
foreach (var one in newItems)
context._datagridColumns.Add(one);
newItems.CollectionChanged += context.collectionChanged;
private ObservableCollection<DataGridColumn> _datagridColumns;
protected override void OnAttached()
base.OnAttached();
this._datagridColumns = AssociatedObject.Columns;
private void collectionChanged(object sender, NotifyCollectionChangedEventArgs e)
switch (e.Action)
case NotifyCollectionChangedAction.Add:
if (e.NewItems != null)
foreach (DataGridColumn one in e.NewItems)
_datagridColumns.Add(one);
break;
case NotifyCollectionChangedAction.Remove:
if (e.OldItems != null)
foreach (DataGridColumn one in e.OldItems)
_datagridColumns.Remove(one);
break;
case NotifyCollectionChangedAction.Move:
_datagridColumns.Move(e.OldStartingIndex, e.NewStartingIndex);
break;
case NotifyCollectionChangedAction.Reset:
_datagridColumns.Clear();
if (e.NewItems != null)
foreach (DataGridColumn one in e.NewItems)
_datagridColumns.Add(one);
break;
查看:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:loc="clr-namespace:WpfApplication1"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid x:Name="_dataGrid">
<DataGrid.Columns>
<DataGridTextColumn Header="Test" />
</DataGrid.Columns>
<i:Interaction.Behaviors>
<loc:ColumnsBindingBehaviour Columns="Binding DataGridColumns"/>
</i:Interaction.Behaviors>
</DataGrid>
</Grid>
视图模型:
private ObservableCollection<DataGridColumn> _dataGridColumns;
public ObservableCollection<DataGridColumn> DataGridColumns
get
if (_dataGridColumns == null)
_dataGridColumns = new ObservableCollection<DataGridColumn>()
new DataGridTextColumn()
Header = "Column1"
;
return _dataGridColumns;
set
_dataGridColumns = value;
OnPropertyChanged("DataGridColumns");
【讨论】:
以上是关于是否可以在 mvvm 模式中获取 wpf 数据网格上的动态列?的主要内容,如果未能解决你的问题,请参考以下文章