【问题标题】:Twoway binding datagrid wpf xaml双向绑定数据网格 wpf xaml
【发布时间】:2021-01-27 12:35:32
【问题描述】:

我想将 CLR 属性绑定到 datagrid Row 如何在 datagrid 中绑定行绑定(双向模式) 代码隐藏:

public partial class TechnicalPropertiesU_Var : Window
    {
        public TechnicalPropertiesU_Var()
        {
            InitializeComponent();
            List<Myclass> myclassList = new List<Myclass>();
            myclassList.Add(new Myclass() { IA = 0, IB = 0, IC = 0, ID = 0, IE = 0, IF = 0, IF = 0 });
            MyGrid.ItemsSource = myclassList;
        }
    }

MyWindow Xaml:

<Grid>
        <DataGrid x:Name="MyGrid" AutoGenerateColumns="False">
            <DataGrid.Columns>
                <DataGridTextColumn Header="Argument Name"></DataGridTextColumn>
                <DataGridTextColumn Header="Argument Value"></DataGridTextColumn>
            </DataGrid.Columns>
            
        </DataGrid>
    </Grid>

模型类

public class Myclass
{
private int iA;
private int iB;
private int iC;
private int iD;
private int iE;
private int iF;
private int iG;

public int IA{get=>iA; set=>iA =value;}
public int IB{get=>iB; set=> iB =value;}
public int IC{get=>iC; set=> iC =value;}
public int ID{get=>iD; set=> iD =value;}
public int IE{get=>iE; set=> iE =value;}
public int IF{get=>iF; set=> iF =value;}
public int IG{get=>iG; set=> iG =value;}
}

如何获取数据网格字段中的特定列。请提供任何样品

