0

I am attempting to bind an array of arrays to a DataGrid.

I am aware that I could do this fairly easily by converting the data to a DataTable and then binding that. BUT this isn't what I need as I need to be able to add groupings to the data via a PagedCollectionVIew.

The reason for doing this is: I have a set of controls that create a structure for a document, I want to reflect this structure with dummy data dynamically while the structure controls are being used. The dummy data is a simple symmetric matrix of random ints where the rows are the data elements, I want to bind each grid column to the elemtns of the array, NOT the array object.

_dataGenerator = new DummyDataGenerator();
_dummyDataView = new ObservableCollection<int[]>();
DummyData = new PagedCollectionView(_dummyDataView);

Where the _dummyDataView is populated by

// Set up the dummy data for the fields available.
_dataGenerator.CreateData(ReportFields).ForEach(_dummyDataView.Add);

and the XAML is just a DataGrid binding to the DummyData... then I will be dynamically adding Groupings and Sortings to is as the user plays with the document data. I have searched everywhere but can't find a solution, but there must be a way (someone must have blogged this) to bind to elements of an array in xaml! Really need some help here.

2
  • I have answered my own question below. This should probably be a blog post as everyone who has been involved here has been me. But there are some really useful snippets in there. Worth a read I guess. Commented Mar 12, 2013 at 15:27
  • Hey, past me... you could have used the ExpandoObject rather than creating a new type? blogs.msdn.com/b/csharpfaq/archive/2009/10/01/… Commented Dec 12, 2013 at 10:17

1 Answer 1

1

I have the answer (well I had it ages ago, but I remembered I posted this question). It's a frankencode monster from about a zillion snippets and my own brain, so I can't attribute everyone. I am going to put it up. Because I think it is awesome. Even if nobody else cares:

It uses: - The DLR - A bit of reflection - A bit of IL building.

Should probably put all of this into a blog post somewhere but I don't keep a blog.

This is the code that creates the data for the view called by the ViewModel.

    public IEnumerable<dynamic> CreateData(ObservableCollection<ReportFieldVm> reportFields)
    {
        // Find the length of the array.
        var size = reportFields.Count;

        // Create matrix.
        var b = new int[size, size];

        // Random for the values.
        var rand = new Random((int)DateTime.Now.Ticks);

        // Build the symmetric matrix.
        for (var i = 0; i < size; i++)
        {
            for (var j = 0; j < size; j++)
            {
                if (i == j)
                {
                    b[i,j] = rand.Next(0, 100);
                }
                else
                {
                    var a = rand.Next(0, 100);
                    b[i,j] = a;
                    b[j,i] = a;
                }
            }
        }

        // Define the assembly to add out new type to.
        var asmName = new AssemblyName("DummyAssembly");
        var ab = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.Run);
        var mb = ab.DefineDynamicModule("DummyModule");

        // Define our type.
        var d = mb.DefineType("DummyType", TypeAttributes.Public);

        const MethodAttributes GetSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;

        // Define all the fields for the new type.
        foreach (var rf in reportFields.OrderBy(rf => rf.SelectOrder))
        {
            var propertyName = rf.FieldName;

            var field = d.DefineField("m_" + propertyName, typeof(int), FieldAttributes.Private);

            var property = d.DefineProperty(propertyName, PropertyAttributes.HasDefault, typeof(int), null);

            var dGetAccessor = d.DefineMethod("get_" + propertyName, GetSetAttr, typeof(int), Type.EmptyTypes);

            var numberGetIl = dGetAccessor.GetILGenerator();
            numberGetIl.Emit(OpCodes.Ldarg_0);
            numberGetIl.Emit(OpCodes.Ldfld, field);
            numberGetIl.Emit(OpCodes.Ret);

            var dSetAccessor = d.DefineMethod("set_" + propertyName, GetSetAttr, null, new Type[] { typeof(int) });
            var numberSetIl = dSetAccessor.GetILGenerator();
            numberSetIl.Emit(OpCodes.Ldarg_0);
            numberSetIl.Emit(OpCodes.Ldarg_1);
            numberSetIl.Emit(OpCodes.Stfld, field);
            numberSetIl.Emit(OpCodes.Ret);

            property.SetGetMethod(dGetAccessor);
            property.SetSetMethod(dSetAccessor);
        }

        // Create the type.
        var dummyType = d.CreateType();

        var array = new List<dynamic>();

        // Convert the matrix into the array of the dynamic.
        for (var i = 0; i < size; i++)
        {
            var obj = Activator.CreateInstance(dummyType);
            int j = 0;
            foreach (var rf in reportFields.OrderBy(rf => rf.SelectOrder))
            {
                var type = obj.GetType();
                var prop = type.GetProperty(rf.FieldName);
                prop.SetValue(obj, b[j, i], null);
                j++;
            }
            array.Add(obj);
        }

        return array;
    }

This is the code that binds the view, called in the view.

Where :

  1. _dynamicReportPreview is the named grid in the XAML.

  2. ViewModel is a property that exposes the DataContext of the view set in the view constructor.

    private void BuildPreviewGridColumns()
    {
        if (_dynamicReportPreview == null)
            return;
    
        _dynamicReportPreview.Columns.Clear();
    
        var initialFields = ViewModel.ReportFields.OrderBy(rf => rf.SelectOrder);
    
        foreach (var rf in initialFields)
        {
            var col = new Column
            {
                ColumnName = rf.FieldName, 
                Binding = new Binding(rf.FieldName)
            };
    
            _dynamicReportPreview.Columns.Add(col);
    
            if (!rf.IsVisible)
            {
                col.Visible = false;
            }
        }
    }
    
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.