Patrick
Patrick

Reputation: 2583

How to databing datagrid with class with collection and modify columns accordingly

I have the following class:

public class MyDimension
{
    public MyDimension()
    {
        obcItemsName = new ObservableCollection<string>();
        obcItemsMeasured = new ObservableCollection<double>();
    }
    public string NameAxisDimension { get; set; }
    public double Nominal { get; set; }
    public double UpperTolerance { get; set; }
    public double LowerTolerance { get; set; }
    public ObservableCollection<string> obcItemsName;
    public ObservableCollection<double> obcItemsMeasured;
}

and its instances are stored into:

ObservableCollection<MyDimension> obcmMyDim = new ObservableCollection<MyDimension>();

I would like to databind it to a datagrid with

dtgResults.ItemsSource = obcmMyDim

so that the datagrid results like that (made with Excel)

enter image description here

where PART11 PART20 are the names stored in the obcItemsName collection and the values below are stored in the obcItemsMeasured collection.

Thank you in advance

Upvotes: 0

Views: 102

Answers (3)

J.H.
J.H.

Reputation: 4322

See my answer to your other question too: https://stackoverflow.com/a/35299063/2141972

You could convert your ObservableCollection to a System.Data.DataTable. Add the columns you know about at design time and then loop through the columns you know only at runtime and add those to the DataTable. Bind your DataGrid to this DataTable.

You didn't state if obcItemsName has the same values for each row or not so I coded up an example where it assumes they don't. See the comments in the code for more info.

XAML:

<Window x:Class="WpfApplication2.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:WpfApplication2"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <DataGrid ItemsSource="{Binding MyDataTable}" CanUserAddRows="False" />
</Window>

Code:

using System.Windows;
using System.Data;

