Привязка к DataGrid

У меня есть два класса — Объект и Параметры.

Public class Object
{
    public int ID { get; set; }
    public string Name { get; set; }
    ......
    public List<Parameters> Paramlist { get; set; } = new List<Parameters>();
}

Public class Parameters
{   
    public string parName { get; set; }
    public string Value { get; set; }   
}

Существует около 40 типов объектов. Каждый тип объекта имеет несколько параметров, общие для всех из них перечислены в классе объекта. Однако каждый тип также имеет свои параметры. Вот почему я использую List.

Теперь пользователь может выбирать, какие объекты он хочет видеть в DataGrid. И затем он может выбрать, какие параметры он хочет видеть.

Таким образом, определен только один столбец DataGrid — ObjectName (имя экземпляра). Другие добавляются во время выполнения следующим образом.

 MainDataGrid.Columns.Add(new DataGridTextColumn
 {
     Header = _parameter,
     Binding = new System.Windows.Data.Binding(_parameter),
     //_parameter is string received from user by picking one of the parameters in another DataGrid

 });
  

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

Мне не обязательно, чтобы это был Лист, это была первая идея, с которой я работал. Явно не очень хороший вариант.

Спасибо за любые предложения.

Basically go through each instance in DataGrid and if the instance has the parameter in the List, Bind the value from the list to a new column. - зачем вам это делать вручную? столбец обычно имеет только этот элемент, и он автоматически преобразует этот элемент в экземпляр строки; для этого не требуется никакой ручной работы. i.sstatic.net/IxbQDPMW.png
Rand Random 07.06.2024 11:29

Не следует вручную создавать столбцы DataGrid. Просто включите автоматическое создание столбцов (оно включено по умолчанию), и когда пользователь завершит свой выбор, вы создадите DataTable из собранной информации. Затем привяжите DataGrid.ItemsSource к этому DataTable.

BionicCode 07.06.2024 11:33

Да, вы правы, я имел в виду то, чего хочу. Виноват. Я не был уверен, как точно описать мою проблему. Пользователь выберет имя параметра в другом DataGrid, имя будет добавлено в заголовок, и значение этого параметра в списке каждого экземпляра будет привязано. Если у экземпляра нет параметра, он будет пустым.

iksd 07.06.2024 11:33

но опять же, это уже должно работать, как описано, из коробки. просто создайте столбцы, как вы уже делаете, и все должно работать. где твоя проблема? вот образец с «экземпляр не имеет параметра» — i.sstatic.net/2fsVQlSM.png

Rand Random 07.06.2024 11:35

вот код решения: i.sstatic.net/IYniRGiW.png

Rand Random 07.06.2024 11:37

@RandRandom Хорошо, сейчас я чувствую, что мой мозг только что сломался, но я не понимаю, как это может сработать для меня. Возможно, я объясню дальше свою ситуацию. У меня есть таблица, содержащая такие столбцы, как идентификатор, имя, значение... и т. д. Затем есть столбец с именем parName. В таблице каждый экземпляр некоторых объектов записан примерно десятки-сотни раз. Каждое значение, такое как Имя или ID, одинаково, НО имя_пары всегда разное (такое же, как и значение). Поэтому я читаю таблицу в наблюдаемой коллекции типа Object (добавляю экземпляр только 1 раз) и читаю все различные параметры и значения для этого экземпляра в списке.

iksd 07.06.2024 12:00

@RandRandom Из этой картинки я понял, что вы создали разные классы для каждого типа объектов. Но у меня около 40 типов объектов и всего около 2000 различных параметров. Поэтому я боюсь, что если я правильно пойму ваше решение, оно мне не подойдет.

iksd 07.06.2024 12:03

@BionicCode Спасибо за вашу идею, у меня нет опыта работы с DataTable, поэтому я рассмотрю ее.

iksd 07.06.2024 12:03
But I have around 40 types of objects и? если 1 или 2, или 5, или 40? В чем проблема? and in total around 2000 different parameters тут какой-то вопрос и? если 1 или 2 или 5 или 2000? В чем проблема? - если вы хотите иметь сетку данных с 2000 столбцами/параметрами и сделать это, это не делает предоставленное решение недействительным. все, что вам нужно, это 1) список экземпляров (содержащий x разных типов, никого не волнует) 2) список параметров (для создания x количества столбцов) - это все, что я делаю, и я все еще верю, что это все вам нужно сделать. поэтому боюсь сказать, я до сих пор не понимаю вашей проблемы
Rand Random 07.06.2024 12:12

