DataGridColumn with Header '*' already exists in the Columns collection of a DataGrid

余生颓废 提交于 2021-02-20 05:48:28

问题


I have a WPF application with MVVM pattern. In one of my view, I have to bind an ObservableCollection to view. In that view, I have one ListBox and one DataGrid both bind to the same ObservableCollection but doing different things like events, style etc..

I need only one of these controls displayed at a time and what I did is created two user controls, one for DataGrid and other for ListBox. And I switched between them by placing a ContentControl on the main view(something similar to this blog. The default view is DataGrid and when click on a button the other view is displayed(i.e. ListBox). Up to this are working fine.

One more thing to keep in mind that the Data Grid columns are generated dynamically by using the solution described in the following link. So when I go back to DataGrid view it's throwing an error while adding columns to Data Grid in foreach statement (pls refer the answer of the previous link) like

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

But I'm sure that before adding columns to DataGrid its Count property is zero(dataGrid.Columns.Count()). So how the DataGrid header properties are persisted? Is there any way to clear the header values?.

Please suggest...


回答1:


I had have the same error after using the behavior in the mentioned link. The question is old but in case someone else has the same problem, I solved it by adding a 'bridge' class to use instead of adding the columns directly.

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

In XAML, instead of adding the columns directly, add them inside a 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>



回答2:


I'm using the Bindable Column. My grid uses CollectionViewSource for data source and I had same problem with columns being shared. I've fixed with reflection.

So inside BindableColumnsPropertyChanged change your logic like below:

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

Full Code:

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:


When adding an instance to a control or element in WPF you should allways clean the added control's parent, since, when you add a control to a collection of childs, the parent control is added to the new child as it's parent, this is what the message is telling you




回答4:


If you are using triggers to swap the views set the content as a dynamic resource so the datagrid is always resolved at runtime.




回答5:


If the data grid and its binding is set once then don't hamper with that instance of data columns created, if the observable collection is not changing, instead play with the visibility property for user control created for both list box and data grid using triggers.



来源:https://stackoverflow.com/questions/17986380/datagridcolumn-with-header-already-exists-in-the-columns-collection-of-a-dat

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!