DataGrid中已存在一个带有标题“*”的DataGridColumn。

13

我有一个使用MVVM模式的WPF应用程序。在我的某个视图中,我必须将一个ObservableCollection绑定到视图上。在该视图中,我有一个ListBox和一个DataGrid都绑定到同一个ObservableCollection,但是它们进行不同的操作,如事件、样式等。

我只需要其中一个控件显示在屏幕上,所以我创建了两个用户控件,一个用于DataGrid,另一个用于ListBox。我通过在主视图上放置一个ContentControl来在两者之间切换(类似于这篇博客)。默认视图是DataGrid,当单击按钮时显示另一个视图(即ListBox)。到此为止都能正常工作。

还有一件事需要注意,数据网格列是通过使用以下链接中描述的解决方案动态生成的。所以当我返回到DataGrid视图时,在foreach语句中向数据网格添加列时会出现错误(请参考上述链接的答案),例如:

"DataGridColumn with Header 'Ord' already exists in the Columns collection of a DataGrid. DataGrids cannot share columns and cannot contain duplicate column instances."

但我确信在向DataGrid添加列之前,它的Count属性为零(dataGrid.Columns.Count())。那么DataGrid的标题属性是如何保留下来的呢?是否有任何方法可以清除这些标题值?

请建议...


你可以尝试使用像Snoop这样的工具来检查可视化树。 - oddparity
我们能否看到这个ViewModel和Model,或者至少一个等价的例子? - Tyler Morrow
问题可能与您从args中填充的columns变量有关。在引用到foreach语句之前,请使用调试器检查该变量的内容。重复可能就在其中。 - Joseph Devlin
5个回答

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);
        }
    }
}

非常感谢您帮我省去了很多麻烦!这应该是被采纳的答案。完整的代码包含了对另一个StackOverflow解决方案的修复。 - Jessica

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
如果数据网格及其绑定设置一次,则不要干扰创建的数据列实例,如果可观察集合未更改,请改用触发器控制列表框和数据网格创建的用户控件的可见性属性。

0

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


0
在 WPF 中向控件或元素添加实例时,您应始终清除所添加控件的父级,因为当您将控件添加到子项集合中时,父控件会作为其父级添加到新子项中,这就是该消息告诉您的内容。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接