так что, возможно, мой мозг сломан, для дальнейшей помощи я бы попросил вас предоставить MRE stackoverflow.com/help/minimal-reproducible-example - чтобы я действительно мог увидеть и понять вашу проблему

Rand Random 07.06.2024 12:14

окей... у меня есть идея... дай мне секунду

Rand Random 07.06.2024 12:21

@iksd: Итак, свойство ItemsSourceDataGrid привязано к IEnumerable<Object>, и вы хотите добавить столбец для каждого элемента в Paramlist?

mm8 07.06.2024 12:39

@mm8 Да, именно так, но ParamList — это другой класс (а также я не могу обращаться к нему как к object.ParamList.Value...)

iksd 07.06.2024 13:08

@iksd: Тогда динамически добавляемый столбец должен быть привязан к Paramlist[i].Value, где i — индекс Parameters в коллекции Paramlist.

mm8 07.06.2024 13:11
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
14
79
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

После некоторых трудностей с пониманием вашей проблемы, я думаю, вы ищете Indexers

Здесь документация:

https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/indexers/

Расширьте свой Object класс с помощью indexer

public class Object
{
    public int ID { get; set; }
    public string Name { get; set; }

    public string this[string propertyName]
    {
        get
        {
            var parameterByName = Paramlist.FirstOrDefault(x => x.Name == propertyName);
            return parameterByName?.Value;
        }
        set
        {
            var parameterByName = Paramlist.FirstOrDefault(x => x.Name == propertyName);
            if (parameterByName == null)
                Paramlist.Add(new Parameters() { Name = propertyName, Value = value });
            else
                parameterByName.Value = value;
        }
    }

    public List<Parameters> Paramlist { get; set; } = new List<Parameters>();
}

Сделав это, вы можете создать Binding с синтаксисом индексатора [PropertyName].

например.

new System.Windows.Data.Binding("[MyProperty]")

Вот рабочий образец:

MainWindow.xaml

<Window x:Class = "WpfApp59.MainWindow"
        xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d = "http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable = "d"
        Title = "MainWindow"
        Height = "450"
        Width = "800"
        DataContext = "{Binding RelativeSource = {RelativeSource Self}}">
    <Grid>
        <DataGrid ItemsSource = "{Binding MyItems}"
                  Name = "MainDataGrid"
                  AutoGenerateColumns = "False">

            <!-- if you want to define the columns in xaml and not code behind -->
            <!--<DataGrid.Columns>
                <DataGridTextColumn Binding = "{Binding MySharedProperty}" />
                <DataGridTextColumn Binding = "{Binding MyFooProperty}" />
                <DataGridTextColumn Binding = "{Binding MyBarProperty}" />
                <DataGridTextColumn Binding = "{Binding [MyMagic1]}" />
                <DataGridTextColumn Binding = "{Binding [MyMagic2]}" />
                <DataGridTextColumn Binding = "{Binding [MyMagic3]}" />
            </DataGrid.Columns>-->
        </DataGrid>
    </Grid>
</Window>

MainWindow.xaml.cs

public partial class MainWindow : Window
{
    public IEnumerable<object> MyItems { get; set; } = new object[]
    {
        new Foo(){
            MySharedProperty = "I'm a Foo object.", 
            MyFooProperty = "Foo Property",
            ParamList = new List<Parameters>()
            {
                new Parameters(){Name = "MyMagic1", Value = "Foo Magic Value 1"},
                //new (){Name = "MyMagic2", Value = "Foo Magic Value 2"},
                new Parameters(){Name = "MyMagic3", Value = "Foo Magic Value 3"}
            }
        }, 
        new Bar(){
            MySharedProperty = "I'm a Bar object.", 
            MyBarProperty = "Bar Property",
            ParamList = new List<Parameters>()
            {
                new Parameters(){Name = "MyMagic1", Value = "Bar Magic Value 1"},
                new Parameters(){Name = "MyMagic2", Value = "Bar Magic Value 2"},
                //new (){Name = "MyMagic3", Value = "Foo Magic Value 3"}
            }
        }
    };