namespace WpfApplication2
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            // Build list of MyDimensions manually (since I don't have access to your data)
            var Dimensions = CreateData();
            this.DataContext = new MyViewModel(Dimensions);
        }
        public IList<MyDimension> CreateData()
        {
            List<MyDimension> Dimensions = new List<MyDimension>();
            MyDimension d;
            Dimensions.Add(new MyDimension() { NameAxisDimension = "LOC1-X" });
            Dimensions.Add(new MyDimension() { NameAxisDimension = "LOC1-Y" });
            d = new MyDimension() { NameAxisDimension = "LOC1-D" };
            d.obcItemsName.Add("PART11");
            d.obcItemsMeasured.Add(10.0);
            d.obcItemsName.Add("PART20");
            d.obcItemsMeasured.Add(10.1);
            Dimensions.Add(d);
            Dimensions.Add(new MyDimension() { NameAxisDimension = "LOC1-RN" });
            Dimensions.Add(new MyDimension() { NameAxisDimension = "LOC2-X" });
            Dimensions.Add(new MyDimension() { NameAxisDimension = "LOC2-Y" });
            d = new MyDimension() { NameAxisDimension = "LOC2-DF" };
            d.obcItemsName.Add("PART11");
            d.obcItemsMeasured.Add(10.2);
            d.obcItemsName.Add("PART20");
            d.obcItemsMeasured.Add(10.3);
            Dimensions.Add(d);
            Dimensions.Add(new MyDimension() { NameAxisDimension = "LOC2-TP" });
            d = new MyDimension() { NameAxisDimension = "DIST1-M" };
            d.obcItemsName.Add("PART11");
            d.obcItemsMeasured.Add(14.14214);
            d.obcItemsName.Add("PART20");
            d.obcItemsMeasured.Add(14.14215);
            Dimensions.Add(d);
            d = new MyDimension() { NameAxisDimension = "DIST2-M" };
            d.obcItemsName.Add("PART11");
            d.obcItemsMeasured.Add(10.4);
            d.obcItemsName.Add("PART20");
            d.obcItemsMeasured.Add(10.5);
            Dimensions.Add(d);

            d = new MyDimension() { NameAxisDimension = "Other Field" };
            d.obcItemsName.Add("PartyLikeIts");
            d.obcItemsMeasured.Add(1999);
            Dimensions.Add(d);
            // etc...
            return Dimensions;
        }
    }

    public class MyViewModel
    {
        public DataTable MyDataTable { get; set; }

        public MyViewModel(IList<MyDimension> Dimensions)
        {
            // Create a DataTable
            // Add the already known columns - static
            // Add the unknown at designtime columns - dynamic
            // Optionally hook the RowChanged event of the DataTable to push the changes back to Dimensions

            MyDataTable = new DataTable();
            // Add the 'static' columns
            MyDataTable.Columns.Add("NameAxisDimension", typeof(string)).ReadOnly = true; // Assuming this is the PK and shouldn't change
            MyDataTable.Columns.Add("Nominal", typeof(double));
            MyDataTable.Columns.Add("UpperTolerance", typeof(double));
            MyDataTable.Columns.Add("LowerTolerance", typeof(double));
            // Add the 'dynamic' columns
            var names = Dimensions.SelectMany(aa => aa.obcItemsName.Select(bb => bb)).Distinct(); // Get a list of the distinct values in all of the obcItemsName collections
            foreach (var name in names)
                MyDataTable.Columns.Add(name, typeof(double));
            foreach (var dim in Dimensions)
            {
                List<object> vals = new List<object> { dim.NameAxisDimension, dim.Nominal, dim.UpperTolerance, dim.LowerTolerance }; // static field values
                foreach (var name in names)
                {
                    object val = 0.0; // Default value if obcItemsName doesn't have this dynamic field - could also use DBNull.Value
                    int idx = dim.obcItemsName.IndexOf(name);
                    if (idx != -1)
                        val = dim.obcItemsMeasured[idx];
                    vals.Add(val);
                }
                MyDataTable.Rows.Add(vals.ToArray());
            }

            // If needed, hook the RowChanged event to capture changes and push the changes back to the original collection
            MyDataTable.RowChanged += (s, e) =>
            {
                if (e.Action == DataRowAction.Change)
                {
                    // Assumes that "NameAxisDimension" is the PK (and unique to the row)
                    var nameAxisDimension = e.Row.Field<string>("NameAxisDimension");
                    var item = Dimensions.FirstOrDefault(aa => aa.NameAxisDimension == nameAxisDimension);
                    if (item != null)
                    {
                        // Found the entry, update its values
                        // Static fields
                        item.Nominal = e.Row.Field<double>("Nominal");
                        item.UpperTolerance = e.Row.Field<double>("UpperTolerance");
                        item.LowerTolerance = e.Row.Field<double>("LowerTolerance");
                        // Dynamic fields
                        foreach (var name in names)
                        {
                            int idx = item.obcItemsName.IndexOf(name);
                            if (idx != -1)
                            {
                                item.obcItemsMeasured[idx] = e.Row.Field<double>(name);
                            }
                            else
                            {
                                // This row doesn't have that name in obcItemsName. Add it?
                                item.obcItemsName.Add(name);
                                item.obcItemsMeasured.Add(e.Row.Field<double>(name));
                            }
                        }
                    }
                }
            };
        }
    }

    public class MyDimension
    {
        public MyDimension()
        {
            obcItemsName = new ObservableCollection<string>();
            obcItemsMeasured = new ObservableCollection<double>();
        }
        public string NameAxisDimension { get; set; }
        public double Nominal { get; set; }
        public double UpperTolerance { get; set; }
        public double LowerTolerance { get; set; }
        public ObservableCollection<string> obcItemsName;
        public ObservableCollection<double> obcItemsMeasured;
    }
}

Screenshot:

enter image description here

Upvotes: 0

Ahmed Attia
Ahmed Attia

Reputation: 127

You can find here a good tutorial to show how you can work with MVVM design pattern MVVM TUTORIAL

Upvotes: 1

Ahmed Attia
Ahmed Attia

Reputation: 127

In your ViewModel :

 private void LoadData()
{
    obcmMyDim = new ObservableCollection<MyDimension>() {
    new MyDimension() { DimensionProperty = value}, }

And then Initilize this method in the ViewModel Main constructor

and in the Xaml code

 <datagrid itemsSource="{binding obcmMyDim }" />

Upvotes: 0

Related Questions