将任意类绑定到wpf数据网格 [英] Binding arbitary class to a wpf datagrid

查看:68
本文介绍了将任意类绑定到wpf数据网格的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Hello All,



我正在尝试将复杂类绑定到wpf datagrid。我的类包含一些属于类本身的属性。我可以使用许多类似的类,其复杂程度各不相同。所以我正在寻找一个通用的解决方案。



我创建了一个简单的例子来说明我的问题(下面的代码)

下面的链接显示我目前在数据网格中看到的内容以及我想看到的内容



输出



如何完成所需的输出显示?



 使用系统; 
使用 System.Collections.Generic;
使用 System.Linq;
使用 System.Text;
使用 System.Windows;
使用 System.Windows.Controls;
使用 System.Windows.Data;
使用 System.Windows.Documents;
使用 System.Windows.Input;
使用 System.Windows.Media;
使用 System.Windows.Media.Imaging;
使用 System.Windows.Navigation;
使用 System.Windows.Shapes;
使用 System.Collections.ObjectModel;

命名空间 MainApp
{
/// < 摘要 >
/// MainWindow.xaml $ b的交互逻辑$ b /// < < span class =code-summarycomment> / summary >
public partial class MainWindow:Window
{
public MainWindow()
{
InitializeComponent();

dgMain.ItemsSource = SetUP();
}

public Collection< toClass> SetUP()
{
Collection< toClass> inpData = 集合< toClass> { new toClass {Inp = new inputsClass {inp1 = 100 ,inp2 = 200 },Outp = new outputsClass {otp1 = 30 ,otp2 = 25 }}};
return inpData;
}
}

public class inputsClass
{
public double inp1 {get;组; }
public double inp2 {get;组; }
}

public class outputsClass
{
public double otp1 {get;组; }
public double otp2 {get;组; }
}

public class toClass
{
public inputsClass Inp {get;组; }
public outputsClass Outp {get;组; }


}

}

解决方案

看看原始MDSN文档中的XAML示例在 System.Windows.Controls.GridView

http://msdn.microsoft.com/en-us/library/system.windows.controls.gridview %28v = vs.110%29.aspx [ ^ ]。



你需要使用XML元素 GridViewColumn ,属性为 DisplayMemberBinding (再次,请参阅XAML示例)。在运行时期间,应将 inpData 分配给控件的属性 ItemsSource 。在你的情况下,对于 toClass (错误的名字!它违反了Microsoft的命名条件并且很奇怪:在类的名称中使用class一词!)列将是像这样:

 <! -     ...    - >   
< GridView >
< GridViewColumn 标题 = 名字 < span class =code-attribute>

< span class =code-attribute> DisplayMemberBinding = {Binding XPath = Inp} / >
< GridViewColumn 标题 = 姓氏

DisplayMemberBinding = {Binding XPath = Outp} / >
< / GridView >
<! - 。 .. - >





请注意,如果要静态表示数据,这就足够了。绑定的想法也是允许UI处理数据的变化。如果要在运行时更改列表(添加/删除/移动项目),列表是不够的。如果您希望UI响应此类更改,则需要实现支持接口的集合 System.Collections.ObjectModel.ObservableCollection< toClass>

http://msdn.microsoft.com/en-us/library/ms668604.aspx [ ^ ]。



此外,如果您还需要修改 toClass 成员的UI处理更改的方式,这个类应该实现接口 System.ComponentModel.INotifyPropertyChanged

http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged%28v=vs.110%29.aspx [ ^ ]。



查看代码示例上面引用的MSDN文章。不幸的是,这个界面和 System.ComponentModel 中的所有内容一样难看。在这种情况下,您需要保持事件的事件参数和属性返回的属性名称的字符串值的同步:

http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.propertychanged %28v = vs.110%29.aspx [ ^ ],

http://msdn.microsoft.com/en-us/library/system.componentmodel.propertychangedeventhandler%28v=vs.110%29.aspx [ ^ ],

<小时EF = http://msdn.microsoft.com/en-us/library/system.componentmodel.propertychangedeventargs(v=vs.110).aspx> http://msdn.microsoft.com/en-us/library /system.componentmodel.propertychangedeventargs(v=vs.110).aspx [ ^ ]。



与<$绑定的XAML代码c $ c> PropertyChanged 可以如下所示:

 <  < span class =code-leadattribute> GridViewColumn  >  
