Unable to use DataTemplates with GridView dynamic data. Not even with ICustomTypeProvider

1 Answer 240 Views
GridView
Joe
Top achievements
Rank 2
Iron
Iron
Veteran
Joe asked on 09 Jul 2022, 06:19 PM | edited on 09 Jul 2022, 11:20 PM


I'm binding my GridView to a custom report class with records.   The `Report`class has a list of field types that is not known until runtime.  It exposes an array of `Record` objects.  The Record class derives from System::DynamicObject.  Binding an IEnumerable<Record> to GridView is pretty easy with the AutoGenerateColumns turned on.    (Note this code is C++/CLI but the client code that uses it is C#/WPF/XAML)

public ref class Record 
    : public DynamicObject
{
    int     m_row;
    Report^ m_rep;

public:
    Record(Report^ rep, int row);
#pragma region DynamicObject Overrides
    virtual bool TryGetMember(GetMemberBinder^ binder, [SRI::Out]Object^% obj) override;
    virtual bool TrySetMember(SetMemberBinder^ binder, Object^ value) override;
    virtual IEnumerable<String^>^ GetDynamicMemberNames () override;
    virtual bool TryGetIndex(GetIndexBinder^ binder, array<Object^>^ indices, [SRI::Out]Object^% result) override;
#pragma endregion 
    
    property Object^    default[int]  { Object^ get(int idx); }
    property Object^    default[String^] { Object^ get(String^ name); }
};

The problem is that when I bind it, this way, the values displayed just use the default `ToString()` method of each object.  That's not going to work.  I need to use DataTemplates, For example, I want any double value to use 3 decimal places. 


<tk:RadGridView.Resources>
    <DataTemplate x:Key="MyDoubleTemplate" DataType="{x:Type sys:Double}">
        <Label ContentStringFormat="{}{0:F3" Content="{Binding}"/>
    </DataTemplate>
</tk:RadGridView.Resources>


So I tried adding a handler for the `AutoGeneratingColumn` column event.  In the handler and explicitly specifying the `CellTemplate` to be used for each column.  (I named the templates in XAML and then loaded them directly via TryFindResource)

private void GridView_OnAutoGeneratingColumn(object? sender, GridViewAutoGeneratingColumnEventArgs e)
{
    if (e.Column is not GridViewDataColumn col)
        return;
    
    var name = col.DataMemberBinding.Path.Path;

    if (!TryGetField(name, out var field, out var rep))
        return;

    // Based on the field type, use a default DataTemplate that we previously loaded via TryFindResourcee

    switch (field.PropType)
    {
    case PropType.Double:  col.CellTemplate = DoubleTemplate; col.DataType = typeof(double); break;

// NOTE: Other types/templates not shown for brevity. default: e.Cancel = true; break; // Some type we don't recognize } col.CellEditTemplate = null; }


But this fails too.  The value supplied to my `DataTemplate' binding is not the individual record field double value, it is the entire Record object.     Likewise every other template reports errors because it is expecting the individual column double/int/string, etc but instead getting the whole `Record` object.

Next I tried using the CellTemplateSelector approach.,  I created one and then set THAT in the event handler above.  But the problem remained the same.  The selector's SelectTemplate method was receiving an object of type Record  

 OK, then I looked at your XAML-SDK where you show a nice example of binding to `ICustomTypeDescriptor`  (the BindingToCustomTypeDescriptor example).  That was a lot of work.  I made my Record class implement that interface just as in your exmaple.  I see its methods being called..  Hooked it up to the GridView. 

But results are exactly the same.  GridView still supplies the Record value instead of the individual cell value to the bindings.

Please tell me what I am doing wrong...



 


1 Answer, 1 is accepted

Sort by
0
Petar Mladenov
Telerik team
answered on 13 Jul 2022, 11:10 AM

Hi Joe,

I tried to replicate your scenario and I think I succeeded. Here is my code (based on 'Binding to Dynamic object SDK sample') and my initial result:

    <Grid DataContext="{StaticResource MyViewModel}">
        <telerik:RadGridView ItemsSource="{Binding Data}"  x:Name="xGridView"
                             Margin="5" AutoGenerateColumns="True" AutoGeneratingColumn="RadGridView_AutoGeneratingColumn">

            <telerik:RadGridView.Resources>
                <DataTemplate x:Key="MyDoubleTemplate" DataType="{x:Type sys:Int32}">
                    <Label ContentStringFormat="{}{0:F3}" Content="{Binding}"/>
                </DataTemplate>
            </telerik:RadGridView.Resources>
        </telerik:RadGridView>
    </Grid>

    public class MyViewModel: ViewModelBase
    {
        private ObservableCollection<MyDataRow> data;

        public ObservableCollection<MyDataRow> Data 
        {
            get 
            {
                if (this.data == null)
                {
                    this.data = this.GenerateData();
                }

                return this.data;
            }
        }

        private ObservableCollection<MyDataRow> GenerateData()
        {
            ObservableCollection<MyDataRow> items = new ObservableCollection<MyDataRow>();

            for (int i = 0; i < 10; i++)
            {
                dynamic item = new MyDataRow();
                
                item["ID"] = i;
                item["Name"] = "Name " + i.ToString();

                items.Add(item);
            }

            return items;
        }
    }

code behind:
        private void RadGridView_AutoGeneratingColumn(object sender, Telerik.Windows.Controls.GridViewAutoGeneratingColumnEventArgs e)
        {
            GridViewDataColumn col = e.Column as GridViewDataColumn;
            if (col != null)
            {
                if (col.DataMemberBinding.Path.Path == "ID")
                {
                    col.CellTemplate = this.xGridView.Resources["MyDoubleTemplate"] as DataTemplate;
                }
            }
        }



Redirecting the binding to ID property produces the following:

  <DataTemplate x:Key="MyDoubleTemplate" DataType="{x:Type sys:Int32}">
                    <Label ContentStringFormat="{}{0:F3}" Content="{Binding ID}"/>
                </DataTemplate>

Hope this can be useful.

Regards,
Petar Mladenov
Progress Telerik

Virtual Classroom, the free self-paced technical training that gets you up to speed with Telerik and Kendo UI products quickly just got a fresh new look + new and improved content including a brand new Blazor course! Check it out at https://learn.telerik.com/.

Tags
GridView
Asked by
Joe
Top achievements
Rank 2
Iron
Iron
Veteran
Answers by
Petar Mladenov
Telerik team
Share this question
or