Click here to Skip to main content
15,886,258 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
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?

C++
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; }


    }

}
Posted

1 solution

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:
XML
<!-- ... -->
<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:
XML
<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
 
Share this answer
 
v3
Comments
jainga10213 21-Jan-14 14:43pm    
Hello Sergey,
Thanks for your comments and detailed reply.

Actually, what I was looking for is to be able to bind an arbitrary complex class to a datagrid. That would mean I cannot create columns in XAML in advance as I have a lot of such complex classes with many nested properties. I wanted to avoid the tedious job of creating and maintaining such XAML files. Instead, what I want is a generic method that could recursively look for properties within nested classes and bind them to a datagrid. Of course my classes should implement INotifyPropertyChanged and the collection should be an observable collection. And with binding to datagrid if I change a value in the grid then it should trickle all the way down to the right class and right property (through the chain of nested classes).

BTW, Is there any document I can reference for naming convention in c#?
Sergey Alexandrovich Kryukov 21-Jan-14 15:28pm    
Please see my update to the answer, after [EDIT].
Unfortunately, this is not easy at all; so it would be very hard to guide you in all the detail. Anyway, will you accept my answer formally (green "Accept" button)? Your follow-up questions will be welcome anyway.
—SA
jainga10213 28-Jan-14 15:20pm    
Thank you Sergey for your suggestions. I Have decided to go with the initial approach that is also easy for me to comprehend and implement. I am now defining all the possible columns that the grid can have and then hide the unwanted columns based on some criteria.
Sergey Alexandrovich Kryukov 28-Jan-14 16:14pm    
Great.
You are very welcome. Thank you for the interesting questions.
Good luck, call again.
—SA
Maciej Los 21-Jan-14 15:52pm    
The most valuable answer i've ever seen!

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900