带标题的DataGridColumn'*';DataGrid的Columns集合中已存在
本文关键字:集合 存在 Columns 标题 DataGridColumn DataGrid | 更新日期: 2023-09-27 18:25:55
我有一个带有MVVM模式的WPF应用程序。在我的一种观点中,我必须绑定一个ObservableCollection
来查看。在这个视图中,我有一个ListBox
和一个DataGrid
,它们都绑定到同一ObservableCollection
,但做不同的事情,如事件、样式等
我一次只需要显示其中一个控件,我所做的是创建两个用户控件,一个用于DataGrid
,另一个用于ListBox
。我在主视图上放了一个ContentControl
(类似于这个博客。默认视图是DataGrid
,点击按钮时会显示另一个视图(即ListBox
)。到目前为止运行良好。
需要记住的另一件事是,数据网格列是通过使用以下链接中描述的解决方案动态生成的。因此,当我回到DataGrid
视图时,它在foreach
语句中向数据网格添加列时抛出了一个错误(请参考上一个链接的答案),如
"标题为'Ord'的DataGridColumn已存在于
DataGrid
的Columns集合中。DataGrids不能共享列,也不能包含重复的列实例。"
但我确信在将列添加到DataGrid
之前,它的Count
属性是零(dataGrid.columns.Count())。那么DataGrid
标头属性是如何持久化的呢?有什么方法可以清除标头值吗?。
请建议。。。
在使用上述链接中的行为后,我也出现了同样的错误。这个问题很老,但如果其他人也有同样的问题,我通过添加一个"bridge"类来解决,而不是直接添加列。
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>
我正在使用可绑定列。我的网格使用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);
}
}
}
在WPF中将实例添加到控件或元素时,您应该始终清除添加的控件的父控件,因为当您将控件添加到子控件的集合时,父控件将作为其父控件添加到新的子控件,这就是消息告诉您的
如果使用触发器交换视图,请将内容设置为动态资源,以便始终在运行时解析数据网格。
如果数据网格及其绑定设置了一次,那么不要妨碍创建的数据列的实例,如果可观察集合没有更改,而是使用触发器为列表框和数据网格创建的用户控件的可见性属性。