【问题讨论】:

  • 它们都是 int 吗?传递 MyClass 和 propertyinfo 定义要绑定到行视图模型的属性。这可以计算出属性的名称,公开它自己的 int 属性以进行绑定。该属性的 getter 和 setter 使用 propertyinfo 来获取和设置属性。使用反射获取 MyClass 中的 propertyinfo 列表。
  • 这里不需要反射或任何属性信息。你的数据结构也是错误的。如果您尝试获得的结果是一个包含两列的表格(请参见您的图片),那么我猜您显然混淆了行和列。您的数据类必须具有两个属性:ArgumentNameArgumentValue。您必须知道每个项目(或MyClass 的每个实例将显示为一行。现在,为每一行创建一个MyClass 的实例并将其添加到源集合中。将DataGridTextColumn.Binding 属性绑定到MyClass 模型的列相关属性。
  • 对于双向绑定,MyClass 必须实现 INotifyPropertyChanged
  • @BionicCode 请提供任何示例代码
  • 向您展示究竟是什么?如何定义具有两个属性的类?

标签: c# wpf data-binding datagrid two-way-binding


【解决方案1】:

如果您尝试获得的结果是一个包含两列的表格(请参见您的图片),那么我猜您显然混淆了行和列。您的数据类必须有两个属性:ArgumentNameArgumentValue。您必须知道每个项目(或MyClass 的每个实例)都将显示为一行。现在,为每一行创建一个 MyClass 的实例并将其添加到源集合中。将DataGridTextColumn.Binding 属性绑定到MyClass 模型的列相关属性。

以下示例将显示您图片中的表格:

MyTableClass.cs

public class MyTableClass : INotifyPropertyChanged
{
    public MyTableClass(int argumentName, int argumentValue)
    {
        this.ArgumentName = argumentName;
        this.ArgumentValue = argumentValue;
    }

    private int argumentName;   
    private int argumentValue;

    public int ArgumentName{get=>argumentName; set{argumentName=value; NotifyPropertyChanged();}}
    public int ArgumentValue{get=>argumentValue; set{argumentValue=value; NotifyPropertyChanged();}}

    public event PropertyChangedEventHandler PropertyChanged;

    // This method is called by the Set accessor of each property.
    // The CallerMemberName attribute that is applied to the optional propertyName
    // parameter causes the property name of the caller to be substituted as an argument.
    private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

TechnicalPropertiesU_Var.xaml.cs

public partial class TechnicalPropertiesU_Var : Window
{
    public TechnicalPropertiesU_Var()
    {
        InitializeComponent();
        List<MyTableClass> myTableClasses = new List<MyTableClass>();

        myTableClasses.Add(new MyTableClass() { ArgumentName = "IA", ArgumentValue = 5 });
        myTableClasses.Add(new MyTableClass() { ArgumentName = "IB", ArgumentValue = 3 });
        myTableClasses.Add(new MyTableClass() { ArgumentName = "IC", ArgumentValue = 2 });
        myTableClasses.Add(new MyTableClass() { ArgumentName = "ID", ArgumentValue = 0 });
          
        MyGrid.ItemsSource = myTableClasses;
    }
}

MyWindow.xaml

<Grid>
    <DataGrid x:Name="MyGrid" AutoGenerateColumns="False">
        <DataGrid.Columns>
            <DataGridTextColumn Header="Argument Name" Binding="{Binding ArgumentName}"></DataGridTextColumn>
            <DataGridTextColumn Header="Argument Value" Binding="{Binding ArgumentValue}"></DataGridTextColumn>
        </DataGrid.Columns>
        
    </DataGrid>
</Grid>

解决您的评论: “我不想绑定参数名称和参数值属性。我想绑定我自己的 CLR 属性。因为我有 250 个变量。”

我不必了解您的意图就可以告诉您这是一个坏主意。一个类不应该有 250 个属性。您的图像清楚地表明,并非所有 250 个属性都有关系,但只有两个有关系。

在您的情况下,数据包含一个字符串值,例如 "IA" 和一个数值,例如5。您应该更改您的班级以反映这一点。
您应该知道,在常见的关系数据库设计中,数据记录由一行表示。每一行是一个对象,这一行的每一列是该对象的一个​​属性。 DataGrid´ is designed with the same intention: each row is an item of the DataGrid.ItemsSource`。每个项目都是一个单独的实例。通常从左到右阅读任何表格,例如产品价格表,其中一行的每一列都应与单个数据项相关,例如产品。

我强烈建议您放弃最初的意图并更改数据类以反映数据的关系,例如示例的MyTableClass 反映了具有两个属性(列)的数据对象(行)的关系。

【讨论】:

  • 我不想绑定参数名和参数值属性。我想绑定我自己的 CLR 属性。因为我有 250 个变量。所以我想按行绑定,而不是按列绑定。如何在datagrid wpf xaml中使用第一行作为标题
  • “我想绑定我自己的CLR属性。”请解释一下:你自己的CLR属性是什么?总的来说我不明白。您想从单个项目创建多行吗?没有一些努力是不可能的。你可以考虑改变你的数据结构。在表中,一行代表一个数据集。你应该清理你的数据结构。 DataGrid 反映了这种常见做法:ItemsSource 中的每个项目都呈现为一行。您最好的解决方案是将您的项目转换为适当的数据结构(参见我更新的示例),或者更好地将您的 Myclass 替换为上述版本。
  • 我认为你举了很好的例子。 CoderGreat 显然是新的,并没有完全掌握你的提议。我无法想象 250 个变量与一个列表,即使它是基于 250 个事物的枚举......至少你可以分别浏览列表。也许您需要将第一个参数作为字符串来显示它们的“iA”、“iB”、“iC”标签。
  • @DRapp 谢谢。我觉得你说的很对。我也觉得 OP 没有理解真正的问题:错误的类设计。具有 250 多个变量/属性的类确实非常“不常见”,并且肯定是糟糕设计的指标。
  • 也许在您的代码中进行调整以显示其 IA - IF 设计的示例枚举,执行循环以填充并将该列表设置为绑定可能会睁开眼睛。然后通知他们您对答案的调整。他们绝对不会在这个级别获得设计,他们将远远落后于前进。也喜欢你在笔记中的努力和清晰。
【解决方案2】:

您尝试做的事情没有多大意义,尤其是从维护的角度来看。您显然有 250 个要列出的变量。如果这个列表被缩短或扩展,你现在有 2389 个变量(夸大了,但只是为了重点)。你会为了保持头发而拔掉头发。

相反,您应该更多地了解类结构、更多可能的数据库,并尝试找到一个共同的模式。您拥有的每个“事物”都是基于整数的项目。但是每一个“东西”都有对应的用途,或者描述比如你的IA、IB、IC等等。

我建议您考虑以下内容。我已经制作了一个带有清单的枚举器。下面的示例,但我只有 IA-IG 的项目。出于示例目的,我还添加了与其中两个相关的描述。

public enum my250Things
{
    IA,
    IB,
    [Description("this is item C")]
    IC,
    ID,
    IE,
    [Description("testing F description")]
    IF,
    IG
}

所以,现在在您的代码中,如果我有一个基于“my250Things”类型的变量,我可以通过它的枚举来引用它以了解它代表什么。

回到您的数据网格。由于要呈现的所有记录各占一行,并显示相应的变量名称上下文(IA、IB 等)及其包含的值,因此我创建了一个类。这个类实际上有 3 个属性。一个显示值,一个显示 int,以及它创建的原始枚举值。下面。

public class YourCommonThings : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private string _yourShowValue;
    public string YourShowValue
    {
        get { return _yourShowValue; }
        set { _yourShowValue = value;
            NotifyPropertyChanged(); }
    }

    private int _yourIntValue;
    public int YourIntValue
    {
        get { return _yourIntValue; }
        set
        {
            _yourIntValue = value;
            NotifyPropertyChanged();
        }
    }

    private my250Things _theOriginalEnum;
    public my250Things TheOriginalEnum
    {
        get { return _theOriginalEnum; }
        set
        {
            _theOriginalEnum = value;
            NotifyPropertyChanged();
        }
    }

    // This method is called by the Set accessor of each property.
    // The CallerMemberName attribute that is applied to the optional propertyName
    // parameter causes the property name of the caller to be substituted as an argument.
    private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

现在,我想填充我想呈现给用户的内容列表。由于枚举让我知道我正在尝试跟踪的“事物”,因此我可以使用它来填充列表,以及适用的描述。如果没有,它默认使用枚举本身的字符串表示。我为演示创建了一个简单的窗口“Stack1”,创建了一个用于将数据列表绑定到并基于枚举自动填充的公共属性。

您需要在窗口的 .cs 文件顶部使用这些“使用”引用

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Windows;

这是窗口代码隐藏中的代码

public partial class Stack1 : Window
{

    // first, making a public property as a list of the things I want to present
    public List<YourCommonThings> BindToThisListOfThings { get; set; }

    // the name of my window is called "Stack1" and this is the constructor of the window
    public Stack1()
    {

        // start by declaring a new list of things based on the class
        BindToThisListOfThings = new List<YourCommonThings>();

        // Now, we can dynamically create a list of all the "things" you are trying to present
        // into your data grid.  In this case, I am going through an enumerator list, but could
        // also be done if the list of things was stored in a database (for example) and you 
        // queried from that, but that is a different question / option.
        foreach(my250Things oneThing in  typeof( my250Things ).GetEnumValues() )
        {
            // creating one instance of whatever your enum underlying thing is and
            // default setting the properties within the class instance for the parts
            // that are within the { }
            // the ".ToString()" portion will get your "IA","IB", ...
            // I can also add in to store the original enum value while I'm at it,
            // even though not showing to the end user in the screen.
            var justOne = new YourCommonThings
                    {   YourShowValue = oneThing.ToString(),
                        YourIntValue = 0,
                        TheOriginalEnum = oneThing };

            // and you can optionally just for sake of example purposes associate a description
            // to an enum value and pull THAT instead...
            string description = GetEnumDescription(oneThing);
            if (!string.IsNullOrWhiteSpace(description))
                justOne.YourShowValue = description;

            // Now, add to your list
            BindToThisListOfThings.Add(justOne);
        }

        // Now, you have your list prepared and ready to go for binding to
        DataContext = this;

        // and finish initializing the rest of the form.
        InitializeComponent();
    }


    // this static method is used to get the "description" component
    // out of the enumeration declaration IF such a description is declared.
    public static string GetEnumDescription(Enum value)
    {
        FieldInfo fi = value.GetType().GetField(value.ToString());

        DescriptionAttribute[] attributes = fi.GetCustomAttributes(typeof(DescriptionAttribute), false) as DescriptionAttribute[];

        if (attributes != null && attributes.Any())
            return attributes.First().Description;

        return value.ToString();
    }
}

最后,在表单的 xaml 中,我将数据网格绑定到从枚举中准备的事物列表。

    <DataGrid AutoGenerateColumns="False" VerticalContentAlignment="Stretch" 
        ItemsSource="{Binding BindToThisListOfThings, NotifyOnSourceUpdated=True}">

        <DataGrid.Columns>
            <DataGridTextColumn Header="Argument Name" Binding="{Binding YourShowValue}" Width="125" />

            <DataGridTextColumn Header="Argument Value" Binding="{Binding YourIntValue}" Width="125" />
        </DataGrid.Columns>
    </DataGrid>

现在,在表单/窗口中,或者在任何您定义了 MVVM 模式的地方(此时尚存疑问),但无论如何,无论您打算保存数据,您都可以循环浏览事物列表,并获取其各自的东西和描述要保存在您计划的任何地方,但您的问题中没有披露以进一步应用。

public void SaveData()
{
    foreach( var oneThing in BindToThisListOfThings )
    {
        // Here, you can refer to 
        // oneThing.YourShowValue
        // oneThing.YourIntValue
        // oneThing.TheOriginalEnum
    }
}

祝你好运。

【讨论】:

    猜你喜欢
    • 2016-03-01
    • 2011-08-15
    • 2011-02-23
    • 2011-10-29
    • 2018-07-27
    • 1970-01-01
    • 1970-01-01
    • 2015-11-23
    • 2011-09-28
    相关资源
    最近更新 更多