This is a migrated thread and some comments may be shown as answers.

Tricky header bindings in GridViewDataColumn

16 Answers 782 Views
GridView
This is a migrated thread and some comments may be shown as answers.
Geoff Smith
Top achievements
Rank 1
Geoff Smith asked on 07 Oct 2010, 12:55 PM
Hi,

I'm having some trouble using bindings in my column headers. After reading this post: http://www.telerik.com/community/forums/wpf/gridview/binding-the-header-property-of-a-gridviewcolumn.aspx it seems like you have to have the property you are binding to available when the column is initialised, otherwise it will not get updated. 

I am wanting to bind to a property of the data context of a parent of the control that hosts the RadGridView. The DataContext hasn't been set by the time the column is initialized. Is there a way around this? I had a few ideas:

- Is there some event I can handle which is called before the columns are initialized, but after we have access to the UserControl's parent and hence its DataContext.
- Is there a way to reset the columns and hence the bindings?

I'm trying an MVVM approach, but I can't seem to fit in the ModelView approach with the column headers as I can't bind. The only way is to have the DataContext set in the control constructor, but that's fairly limited and I would rather avoid it.

Any suggestions / tips would be much appreciated!

16 Answers, 1 is accepted

Sort by
0
Geoff Smith
Top achievements
Rank 1
answered on 07 Oct 2010, 01:15 PM
I'm just posting an example to better explain what I mean. First of all I have my control defined like this. The model view for ControlWithGrid should be a property of the main model view.

<Page DataContext="{StaticResource modelView}">
    <ControlWithGrid DataContext="{Binding SubModelView}"/>
</Page>

I define a RadGridView in the ControlWithGrid and attempt to bind the column header to a property of SubModelView:

<UserControl>
    <telerik:RadGridView
                             ItemsSource="{Binding Players}"
                             AutoGenerateColumns="False">
        <telerik:RadGridView.Columns>
            <telerik:GridViewColumn Header="{Binding Path=HeaderName}" >
                <telerik:GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Name}" />
                    </DataTemplate>
                </telerik:GridViewColumn.CellTemplate>
            </telerik:GridViewColumn>
        </telerik:RadGridView.Columns>
    </telerik:RadGridView>
 
</UserControl>

However, the header binding is not set because the DataContext is set too late.

I've come up with a workaround where I've created sub-classes of GridViewColumn and added them to RadGridView.Columns after DataContext is set. This works fine, though it would be nice to have a better solution if possible.
0
Maya
Telerik team
answered on 13 Oct 2010, 09:23 AM
Hi Geoff Smith,

Basically, the DataContext of the column's header is different that of the column itself. So, you may need to define your ViewModel as a Resource and then set the Source property in the Binding of the Header. 
For example:

<Window.Resources>
    <local:Headers x:Key="MyViewModel" />
</Window.Resources>

<telerik:GridViewDataColumn.Header>
   <TextBlock Text="{Binding HeaderName, Source={StaticResource MyViewModel}}" />  
</telerik:GridViewDataColumn.Header>

HeaderName is a property defined in your ViewModel for example. 
Let me know if it meets your requirements.

Kind regards,
Maya
the Telerik team
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
0
irabanta
Top achievements
Rank 1
answered on 30 Dec 2010, 04:00 PM
hi, In the MyViewModel, i updated the HeaderName property value and also notified, but it remains the static data. I mean the value i assigned doesn't come up in the column header.
0
Maya
Telerik team
answered on 03 Jan 2011, 09:10 AM
Hi Geoff Smith,

I am sending you a sample project illustrating the solution proposed above. Please take a look at it and let me know in case of any misunderstandings according to your requirements. Feel free to change the application in the way you want and send it back if needed.
 

Kind regards,
Maya
the Telerik team
Browse the videos here>> to help you get started with RadControls for WPF
0
Mark
Top achievements
Rank 1
answered on 20 Dec 2012, 10:45 PM
I looked at Maya's Jan 3, 2011 proposed solution.  

I don't understand why the data context of the Header property is not inherited from it's parent object on the visual tree.

I think this must be a bug.

It is not a big deal to explicitly set the data context of the Header property in code if you only have one grid in your application.  But if the grid is part of a user control that appears on, say, multiple tab items, then it is more of a big deal.  
0
Maya
Telerik team
answered on 21 Dec 2012, 08:08 AM
Hello Geoff,

