Как привязать список объектов к DataGrid в WPF - PullRequest
0 голосов
/ 10 июня 2019

Это мой код:

public partial class MainWindow : INotifyPropertyChanged
{
    private List<Word> _words;

    public List<Word> Words
    {
        get => _words;
        set
        {
            _words = value;
            OnPropertyChanged("Words");
        }
    }

    public MainWindow()
    {
        InitializeComponent();

        MeaningGroup group1 = new MeaningGroup()
        {
            Synonyms = new List<string> {"synonym1", "synonym2", "synonym3"},
            Acronyms = new List<string> {"acronym1", "acronym2"}
        };

        MeaningGroup group2 = new MeaningGroup()
        {
            Synonyms = new List<string> { "synonym1"},
            Acronyms = new List<string> { "acronym1", "acronym2", "acronym3" }
        };

        MeaningGroup group3 = new MeaningGroup()
        {
            Synonyms = new List<string> { "synonym1", "synonym2" },
            Acronyms = new List<string> { }
        };

        MeaningGroup group4 = new MeaningGroup()
        {
            Synonyms = new List<string> { "synonym1" },
            Acronyms = new List<string> { "acronym1", "acronym2", "acronym3","acronym4" }
        };

        Word word1 = new Word() {Name = "word1",MeaningGroups = new List<MeaningGroup>() {group1, group2}};
        Word word2 = new Word() { Name = "word2", MeaningGroups = new List<MeaningGroup>() { group3, group4 } };
        Word word3 = new Word() { Name = "word3", MeaningGroups = new List<MeaningGroup>() { group1, group2,group4 } };
        Word word4 = new Word() { Name = "word4", MeaningGroups = new List<MeaningGroup>() { group3 } };

        Words = new List<Word> {word1, word2, word3, word4};

    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

public class Word
{
    public String Name { get; set; }
    public List<MeaningGroup> MeaningGroups { get; set; }

}

public class MeaningGroup
{
    public List<string> Synonyms { get; set; }
    public List<string> Acronyms { get; set; }
}

А это код MainWindow.xaml:

<Window x:Class="WpfApp4.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"
    xmlns:local="clr-namespace:WpfApp4"
    DataContext="{Binding RelativeSource={RelativeSource Self}}"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800">
<Grid>
    <DataGrid ItemsSource="{Binding Words}" AutoGenerateColumns="False">
        <DataGrid.Columns>
            <DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
            <DataGridTextColumn Header="Synonym and acronyms">
               <!-- How binding? -->
            </DataGridTextColumn>
        </DataGrid.Columns>
    </DataGrid>
</Grid>

Я хочу связать объекты данных следующим образом: enter image description here

Ответы [ 3 ]

1 голос
/ 11 июня 2019

В вашем обновленном коде все еще есть ошибка привязки, вам нужно добавить это в конец вашего конструктора:

    this.DataContext = this;

Основная проблема с кодом, который вы разместили, заключается в том, что вы пытаетесь объединить массивы Synonym и Acronym в один список, что на самом деле нужно сделать перед передачей данных в слой представления. Я буду использовать конвертер, чтобы выполнить эту задачу позже, но имейте в виду, что это то, что вы, вероятно, должны исправить позже.

В любом случае то, что вы пытаетесь достичь, относительно просто. Используйте DataGridTemplateColumn, чтобы объявить пользовательский тип столбца и установить для его содержимого значение ItemsControl (его панель по умолчанию - вертикальная панель стека). Каждый элемент панели стека - MeaningGroup, поэтому просто используйте другой ItemsControl для рендеринга ваших тегов и установите вместо него ItemsPanelTemplate как горизонтальный макет (я использовал WrapPanel). Соберите все это вместе, и вы получите это:

<Window.Resources>

    <behaviors:ListUnionConverter x:Key="ListUnionConverter" />

    <DataTemplate x:Key="TagTemplate">
        <Border BorderBrush="Black" BorderThickness="1" CornerRadius="5" Margin="5" Padding="5">
            <TextBlock Text="{Binding}" />
        </Border>
    </DataTemplate>

    <DataTemplate x:Key="TagListTemplate">
        <ItemsControl ItemTemplate="{StaticResource TagTemplate}">
            <ItemsControl.ItemsSource>
                <MultiBinding Converter="{StaticResource ListUnionConverter}">
                    <Binding Path="Synonyms" />
                    <Binding Path="Acronyms" />
                </MultiBinding>
            </ItemsControl.ItemsSource>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
    </DataTemplate>

    <DataTemplate x:Key="CellTemplate">
        <ItemsControl ItemsSource="{Binding MeaningGroups}" ItemTemplate="{StaticResource TagListTemplate}" />
    </DataTemplate>

</Window.Resources>

<Grid>
    <DataGrid ItemsSource="{Binding Words}" AutoGenerateColumns="False">
        <DataGrid.Columns>
            <DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
            <DataGridTemplateColumn Header="Synonym and acronyms" IsReadOnly="True" CellTemplate="{StaticResource CellTemplate}" />
        </DataGrid.Columns>
    </DataGrid>
</Grid>

Вам также понадобится код конвертера, который я использую для объединения синонимов и сокращений в единый список. Опять же, вы должны либо заменить это чем-то более надежным, или, желательно, исправить свои структуры данных, чтобы лучше соответствовать требованиям представления:

Результат:

public class ListUnionConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        return (values[0] as IEnumerable<string>).Concat(values[1] as IEnumerable<string>).ToArray();
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

enter image description here

ОБНОВЛЕНИЕ: Я только что заметил, что цвета должны быть разными. Вы также можете использовать конвертер, чтобы сделать это, если вам абсолютно необходимо, но это действительно грязное решение. Правильный способ реализовать это - использовать модель с промежуточным представлением и объединить как синонимы, так и сокращения в один список.

1 голос
/ 11 июня 2019

ответ ниже решает эту проблему, но я предложу другой путь. Как сказано выше, сначала вам нужно добавить DataContext = this в ваш код. Затем просто измените разметку xaml следующим образом:

     <Grid>
        <DataGrid ItemsSource="{Binding Words}" AutoGenerateColumns="False">
            <DataGrid.Columns>
                <DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
                <DataGridTemplateColumn Header="Synonym and acronyms">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <StackPanel>
                                <ItemsControl ItemsSource="{Binding MeaningGroups}">
                                    <ItemsControl.ItemTemplate>
                                        <DataTemplate>
                                            <StackPanel Orientation="Horizontal">
                                                <ItemsControl ItemsSource="{Binding Synonyms}">
                                                    <ItemsControl.ItemsPanel>
                                                        <ItemsPanelTemplate>
                                                            <WrapPanel/>
                                                        </ItemsPanelTemplate>
                                                    </ItemsControl.ItemsPanel>
                                                    <ItemsControl.ItemTemplate>
                                                        <DataTemplate>
                                                            <Border BorderThickness="1" BorderBrush="Black" CornerRadius="5" Margin="5 5 0 0">
                                                            <TextBlock Margin="3" Text="{Binding}"/>
                                                            </Border>
                                                        </DataTemplate>
                                                    </ItemsControl.ItemTemplate>
                                                </ItemsControl>
                                                <ItemsControl ItemsSource="{Binding Acronyms}">
                                                    <ItemsControl.ItemsPanel>
                                                        <ItemsPanelTemplate>
                                                            <WrapPanel/>
                                                        </ItemsPanelTemplate>
                                                    </ItemsControl.ItemsPanel>
                                                    <ItemsControl.ItemTemplate>
                                                        <DataTemplate>
                                                            <Border BorderThickness="1" BorderBrush="Black" CornerRadius="5" Background="Red" Margin="5 5 0 0">
                                                            <TextBlock Margin="3" Text="{Binding}"/>
                                                            </Border>
                                                        </DataTemplate>
                                                    </ItemsControl.ItemTemplate>
                                                </ItemsControl>
                                            </StackPanel>
                                        </DataTemplate>
                                    </ItemsControl.ItemTemplate>
                                </ItemsControl>
                            </StackPanel>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>

В результате получим enter image description here

1 голос
/ 10 июня 2019

Я не совсем понимаю, что вы имеете в виду, но если вы хотите связать свойства со столбцами, вы можете сделать это:

<DataGrid ItemsSource="{Binding Words}">
   <DataGrid.Columns>
     <DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
   </DataGrid.Columns>
</DataGrid>

Теперь «Имя» каждого элемента будет добавлено в этот столбец. Но если вы хотите добавить более одного свойства в столбец, вам нужно будет добавить DataGridTemplateColumn:

<DataGrid ItemsSource="{Binding Words}">       
   <DataGrid.Columns>
      <DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
      <DataGridTemplateColumn Header="Synonyms and Acronyms">
         <DataGridTemplateColumn.CellTemplate>
            <DataTemplate>
               <StackPanel>
                     //There will be all logic. For example
                     //<TextBlock Text="{Binding Synonyms}"/>
                     //<TextBlock Text="{Binding Acronyms}"/>
               </StackPanel>
            </DataTemplate>
         </DataGridTemplateColumn.CellTemplate>
      </DataGridTemplateColumn>
   </DataGrid.Columns>
</DataGrid>

Но извините, я не знаю, что произойдет, если столбец пройдет набор строк (как в вашем примере). И извините, если я вас неправильно понял

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...