带有标题“*”的 DataGridColumn 已存在于 DataGrid 的 Columns 集合中

问题描述 投票:0回答:6

我有一个采用 MVVM 模式的 WPF 应用程序。在我的一种观点中,我必须绑定一个

ObservableCollection
才能查看。从这个角度来看,我有一个
ListBox
和一个
DataGrid
都绑定到同一个
ObservableCollection
,但执行不同的操作,例如事件、样式等。

我一次只需要显示其中一个控件,我所做的是创建两个用户控件,一个用于

DataGrid
,另一个用于
ListBox
。我通过在主视图上放置一个
ContentControl
来在它们之间进行切换(与此博客类似。默认视图是
DataGrid
,当单击按钮时,会显示另一个视图(即
ListBox
)。对此工作正常。

还要记住的一件事是,数据网格列是通过使用以下

链接中描述的解决方案动态生成的。因此,当我返回到 DataGrid

 视图时,在 
foreach
 语句中将列添加到数据网格时会抛出错误(请参阅上一个链接的答案),例如 

“标题为“Ord”的 DataGridColumn 已存在于 DataGrid

 的 Columns 集合中。DataGrid 不能共享列,也不能包含重复的列实例。”

但我确信在将列添加到

DataGrid

 之前,其 
Count
 属性为零(dataGrid.Columns.Count())。那么 
DataGrid
 标头属性是如何持久化的呢?有没有办法清除标题值?.

请建议...

c# wpf mvvm wpfdatagrid
6个回答
4
投票
我正在使用

可绑定列。我的网格使用 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); } } }
    

3
投票
在使用

提到的链接中的行为后,我遇到了同样的错误。这个问题很老了,但如果其他人有同样的问题,我通过添加一个“桥”类来解决它,而不是直接添加列。

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>
    

0
投票
在 WPF 中向控件或元素添加实例时,您应该始终清除添加的控件的父控件,因为当您将控件添加到子控件集合时,父控件将作为其父控件添加到新子控件中,这就是什么这条消息正在告诉你


0
投票
如果您使用触发器交换视图,请将内容设置为动态资源,以便数据网格始终在运行时解析。


0
投票
如果数据网格及其绑定设置一次,则不要妨碍创建的数据列实例,如果可观察集合没有更改,请使用为列表框和数据网格创建的用户控件的可见性属性触发器。


0
投票
问题不在于 DataGrid,而在于 DataGridColumn 在第二次添加时已经有一个

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); }
    
© www.soinside.com 2019 - 2024. All rights reserved.