Tricky header bindings in GridViewDataColumn

17 posts, 0 answers
  1. Geoff Smith
    Geoff Smith avatar
    48 posts
    Member since:
    Mar 2010

    Posted 07 Oct 2010 Link to this post

    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!
  2. Geoff Smith
    Geoff Smith avatar
    48 posts
    Member since:
    Mar 2010

    Posted 07 Oct 2010 Link to this post

    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.
  3. UI for WPF is Visual Studio 2017 Ready
  4. Maya
    Admin
    Maya avatar
    4062 posts

    Posted 13 Oct 2010 Link to this post

    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
  5. irabanta
    irabanta avatar
    1 posts
    Member since:
    Dec 2010

    Posted 30 Dec 2010 Link to this post

    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.
  6. Maya
    Admin
    Maya avatar
    4062 posts

    Posted 03 Jan 2011 Link to this post

    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
  7. Mark
    Mark avatar
    15 posts
    Member since:
    Apr 2012

    Posted 20 Dec 2012 Link to this post

    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.  
  8. Maya
    Admin
    Maya avatar
    4062 posts

    Posted 21 Dec 2012 Link to this post

    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.

  9. Mark
    Mark avatar
    15 posts
    Member since:
    Apr 2012

    Posted 29 Dec 2012 Link to this post

    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.


  10. Maya
    Admin
    Maya avatar
    4062 posts

    Posted 02 Jan 2013 Link to this post

    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.

  11. De
    De avatar
    16 posts
    Member since:
    Aug 2011

    Posted 12 Sep 2015 Link to this post

     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>

  12. Maya
    Admin
    Maya avatar
    4062 posts

    Posted 14 Sep 2015 Link to this post

    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
  13. De
    De avatar
    16 posts
    Member since:
    Aug 2011

    Posted 14 Sep 2015 Link to this post

    My pleasure.  Thanks.
  14. azerty
    azerty avatar
    1 posts
    Member since:
    Oct 2015

    Posted 19 Oct 2015 Link to this post

    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,

  15. De
    De avatar
    16 posts
    Member since:
    Aug 2011

    Posted 19 Oct 2015 Link to this post

    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.  

  16. Maya
    Admin
    Maya avatar
    4062 posts

    Posted 20 Oct 2015 Link to this post

    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
  17. Markus Hopfenspirger
    Markus Hopfenspirger avatar
    21 posts
    Member since:
    Oct 2009

    Posted 01 Jun in reply to Maya Link to this post

    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...

  18. Maya
    Admin
    Maya avatar
    4062 posts

    Posted 06 Jun Link to this post

    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.
Back to Top
UI for WPF is Visual Studio 2017 Ready