DataGridColumn с заголовком '*' уже существует в коллекции Columns DataGrid

У меня есть приложение WPF с шаблоном MVVM. В одном из моих представлений мне нужно привязать ObservableCollection для просмотра. В этом представлении у меня есть один ListBox и один DataGrid оба привязаны к тому же ObservableCollection, но выполняют разные вещи, такие как события, стиль и т.д.

Мне нужен только один из этих элементов управления, отображаемый за раз, и я создал два пользовательских элемента управления: один для DataGrid и другой для ListBox. И я переключился между ними, разместив ContentControl на главном представлении (что-то похожее на этот блог. По умолчанию отображается DataGrid и при нажатии на кнопку отображается другое представление (т.е. ListBox). До этого они работают нормально.

Еще одна вещь, чтобы иметь в виду, что столбцы Data Grid генерируются динамически, используя решение, описанное в следующей ссылке. Поэтому, когда я возвращаюсь к просмотру DataGrid, он бросает ошибку при добавлении столбцов в Data Grid в выражении foreach (PLS ссылается на ответ предыдущей ссылки), например

"DataGridColumn с заголовком" Ord "уже существует в коллекции Columns DataGrid. DataGrids не могут совместно использовать столбцы и не могут содержать повторяющиеся экземпляры столбцов.

Но я уверен, что перед добавлением столбцов в DataGrid его свойство Count равно нулю (dataGrid.Columns.Count()). Итак, как сохраняются свойства заголовка DataGrid? Есть ли способ очистить значения заголовков?.

Пожалуйста, предложите...

Ответ 1

У меня была такая же ошибка после использования поведения в упомянутой ссылке. Вопрос старен, но в случае, если у кого-то другая проблема, я решил это, добавив класс "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>

Ответ 2

При добавлении экземпляра к элементу управления или элементу в WPF вы всегда должны очищать добавленный родительский элемент управления, поскольку, когда вы добавляете элемент управления в коллекцию дочерних элементов, родительский элемент управления добавляется к новому дочернему элементу как родительскому, это это то, что сообщение сообщает вам.

Ответ 3

Если вы используете триггеры для замены представлений, установите контент как динамический ресурс, поэтому datagrid всегда разрешается во время выполнения.

Ответ 4

Если сетка данных и ее привязка заданы один раз, то не мешайте этому экземпляру создаваемых столбцов данных, если наблюдаемая коллекция не изменяется, вместо этого используйте свойство видимости для пользовательского элемента управления, созданного как для списка, так и для данных сетка с использованием триггеров.

Ответ 5

Я использую привязываемый столбец. Моя сетка использует 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);
        }
    }
}