The header of the column is of type object and you can place whichever control you want inside. It was out intention to leave it that way and not have it bound to the data context of the grid. 

Greetings,
Maya
the Telerik team

Explore the entire Telerik portfolio by downloading Telerik DevCraft Ultimate.

0
Mark
Top achievements
Rank 1
answered on 29 Dec 2012, 11:31 PM
re: It was our intention to not have it bound to the data context of the grid

Perhaps there is a good reason for this. But I think it is standard for objects in the visual tree to inherit their parent's data context. 

In my application, the RadGridView with bound column headers is part of a user control that appears on multiple RadTabItems. Each RadTabItem has it's own ViewModel object.  It would be nice if the header objects could inherit their data context from the RadTabItem.

It is not a big deal for me to work around this.

p.s. Being able to bind column headers is a nice feature.


0
Maya
Telerik team
answered on 02 Jan 2013, 07:55 AM
Hello Mark,

It was our intention to leave it that way so the user has the freedom to bind it to whichever context he requires. Similar is the case with the DataGrid.  

Greetings,
Maya
the Telerik team

Explore the entire Telerik portfolio by downloading Telerik DevCraft Ultimate.

0
De
Top achievements
Rank 1
answered on 12 Sep 2015, 10:56 PM

 Here are 2 generic classes (DataContextProxy and DataContextProxyBehavior) that help to pass data context from one control to resource so that other visual/or not visual can bind to.  

DataContextProxy:  Just a simple VM that hold DataContext object.

DataContextProxyBehavior:  Simple behavior that can be attached to any FrameworkElement.  Once attached, it tracks that FrameworkElement's DataContext.  Any change of DataContext, it will pass the change to DataContextProxy.  The goal is to have DataContextProxy be in resource that other visual/non visual can bind to.

Usage:  See an example of declaration and usage at the bottom.

Hope it helps someone :)

 

01.public class DataContextProxy : SomeImplementationOfINotifyPropertyChanged
02.{
03.    #region private data
04.    private object _dataContext;
05.    #endregion
06. 
07.    #region public properties
08.    public object DataContext
09.    {
10.        get { return _dataContext; }
11.        set { _dataContext = value; FirePropertyChanged(() => DataContext); }
12.    }
13.    #endregion
14. 
15.    #region constructors
16.    public DataContextProxy()
17.    {
18.        _dataContext = null;
19.    }
20.    #endregion
21.}

 

 

01.public class DataContextProxyBehavior : Behavior<FrameworkElement>
02.{
03.    #region DataContextProxy Dependency
04.    public DataContextProxy DataContextProxy
05.    {
06.        get { return (DataContextProxy)GetValue(DataContextProxyProperty); }
07.        set { SetValue(DataContextProxyProperty, value); }
08.    }
09. 
10.    public static readonly DependencyProperty DataContextProxyProperty =
11.        DependencyProperty.Register("DataContextProxy", typeof(DataContextProxy), typeof(DataContextProxyBehavior));
12.    #endregion
13. 
14.    #region overrides
15.    protected override void OnAttached()
16.    {
17.        base.OnAttached();
18. 
19.        UpdateDataContext();
20.        AssociatedObject.DataContextChanged += AssociatedObjectOnDataContextChanged;
21.    }
22. 
23.    protected override void OnDetaching()
24.    {
25.        base.OnDetaching();
26.        AssociatedObject.DataContextChanged -= AssociatedObjectOnDataContextChanged;
27.    }
28.    #endregion
29. 
30.    #region private functions
31.    private void AssociatedObjectOnDataContextChanged(object sender, DependencyPropertyChangedEventArgs args)
32.    {
33.        UpdateDataContext();
34.    }
35. 
36.    private void UpdateDataContext()
37.    {
38.        if (AssociatedObject != null && DataContextProxy != null)
39.        {
40.            DataContextProxy.DataContext = AssociatedObject.DataContext;
41.        }
42.    }
43.    #endregion
44.}

 

Sample Usage of DataContextProxy and DataContextProxyBehavior classes: 

Create an instance of DataContextProxy in resource dictionary
<UserControl.Resources>
   <DataContextProxy x:Key="DataContextProxy"/>
</UserControl.Resources>
 
