У меня есть два класса — Объект и Параметры.
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, и если экземпляр имеет параметр в списке, привяжите значение из списка к новому столбцу.
Мне не обязательно, чтобы это был Лист, это была первая идея, с которой я работал. Явно не очень хороший вариант.
Спасибо за любые предложения.
Не следует вручную создавать столбцы DataGrid. Просто включите автоматическое создание столбцов (оно включено по умолчанию), и когда пользователь завершит свой выбор, вы создадите DataTable из собранной информации. Затем привяжите DataGrid.ItemsSource к этому DataTable.
Да, вы правы, я имел в виду то, чего хочу. Виноват. Я не был уверен, как точно описать мою проблему. Пользователь выберет имя параметра в другом DataGrid, имя будет добавлено в заголовок, и значение этого параметра в списке каждого экземпляра будет привязано. Если у экземпляра нет параметра, он будет пустым.
но опять же, это уже должно работать, как описано, из коробки. просто создайте столбцы, как вы уже делаете, и все должно работать. где твоя проблема? вот образец с «экземпляр не имеет параметра» — i.sstatic.net/2fsVQlSM.png
вот код решения: i.sstatic.net/IYniRGiW.png
@RandRandom Хорошо, сейчас я чувствую, что мой мозг только что сломался, но я не понимаю, как это может сработать для меня. Возможно, я объясню дальше свою ситуацию. У меня есть таблица, содержащая такие столбцы, как идентификатор, имя, значение... и т. д. Затем есть столбец с именем parName. В таблице каждый экземпляр некоторых объектов записан примерно десятки-сотни раз. Каждое значение, такое как Имя или ID, одинаково, НО имя_пары всегда разное (такое же, как и значение). Поэтому я читаю таблицу в наблюдаемой коллекции типа Object (добавляю экземпляр только 1 раз) и читаю все различные параметры и значения для этого экземпляра в списке.
@RandRandom Из этой картинки я понял, что вы создали разные классы для каждого типа объектов. Но у меня около 40 типов объектов и всего около 2000 различных параметров. Поэтому я боюсь, что если я правильно пойму ваше решение, оно мне не подойдет.
@BionicCode Спасибо за вашу идею, у меня нет опыта работы с DataTable, поэтому я рассмотрю ее.
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 количества столбцов) - это все, что я делаю, и я все еще верю, что это все вам нужно сделать. поэтому боюсь сказать, я до сих пор не понимаю вашей проблемы
так что, возможно, мой мозг сломан, для дальнейшей помощи я бы попросил вас предоставить MRE stackoverflow.com/help/minimal-reproducible-example - чтобы я действительно мог увидеть и понять вашу проблему
окей... у меня есть идея... дай мне секунду
@iksd: Итак, свойство ItemsSource
DataGrid
привязано к IEnumerable<Object>
, и вы хотите добавить столбец для каждого элемента в Paramlist
?
@mm8 Да, именно так, но ParamList — это другой класс (а также я не могу обращаться к нему как к object.ParamList.Value...)
@iksd: Тогда динамически добавляемый столбец должен быть привязан к Paramlist[i].Value
, где i
— индекс Parameters
в коллекции Paramlist
.
После некоторых трудностей с пониманием вашей проблемы, я думаю, вы ищете 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, который я использую, и до сих пор мне не удалось это изменить. Я разберусь с этим, попробую и дам вам знать. Спасибо
Я попробовал ваше решение в тестовом приложении, и кажется, оно работает. Но мне не удалось использовать его в своем живом коде, потому что я использую коллекции Observable и пока не смог привести все к объектам. Как закончу, отмечу как решенное. Спасибо
Дошёл до этого, проблема была между клавиатурой и стулом. Спасибо за терпение и решение.
Чтобы упростить обработку данных и создание представлений, вам следует использовать 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;
}
}
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