WPF动态数据网格绑定方法

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WPF动态数据网格绑定方法相关的知识,希望对你有一定的参考价值。

我没有在c#编程和使用WPF的长期经验,如果我的“词汇”不够技术或适当,我道歉。

我需要在我的应用程序中显示一个表,其中行是类的实例,列是定义我的对象的properties/attributes

public class myObject
{
    //constructor
    public myObject() { }

    //properties
    public string Topic { get;  set}

    public string Description { get; set; }

    public bool Display { get; set; }

    public List<myAttribute> AttributeList { get; set; }


    //method
    public string GetAttributeValueByName(string sAttributeName)
    {
        foreach (myAttribute A in this.AttributeList)
        {
            if (A.Name == sAttributeName) { return A.Value.ToString(); }
        }

        return string.Empty;
    }
}

我有一个myObjects列表,我绑定为datagrid.ItemSource,我已经能够正确绑定定义的属性,如主题/描述,并在列中显示它们。 myObject.Display也是单向绑定的,让我可以在数据网格中可视化myObject(作为一种来自其他地方的过滤器)。

现在,正如您在上面的代码中所注意到的,我有一个属性,它返回一个属性列表。这是因为myObject具有用户可以设置的未定义数量的自由属性。我为这个属性设置了一个合适的类(只有很少的简单属性,如.Name.Value.Color.Rank)。我有方法让我可以将值作为字符串检索,例如左右。

我的主要问题是我无法在数据网格中正确地显示它们,因为通常,绑定适用于属性而不适用于方法。我做了一些关于绑定方法的研究,我尝试过任何成功的结果。

简述:

  • 我需要动态列(一些固定像Topic +每个属性一个)
  • 所有myObjects在AttributeList中具有相同数量的元素
  • 我需要在datagrid的正确单元格中绑定(至少单向)属性的值。

UI类:

namespace myApp.UserInterface
{
    public partial class control : UserControl
    {
        control()
        {
            InitializeComponent();
            InitializeData();
        }

        private InitializeData()
        {
            datagrid.ItemsSource = Core.myObjectPoolList;
        }
    }
}

Ui A ML

<UserControl x:Class="myApp.UserInterface.control"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:myApp.UserInterface;assembly="
         xmlns:core="clr-namespace:myObject.Core;assembly="
         xmlns:system="clr-namespace:System;assembly=mscorlib"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.Resources>
        <ObjectDataProvider x:Key="basic_attribute_value"
                            ObjectType="{x:Type core:D2P_Issue}"
                            MethodName="GetAttributeValueByName"
                            IsAsynchronous="True">
            <ObjectDataProvider.MethodParameters>
                <system:String>status</system:String>
            </ObjectDataProvider.MethodParameters>
        </ObjectDataProvider>
    </UserControl.Resources>

    <Grid>
        <DataGrid Name="issuepool_datagrid" AutoGenerateColumns="False">
            <DataGrid.Columns>
                <DataGridTextColumn Header="Topic" Binding="{Binding Topic}" Width="150" TextBlock.TextAlignment="Center"/>
                <DataGridTextColumn Header="Description" Binding="{Binding Description}" Width="150" TextBlock.TextAlignment="Center"/>
                <DataGridTextColumn x:Name="status_column" Header="status" Binding="{Binding Source={StaticResource basic_attribute_value}, Mode=OneWay}" Width="150" TextBlock.TextAlignment="Center"/>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</UserControl>

在这个例子中,我试图留下动态生成列,只需对其进行硬编码以查找“status”属性名称。

如果有什么不清楚,请随时询问。

答案

您可以在后面的代码中动态创建列。虽然很简单。如果要在同一个项目中多次重复使用,可以将其重写为附加行为,并使用另一个附加属性将动态列集合存储在网格本身上。

由您决定何时更改属性集,并相应地更新列。我已经将动态列存储在字典中,以简化智能更新它们的业务,如果您碰巧知道只添加或删除了一个属性。

如果你是,我也会用myObject.AttributeList取代Dictionary<String, myAttribute>。绑定路径必须稍微改变。

这允许用户编辑属性值。如果您愿意,可以在动态列上设置IsReadOnly = true

public partial class control : UserControl
{
    control()
    {
        InitializeComponent();
        InitializeData();
    }

    private InitializeData()
    {
        datagrid.ItemsSource = Core.myObjectPoolList;

        var firstItem = Core.myObjectPoolList.First();

        if (firstItem != null) {
            UpdateDataGridColumns(dataGrid, firstItem.AttributeList, _dynamicColumns);
        }
    }

    private Dictionary<string, DataGridColumn> _dynamicColumns = new Dictionary<string, DataGridColumn>();
    protected void UpdateDataGridColumns(DataGrid dg, List<myAttribute> attributesSample, Dictionary<string, DataGridColumn> existingDynamicColumns)
    {
        foreach (var col in existingDynamicColumns.Values)
        {
            dg.Columns.Remove(col);
        }
        existingDynamicColumns.Clear();

        int idx = 0;
        foreach (var attr in attributesSample)
        {
            var column = new DataGridTextColumn() {
                Header = attr.Name,
                Binding = new Binding($"AttributeList[{idx}].Value")
            };

            dg.Columns.Add(column);

            existingDynamicColumns.Add(attr.Name, column);

            ++idx;
        }
    }
}

替代方法:将所有属性放在一列中。这不使用上面的代码。这只是这种形式的读取,但可以适应让用户编辑网格中的值。

将此属性添加到myObject

    public IEnumerable<String> AttributeNames 
        => AttributeList.Select(a => a.Name);

这对于XAML中的DataGrid.Columns

<DataGridTemplateColumn
    Width="*"
    >
    <DataGridTemplateColumn.HeaderStyle>
        <Style>
            <Setter Property="ContentControl.HorizontalContentAlignment" Value="Stretch" />
        </Style>
    </DataGridTemplateColumn.HeaderStyle>
    <DataGridTemplateColumn.HeaderTemplate>
        <DataTemplate>
            <ItemsControl
                ItemsSource="{Binding DataContext.Items[0].AttributeNames, RelativeSource={RelativeSource AncestorType=DataGrid}}"
                >
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <UniformGrid />
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Label 
                            Content="{Binding}" 
                            />
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </DataTemplate>
    </DataGridTemplateColumn.HeaderTemplate>
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <ItemsControl
                ItemsSource="{Binding AttributeList}"
                >
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <UniformGrid />
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Label Content="{Binding Value}" />
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
另一答案

为什么不使用DataGridComboBoxColumn

像这样的东西:

<DataGridComboBoxColumnHeader="Attributes" Binding="{Binding AttributeList}" width="150" TextBlock.TextAlignment="Center"/>

以上是关于WPF动态数据网格绑定方法的主要内容,如果未能解决你的问题,请参考以下文章

是否可以在 mvvm 模式中获取 wpf 数据网格上的动态列?

WPF 数据网格绑定工具提示在表格内容绑定刷新时闪烁

在 WPF 中动态地将元素定位到网格中

在 WPF 中,如何将数据网格列绑定到数据表的特定列?

将 Wpf 的数据网格绑定到数据库

带有自定义列的 WPF 数据网格绑定