< GridViewColumnHeader ... >
<! - ... - & gt;
< GridViewColumn.CellTemplate >
< DataTemplate >
< TextBlock < span class =code-attribute> 文本 = {Binding Inp,UpdateSourceTrigger = PropertyChanged} / >
< / DataTemplate >
< / GridViewColumn.CellTemplate >
< / GridViewColumn >





我假设你用 TextBlock <绑定你的 Inp 属性/ code>,其属性文本。由于您的 Inp 不是字符串,因此您需要具有字符串属性并将其转换为double( double.Parse )并返回。这里的关键是字符串Inp。它不必与属性的真实名称相同,但您需要保持此字符串的唯一性(每个集合元素类)并使其与通过 System.ComponentModel返回的字符串保持同步.PropertyChangedEventArgs 。正如我所说,这很难看,但这就是 INotifyPropertyChanged 的工作方式,不仅适用于XAML。







回复一些后续问题:



对于命名约定,你可以使用FXCop:

http://en.wikipedia.org/wiki/Fxcop [ ^ ],

http://msdn.microsoft.com/en-us/library/bb429476.aspx [ ^ ]。



太糟糕了,FxCop很老了,但是......首先,你可以用它来检查你的程序集,包括命名约定。其次,该产品已经引用了正式的MSDN命名规则和其他文档。

我认为大量的规则只是愚蠢的,但检查命名,性能问题和其他外观对我很好您可以随时关闭一些支票或编写自己的规则(实际上是排除)。



如果只是本主题的文档,请从这里开始: http://msdn.microsoft.com/en-us/library/vstudio /ms229045%28v=vs.100%29.aspx [ ^ ]。



关于在网格视图中显示任意类型或者列表视图,这是一项非常艰巨的任务,是一种一流的技术。你应该首先清楚地告诉我这个非平凡且非常有趣的要求。



这类似于序列化和数据合同,其中任何类型都是根据某些规则构建的可以序列化;并且序列化系统对特定类型完全不可知。这里的关键是反思。您需要在运行时通过Reflection发现类型,找出要绑定的成员或事件以及如何绑定,然后相应地构建绑定和呈现控件的实例。我清楚地知道该怎么做,但这将是一项非常认真的工作。如果你有兴趣,我可以解释一下这个问题,但只有你提出特别的问题。 (否则,这将是一篇非常大的文章。)



此外,如果你有很多对象,反射性能将是一个问题。在这种情况下,您需要使用System.Reflection.Emit(与序列化一样),这比序列化要困难一个数量级,因为它需要很好地理解IL,并且调试很困难。简而言之,您需要保留一个在运行时出现的所有绑定类型的容器。在运行中,您应该生成类似绑定程序集的内容,并与已绑定和存储的类型相关联。当你需要绑定一些在你的集合中注册的类型的对象时,你需要检索其中一个动态创建的程序集并调用适当的绑定代码。



I要明白这听起来很复杂而且不太清楚,但这是要走的路。如果你有一些问题,欢迎你问他们,但是他们可能不容易回答,只是因为涉及很多工作。



< DD> -SA


Hello All,

I am trying to bind a a complex class to wpf datagrid. My class contains some properties which are classes themselves. And there can me many such types of classes with varying complexity. So I am looking for a generic solution.

I created a simple example to illustrate my issue (code below)
The link below shows what I am seeing in the datagrid currently and what I want to see

Outputs

How to accomplish the desired outputs display?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;

namespace MainApp
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

          dgMain.ItemsSource =   SetUP();
        }

        public Collection<toClass> SetUP()
        {
            Collection<toClass> inpData = new Collection<toClass> { new toClass { Inp = new inputsClass { inp1 = 100, inp2 = 200 }, Outp = new outputsClass { otp1 = 30, otp2 = 25 } } };
            return inpData;
        }
    }

    public class inputsClass
    {
        public double inp1 { get; set; }
        public double inp2 { get; set; }
    }

    public class outputsClass
    {
        public double otp1 { get; set; }
        public double otp2 { get; set; }
    }

    public class toClass
    {
        public inputsClass Inp { get; set; }
        public outputsClass Outp { get; set; }


    }

}

解决方案

Look at the XAML sample in the original MDSN documentation on System.Windows.Controls.GridView:
http://msdn.microsoft.com/en-us/library/system.windows.controls.gridview%28v=vs.110%29.aspx[^].

You need to use the XML element GridViewColumn with the attribute DisplayMemberBinding (again, see that XAML sample). Your inpData should be assigned to the property ItemsSource of your control, during runtime. In your case, for toClass (bad name! it violates Microsoft naming condition and is weird: using the word "class" in the name of the class!) the columns would be like this:

