【问题标题】:How to bind list of object to DataGrid in WPF如何在 WPF 中将对象列表绑定到 DataGrid
【发布时间】:2019-06-09 21:42:29
【问题描述】:

这是我的代码:

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>

我想像这样绑定数据对象:

【问题讨论】:

  • 对于初学者,您的主列表绑定有问题,您正在创建一个名为 Words 的局部变量并将其设置为 DataContext,但随后您的 ItemsControl 正在尝试绑定到名为Words 的属性 要么将Words 设为MainWindow 的属性并将其设置为您的DataContext,要么将绑定更改为Binding="{Binding}"。除此之外,除了最终将同义词和首字母缩略词的折叠联合呈现为标签的单列列表或其他内容之外,您真的不清楚您要做什么?
  • @MarkFeldman 我已经更新了代码。请重新审核。

标签: c# wpf data-binding datagrid


【解决方案1】:

下面的答案解决了这个问题,但我会建议另一种方法。如上所述,首先您需要在代码隐藏中添加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>

结果,我们会得到

【讨论】:

  • 我没有忘记 DataContext。我已经把它放在 Xaml 中了。
  • 很好的解决方案,但需要注意的是,如果没有足够的水平空间来容纳标签,标签将无法换行。 OP 当然没有指定所需的行为,所以它可能已经足够好了。
【解决方案2】:

我不太明白你的意思,但是如果你想将属性绑定到列,你可以这样做:

<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>

但是抱歉,我不知道如果该列要传递字符串集合(如您的示例)会发生什么。如果我误解了你,我很抱歉

【讨论】:

    【解决方案3】:

    您更新后的代码仍然存在绑定错误,您需要将其添加到构造函数的底部:

        this.DataContext = this;
    

    您发布的代码的主要问题是您试图将 Synonym 和 Acronym 数组合并到一个列表中,这确实是在将数据传递到视图层之前应该完成的事情。我将在事后使用转换器来完成此任务,但请记住,您可能应该稍后修复它。

    无论如何,您要实现的目标相对简单。使用 DataGridTemplateColumn 声明自定义列类型并将其内容设置为 ItemsControl(其默认面板是垂直 StackPanel)。堆栈面板的每个元素都是一个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();
        }
    }
    

    更新:我刚刚注意到颜色不同的要求。如果您绝对必须这样做,您也可以使用转换器来执行此操作,但这是一个非常混乱的解决方案。实现这一点的正确方法是使用中间视图模型并将同义词和首字母缩略词合并到一个列表中。

    【讨论】:

    • 我没有忘记 DataContext。我已将其放入 Xaml:DataContext="{Binding RelativeSource={RelativeSource Self}}"
    • 我没有接受你的回答,不是因为它不好,因为在目前的情况下做你更好的解决方案已经超出了我目前的能力。我害怕完美主义。因此,我希望您继续回答我未来的问题。感谢您的宝贵时间。
    猜你喜欢
    • 2012-05-25
    • 1970-01-01
    • 1970-01-01
    • 2010-12-16
    • 2011-03-01
    • 2015-01-16
    • 1970-01-01
    • 2021-03-18
    • 2015-03-27
    相关资源
    最近更新 更多