    public MainWindow()
    {
        InitializeComponent();

        string[] parameters = new string[] {
            "MySharedProperty", 
            "MyFooProperty", 
            "MyBarProperty",
            "[MyMagic1]",
            "[MyMagic2]",
            "[MyMagic3]"
        };

        foreach (var parameter in parameters)
        {
            MainDataGrid.Columns.Add(new DataGridTextColumn
            {
                Header = parameter,
                Binding = new System.Windows.Data.Binding(parameter),
            });
        }
    }
}

ОбщийОбъект.cs

public class SharedObject
{
    public string MySharedProperty { get; set; }

    public string this[string propertyName]
    {
        get
        {
            var parameterByName = ParamList.FirstOrDefault(x => x.Name == propertyName);
            return parameterByName?.Value;
        }
        set
        {
            var parameterByName = ParamList.FirstOrDefault(x => x.Name == propertyName);
            if (parameterByName == null)
                ParamList.Add(new Parameters() { Name = propertyName, Value = value });
            else
                parameterByName.Value = value;
        }
    }

    public List<Parameters> ParamList { get; set; }
}

Foo.cs

public class Foo : SharedObject
{
    public string MyFooProperty { get; set; }
}

Бар.cs

public class Bar : SharedObject
{
    public string MyBarProperty { get; set; }
}

Параметры.cs

public class Parameters
{
    public string Name { get; set; }
    public string Value { get; set; }
}

Результат выборки должен быть:

Спасибо, кажется, это может сработать. Просто пробую, однако у меня проблемы с версией C#, потому что кажется, что некоторые из них не поддерживаются в C# 7.3, который я использую, и до сих пор мне не удалось это изменить. Я разберусь с этим, попробую и дам вам знать. Спасибо

iksd 07.06.2024 13:10

Я попробовал ваше решение в тестовом приложении, и кажется, оно работает. Но мне не удалось использовать его в своем живом коде, потому что я использую коллекции Observable и пока не смог привести все к объектам. Как закончу, отмечу как решенное. Спасибо

iksd 07.06.2024 16:15

Дошёл до этого, проблема была между клавиатурой и стулом. Спасибо за терпение и решение.

iksd 09.06.2024 18:30

Чтобы упростить обработку данных и создание представлений, вам следует использовать DataTable и настроить DataGrid для автоматического создания столбцов.

Затем вы собираете информацию от пользователя (типы параметров и объекты) и создаете представление данных с помощью DataTable. Затем привяжите DataGrid.ItemsSource к этому DataTable:

ТипПараметра.cs

// Consider using an enum instead
public class ParameterType
{
  public string Name { get; }
}

Параметр.cs

public class Parameter
{  
  public ParameterType { get; }
  public string Name { get; set; }
  public string Value { get; set; }   
}

MainWindow.xaml

<Window x:Name = "VisualRoot">
  <DataGrid ItemsSource = "{Binding ElementName=VisualRoot, Path=TableData}" />
</Window>

MainWindow.xaml.cs

partial class MainWindow : Window
{
  public static readonly DependencyProperty TableDataProperty = DependencyProperty.Register(
    "TableData", 
    typeof(DataTable),
    typeof(MainWindow));

  public DataTable TableData 
  {
    get => (DataTable)GetValue(TableDataProperty);
    set => SetValue(TableDataProperty, value);
  }

  private void CreateTableDataFromSelection(
    IEnumerable<Object> selectObjects, 
    IEnumerable<ParameterType> selectedParamterTypes)
  {
    var table = new DataTable();
    var column = new DataColumn("ObjectName", typeof(string));
    table.Columns.Add(column);

    IEnumerable<string> selectedParameterTypeNames = selectedParameterTypes.Select(parameterType => parameterType.Name);
    foreach (string parameterTypeName in selectedParameterTypeNames)
    {
      column = new DataColumn(parameterTypeName, typeof(string));
      table.Columns.Add(column);
    }

    foreach (Object value in selectedObjects)
    {
      IEnumerable<string> selectedObjectParameters = value.ParamList.Where(parameter => selectedParameterTypeNames.Contains(parameter.ParameterType.Name));

      DataRow row = table.NewRow();

      // If the object does not contain a parameter for a column, 
      // then this column remains empty
      foreach (Parameter selectedObjectParameter in selectedObjectParameters)
      {
        row[selectedParameter.Name] = selectedParameter.Value;
      }
     
      table.Rows.Add(row);
    }

    this.TableData = table;
  }
}

Другие вопросы по теме