<!-- ... -->
<GridView>
      <GridViewColumn Header="First Name" 

                      DisplayMemberBinding="{Binding XPath=Inp}" />
      <GridViewColumn Header="Last Name" 

                      DisplayMemberBinding="{Binding XPath=Outp}" />
</GridView>
<!-- ... -->



Note that this is enough if you want to represent your data statically. The idea of binding is also to allow UI to handle the changes in data. If you want to change your list during runtime (add/remove/move items), the list is not enough. If you want the UI to respond to such changes, you need to implement the collection supporting the interface System.Collections.ObjectModel.ObservableCollection<toClass>:
http://msdn.microsoft.com/en-us/library/ms668604.aspx[^].

Moreover, if you also need to modify the members of toClass the way the changes are handled by the UI, this class should implement the interface System.ComponentModel.INotifyPropertyChanged:
http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged%28v=vs.110%29.aspx[^].

Look at the code sample in the MSDN article referenced above. Unfortunately, this interface is as ugly as everything in System.ComponentModel. In this case, you need to keep in sync the string value of the property name returned by the event arguments of the event and the property :
http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.propertychanged%28v=vs.110%29.aspx[^],
http://msdn.microsoft.com/en-us/library/system.componentmodel.propertychangedeventhandler%28v=vs.110%29.aspx[^],
http://msdn.microsoft.com/en-us/library/system.componentmodel.propertychangedeventargs(v=vs.110).aspx[^].

The XAML code of binding with the PropertyChanged can look like this:

<GridViewColumn>
    <GridViewColumnHeader ...>
    <!-- ... -->
    <GridViewColumn.CellTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Inp, UpdateSourceTrigger=PropertyChanged}"/>
        </DataTemplate>
    </GridViewColumn.CellTemplate>
</GridViewColumn>



I assumed that you bind your Inp property with some TextBlock, its property Text. As your Inp is not a string, you would need, for example, to have a string property and convert it to double (double.Parse) and back. The key here is the string "Inp". It does not have to be the same as the real name of the property, but you need to keep the uniqueness of this string (per collection element class) and keep it in sync with that returned through System.ComponentModel.PropertyChangedEventArgs. As I say, this is ugly, but this is how INotifyPropertyChanged works, not only with XAML.

[EDIT]

In reply to some follow-up questions:

For naming conventions, you can use FXCop:
http://en.wikipedia.org/wiki/Fxcop[^],
http://msdn.microsoft.com/en-us/library/bb429476.aspx[^].

Too bad, FxCop is pretty old, but… First, you can checkup your assemblies with it, including naming conventions. Secondly, the product has all references to formal MSDN naming rules and other documents.
I think that a good amount of of the rules are just stupid, but check up of naming, performance problems, and other looks quite good to me. You can always switch off some checks or write your own rules ("exclusions", actually).

For just documentation of this topic, please start here: http://msdn.microsoft.com/en-us/library/vstudio/ms229045%28v=vs.100%29.aspx[^].

As to showing of the arbitrary type in a grid view or a list view, this is a very difficult task, a kind of some top-notch techniques. You should have told about this non-trivial and quite interesting requirement clearly in first place.

This is similar to serialization and Data Contract where any type built according to some rules can be serialized; and the serializing system is totally agnostic to the particular types. The key here is reflection. You need to discover types during runtime via Reflection, figure out what members or events to bind and how, and then build both binding and the instance of the presenting control accordingly. I clearly understand how to do it, but it would be a very serious work. If you are interested, I can explain hot to do it, but only if you ask particular questions. (Otherwise, it would be a matter of a really big article.)

Moreover, if you have many objects, the Reflection performance would be a problem. In this case, you would need to use System.Reflection.Emit (as serialization does), and this is an order of magnitude more difficult than serialization, because it needs good understanding of IL, and the debugging is difficult. In brief, you would need to keep a container of all bound type as they appear during runtime. On the fly, you should generate something like "binding assemblies" and associate with already bound and stored types. When you need to bind some object of the type which is registered in your collection, you need to retrieve one of those dynamically created assemblies and invoke appropriate binding code.

I do understand that it sounds complex and not quite clear, but this is the way to go. If you have some questions about that, you are welcome to ask them, but they might be not easy to answer, just because a lot of work is involved.

—SA


这篇关于将任意类绑定到wpf数据网格的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