我有一个采用 MVVM 模式的 WPF 应用程序。在我的一种观点中,我必须绑定一个
ObservableCollection
才能查看。从这个角度来看,我有一个 ListBox
和一个 DataGrid
都绑定到同一个 ObservableCollection
,但执行不同的操作,例如事件、样式等。
我一次只需要显示其中一个控件,我所做的是创建两个用户控件,一个用于
DataGrid
,另一个用于 ListBox
。我通过在主视图上放置一个 ContentControl
来在它们之间进行切换(与此博客类似。默认视图是 DataGrid
,当单击按钮时,会显示另一个视图(即 ListBox
)。对此工作正常。
还要记住的一件事是,数据网格列是通过使用以下链接中描述的解决方案动态生成的。因此,当我返回到 DataGrid
视图时,在
foreach
语句中将列添加到数据网格时会抛出错误(请参阅上一个链接的答案),例如
但我确信在将列添加到“标题为“Ord”的 DataGridColumn 已存在于
DataGrid
的 Columns 集合中。DataGrid 不能共享列,也不能包含重复的列实例。”
DataGrid
之前,其
Count
属性为零(dataGrid.Columns.Count())。那么
DataGrid
标头属性是如何持久化的呢?有没有办法清除标题值?.请建议...
可绑定列。我的网格使用 CollectionViewSource
作为数据源,我在共享列时遇到了同样的问题。我已经通过反思修复了。所以在
BindableColumnsPropertyChanged
内部改变你的逻辑,如下所示:
// Add columns from this source.
foreach (var column in newColumns)
if (column != null)
{
var dg = (DataGrid)column.GetType().GetProperty("DataGridOwner", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(column, null);
dg?.Columns.Clear();
dataGrid.Columns.Add(column);
}
完整代码:
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
namespace SGRE.WOS.Common.UI
{
public class DataGridColumnsBehavior
{
public static readonly DependencyProperty BindableColumnsProperty = DependencyProperty.RegisterAttached("BindableColumns", typeof(ObservableCollection<DataGridColumn>), typeof(DataGridColumnsBehavior), new UIPropertyMetadata(null, BindableColumnsPropertyChanged));
/// <summary>Collection to store collection change handlers - to be able to unsubscribe later.</summary>
private static readonly Dictionary<DataGrid, NotifyCollectionChangedEventHandler> _handlers;
static DataGridColumnsBehavior()
{
_handlers = new Dictionary<DataGrid, NotifyCollectionChangedEventHandler>();
}
private static void BindableColumnsPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
if (!(source is DataGrid dataGrid)) return;
if (e.OldValue is ObservableCollection<DataGridColumn> oldColumns)
{
// Remove all columns.
dataGrid.Columns.Clear();
// Unsubscribe from old collection.
if (_handlers.TryGetValue(dataGrid, out var h))
{
oldColumns.CollectionChanged -= h;
_handlers.Remove(dataGrid);
}
}
var newColumns = e.NewValue as ObservableCollection<DataGridColumn>;
dataGrid.Columns.Clear();
if (newColumns != null)
{
// Add columns from this source.
foreach (var column in newColumns)
if (column != null)
{
var dg = (DataGrid)column.GetType().GetProperty("DataGridOwner", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(column, null);
dg?.Columns.Clear();
dataGrid.Columns.Add(column);
}
// Subscribe to future changes.
NotifyCollectionChangedEventHandler h = (_, ne) => OnCollectionChanged(ne, dataGrid);
_handlers[dataGrid] = h;
newColumns.CollectionChanged += h;
}
}
private static void OnCollectionChanged(NotifyCollectionChangedEventArgs ne, DataGrid dataGrid)
{
switch (ne.Action)
{
case NotifyCollectionChangedAction.Reset:
dataGrid.Columns.Clear();
if (ne.NewItems != null && ne.NewItems.Count > 0)
foreach (DataGridColumn column in ne.NewItems)
dataGrid.Columns.Add(column);
break;
case NotifyCollectionChangedAction.Add:
foreach (DataGridColumn column in ne.NewItems)
dataGrid.Columns.Add(column);
break;
case NotifyCollectionChangedAction.Move:
dataGrid.Columns.Move(ne.OldStartingIndex, ne.NewStartingIndex);
break;
case NotifyCollectionChangedAction.Remove:
foreach (DataGridColumn column in ne.OldItems)
dataGrid.Columns.Remove(column);
break;
case NotifyCollectionChangedAction.Replace:
dataGrid.Columns[ne.NewStartingIndex] = ne.NewItems[0] as DataGridColumn;
break;
}
}
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);
}
}
}
提到的链接中的行为后,我遇到了同样的错误。这个问题很老了,但如果其他人有同样的问题,我通过添加一个“桥”类来解决它,而不是直接添加列。
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows.Controls;
namespace AlyElHaddad.Stackoverflow
{
public class DataGridColumnCollection : ObservableCollection<DataGridColumn>
{
public DataGridColumnCollection()
: base()
{ }
public DataGridColumnCollection(IEnumerable<DataGridColumn> collection)
: base(collection)
{ }
public DataGridColumnCollection(List<DataGridColumn> list)
: base(list)
{ }
}
}
在 XAML 中,不要直接添加列,而是将它们添加到
DataGridColumnCollection
中。
<aly:DataGridColumnCollection xmlns:aly="clr-namespace:AlyElHaddad.Stackoverflow">
<DataGridTextColumn Header="Column1" Binding="{Binding Column1}"/>
<DataGridTextColumn Header="Column2" Binding="{Binding Column2}"/>
<DataGridTextColumn Header="Column3" Binding="{Binding Column3}"/>
</aly:DataGridColumnCollection>
DataGridOwner
。解决方案是清除 DataGridColumn 的 DataGridOwner 属性,就像 Rune Anderson 在另一个问题中发布的那样。感谢 Rune Anderson 在
如何将 WPF DataGrid 绑定到可变数量的列?中的评论中发布了此答案
Table.Columns.Clear();
foreach (var column in columns)
{
var dataGridOwnerProperty = column.GetType().GetProperty("DataGridOwner", BindingFlags.Instance | BindingFlags.NonPublic);
dataGridOwnerProperty?.SetValue(column, null);
Table.Columns.Add(column);
}