Use DataContextProxyBehavior to pass an element framework's DataContext to DataContextProxy
<Grid DataContext="{Binding SomeViewModel}">
   <i:Interaction.Behaviors>
       <DataContextProxyBehavior DataContextProxy="{StaticResource DataContextProxy}"/>
   </i:Interaction.Behaviors>
</Grid>
 
In a non-visual or visual element, bind to DataContextProxy using StaticResource.
<telerik:GridViewDataColumn.Header>
   <TextBlock Text="{Binding DataContext.SomeTextPropertyOnSomeViewModel, Source={StaticResource DataContextProxy}}" />
</telerik:GridViewDataColumn.Header>

0
Maya
Telerik team
answered on 14 Sep 2015, 06:50 AM
Hi De,

That's great. Thank you a lot for supporting the community. 

Regards,
Maya
Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
De
Top achievements
Rank 1
answered on 14 Sep 2015, 06:25 PM
My pleasure.  Thanks.
0
azerty
Top achievements
Rank 1
answered on 19 Oct 2015, 02:39 PM

Hi,

 

What about if I want the data context of the Windows ?

If I understand the ItemsSource of RadGridView is related only for the grid item ? (that's work great for me).

But what is the current context of GridViewDataColumn.Header ?

 In my case I have UserControl wich has a viewModel as DataContext.

This viewModel contains to properties: title and ObservableCollection<AnyClass> and I wnt that Title is bound to the header and the observableCollection (called collection) bound to rows.

All thinks work exepted the header....

Can you help me ?

Best Regards,

0
De
Top achievements
Rank 1
answered on 19 Oct 2015, 04:22 PM

question: What about if I want the data  context of the Windows? 

answer: I am assumed that the RadGridView is hosted by a Window? If so, in general you can get the DataContext of the parent window by using 

<Binding DataContext.YourPropertyInsideWindowDataContext, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}

 

If using the DataContextProxy in previous post, then you would replace 

<Grid DataContext="...">

with 

 

<Grid DataContext="DataContext.YourPropertyInsideWindowDataContext, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}">

By now, the Grid would bind to YourPropertyInsideWindowDataContext and the DataContextProxy would have YourPropertyInsideWindowDataContext.  Then your GridViewDataColumn.Header would be able to access Title and ObservableCollection inside YourPropertyInsideWindowDataContext (follow sample described in previous post). 

question: If I understand the ItemsSource of RadGridView is related only for the grid item ?
answer: generally yes.

question: But what is the current context of GridViewDataColumn.Header ?
answer:  as I can remember, it's just empty typed object.  It's definitely a different visual tree than the main one.   That's the reason why the DataContextProxy was introduced so that the Header can get DataContext of main visual tree.

Hope the answers help you.  The key to remember is to somehow to initialize the DataContextProxy the right VM. Once you have the right VM, you can do anything with it.  

0
Maya
Telerik team
answered on 20 Oct 2015, 11:07 AM
Hi De,

Thanks again for the absolutely accurate answers and the support to the other devs in the forum. I updated your Telerik points for the cooperation. 

Regards,
Maya
Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Markus Hopfenspirger
Top achievements
Rank 2
answered on 01 Jun 2016, 03:12 PM
I had the same problem. Just in case someone needs a simple solution to this problem:

 
                <telerik:GridViewDataColumn DataMemberBinding="{Binding DataItemProperty}" Width="70">
                    <telerik:GridViewDataColumn.Header>
                        <TextBlock Text="{Binding DataContext.HeaderTextProperty,RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" />
                    </telerik:GridViewDataColumn.Header>
                </telerik:GridViewDataColumn>

If the DataContext is not holded by the UserControl but another Parent, you can change the AncestorType accordingly...

0
Maya
Telerik team
answered on 06 Jun 2016, 07:27 AM
Hello Markus,

Great, thanks a lot for providing another approach for handling the case.

Regards,
Maya
Telerik
Do you need help with upgrading your AJAX, WPF or WinForms project? Check the Telerik API Analyzer and share your thoughts.
Tags
GridView
Asked by
Geoff Smith
Top achievements
Rank 1
Answers by
Geoff Smith
Top achievements
Rank 1
Maya
Telerik team
irabanta
Top achievements
Rank 1
Mark
Top achievements
Rank 1
De
Top achievements
Rank 1
azerty
Top achievements
Rank 1
Markus Hopfenspirger
Top achievements
Rank 2
Share this question
or