Column Header Binding

7 posts, 1 answers
  1. Rick Glos
    Rick Glos avatar
    70 posts
    Member since:
    Mar 2009

    Posted 22 Mar 2010 Link to this post

    Hello,

    I have a Grid that displays a start and end time for each day of the week.  The column headers must include the Date and DayOfWeek formatted basically like this: ...ToString("ddd M/d")

    I'm not sure how I go about setting the Binding property.  Here's what I have just using hard coded values but really, I need these to be bound to the view model (and not StaticResource either because if the user chooses a different week, then those values must update).

            <telerik:RadGridView Grid.Row="1" Margin="10" Name="radGridViewScheduleLines" ItemsSource="{Binding ScheduleLines}" AutoGenerateColumns="False" IsFilteringAllowed="False" ShowGroupPanel="False"
                <telerik:RadGridView.Columns> 
                    <telerik:GridViewDataColumn DataMemberBinding="{Binding TeamMemberName}" Header="Team Member" CellStyle="{StaticResource TeamMemberColumnStyle}" IsSortable="False" /> 
                    <telerik:GridViewDataColumn DataMemberBinding="{Binding JobCode}" Header="Code" IsSortable="False" /> 
                    <telerik:GridViewComboBoxColumn DataMemberBinding="{Binding SundayStartDaySegment, Mode=TwoWay}" IsSortable="False" ItemsSource="{Binding DaySegments}" DisplayMemberPath="DisplayStart" Width="50"
                        <telerik:GridViewComboBoxColumn.Header> 
                            <StackPanel> 
                                <TextBlock Text="Sun 2/28 Start"  
                                           TextWrapping="Wrap"/> 
                            </StackPanel> 
                        </telerik:GridViewComboBoxColumn.Header> 
                    </telerik:GridViewComboBoxColumn> 
                    <telerik:GridViewComboBoxColumn DataMemberBinding="{Binding SundayEndDaySegment, Mode=TwoWay}" IsSortable="False" ItemsSource="{Binding DaySegments}" DisplayMemberPath="DisplayEnd" Width="50"
                        <telerik:GridViewComboBoxColumn.Header> 
                            <StackPanel> 
                                <TextBlock Text="Sun 2/28 End"  
                                           TextWrapping="Wrap"/> 
                            </StackPanel> 
                        </telerik:GridViewComboBoxColumn.Header> 
                    </telerik:GridViewComboBoxColumn> 
                    <telerik:GridViewComboBoxColumn DataMemberBinding="{Binding MondayStartDaySegment, Mode=TwoWay}" IsSortable="False" ItemsSource="{Binding DaySegments}" DisplayMemberPath="DisplayStart" Width="50"
                        <telerik:GridViewComboBoxColumn.Header> 
                            <StackPanel> 
                                <TextBlock Text="Mon 3/1 Start"  
                                           TextWrapping="Wrap"/> 
                            </StackPanel> 
                        </telerik:GridViewComboBoxColumn.Header> 
                    </telerik:GridViewComboBoxColumn> 
                </telerik:RadGridView.Columns> 
            </telerik:RadGridView> 

    See the pattern?  How do I go about making the date part of the Text in the TextBlock bound to something?  I was thinking I'd just have a property hanging off the ViewModel - one for each day and each Start/End column called MondayStartHeader, MondayEndHeader, etc. That won't work though because the grid is bound to a collection of objects on the ViewModel.

    Thanks.


  2. Answer
    Rossen Hristov
    Admin
    Rossen Hristov avatar
    2477 posts

    Posted 23 Mar 2010 Link to this post

    Hi Rick Glos,

    First you will need to create an IValueConverter to use in the Binding. This converter will receive a DataTime value and will return a string such as string.Format("{0:ddd M/d} Start"). Or with "End" depending on the value.

    Alternatively you can create two separate converters -- one that will append Start and one that will append End.

    Yet another way is to create two TextBlocks standing next to each other with no space. The first can have a Binding to the date and the second can show the fixed text, i.e. Start or End. There are multiple ways to go about this thing.

    Now, let us see how to set the Binding. We will attach to the loaded event of the TextBlock and set its Binding to the property of the ViewModel.

    I have prepared a sample project. You can use it as a base and modify it to match your exact needs and architecture. Here are the important parts:

    <Window x:Class="TicketID_292780_ColumnHeaderBinding.Window1"
        xmlns:local="clr-namespace:TicketID_292780_ColumnHeaderBinding"
        Title="Window1" Height="700" Width="600">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <telerik:RadGridView Name="clubsGrid"
                                 Grid.Row="0"
                                 AutoGenerateColumns="False"
                                 ItemsSource="{Binding Clubs}">
                <telerik:RadGridView.Columns>
                    <telerik:GridViewDataColumn DataMemberBinding="{Binding Name}">
                        <telerik:GridViewDataColumn.Header>
                            <StackPanel>
                                <TextBlock Loaded="TextBlock_Loaded"
                                           TextWrapping="Wrap"/>
                            </StackPanel>
                        </telerik:GridViewDataColumn.Header>
                    </telerik:GridViewDataColumn>
                </telerik:RadGridView.Columns>
            </telerik:RadGridView>
            <Button Grid.Row="1" Content="Change Column 1 Header" Click="Button_Click"/>
        </Grid>
    </Window>

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ComponentModel;
    using System.Collections.ObjectModel;
     
    namespace TicketID_292780_ColumnHeaderBinding
    {
        public class MyViewModel : INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
     
            private ObservableCollection<Club> clubs = Club.GetClubs();
            private DateTime column1Header;
     
            public ObservableCollection<Club> Clubs
            {
                get { return this.clubs; }
            }
     
            public DateTime Column1Header
            {
                get { return this.column1Header; }
                set
                {
                    if (value != this.column1Header)
                    {
                        this.column1Header = value;
                        this.OnPropertyChanged("Column1Header");
                    }
                }
            }
     
            protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
            {
                PropertyChangedEventHandler handler = this.PropertyChanged;
                if (handler != null)
                {
                    handler(this, args);
                }
            }
     
            private void OnPropertyChanged(string propertyName)
            {
                this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
            }
        }
    }

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows.Data;
     
    namespace TicketID_292780_ColumnHeaderBinding
    {
        public class StartConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                if (value != null && value is DateTime)
                {
                    return string.Format("{0: ddd M/d} Start", (DateTime)value);
                }
     
                return null;
            }
     
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                throw new NotImplementedException();
            }
        }
    }

    using System.Windows;
    using Telerik.Windows.Controls;
    using Telerik.Windows.Data;
    using System;
    using System.Windows.Controls;
    using System.Windows.Data;
     
    namespace TicketID_292780_ColumnHeaderBinding
    {
        /// <summary>
        /// Interaction logic for Window1.xaml
        /// </summary>
        public partial class Window1 : Window
        {
            MyViewModel vm = new MyViewModel();
     
            public Window1()
            {
                InitializeComponent();
     
                this.vm.Column1Header = DateTime.Now;
                this.clubsGrid.DataContext = this.vm;
            }
     
            private void Button_Click(object sender, RoutedEventArgs e)
            {
                this.vm.Column1Header = this.vm.Column1Header.AddDays(7);
            }
     
            private void TextBlock_Loaded(object sender, RoutedEventArgs e)
            {
                TextBlock txt = (TextBlock)sender;
                txt.DataContext = this.vm;
                Binding b = new Binding("Column1Header"){Converter = new StartConverter()};
                txt.SetBinding(TextBlock.TextProperty, b);
            }
        }
    }

    You can find the whole project attached. When you click the button you will see how the header of the column changes.

    I hope this helps.

    Greetings,
    Ross
    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.
  3. Rick Glos
    Rick Glos avatar
    70 posts
    Member since:
    Mar 2009

    Posted 24 Mar 2010 Link to this post

    Thanks Ross.  Very helpful. 

    Not only does it solve this problem I was having above, but this solution also works great for setting Grand Total Footer values for the Silverlight version of the RadGirdView I'm using in another application.  I was setting the values programmitacilly instead of binding them.  Now I see how I can reset the DataContext on the TextBox to the parent ViewModel.  Excellent.
  4. Rick Glos
    Rick Glos avatar
    70 posts
    Member since:
    Mar 2009

    Posted 25 Mar 2010 Link to this post

    In case this helps someone else, here's how I actually implemented this.

    Instead of using converters, I put the property path I wanted to bind the header to in the Tag property of the TextBlock.

    <TextBlock Tag="SundayStartHeaderText" TextWrapping="Wrap" Loaded="HeaderTextBlock_Loaded"/> 

    Then in the event handler, just used that to set the binding of the TextBlock

            private void HeaderTextBlock_Loaded(object sender, RoutedEventArgs e) 
            { 
                TextBlock textBlock = sender as TextBlock; 
                if (textBlock != null
                { 
                    // Set the DataContext to the ViewModel 
                    textBlock.DataContext = this._viewModel; 
                    // The tag property contains the property path we want to bind to 
                    string propertyPath = textBlock.Tag.ToString(); 
                    Binding binding = new Binding(propertyPath); 
                    textBlock.SetBinding(TextBlock.TextProperty, binding); 
                } 
            } 

    Thanks again for the input Ross.
  5. M
    M avatar
    5 posts
    Member since:
    Mar 2012

    Posted 27 Mar 2012 Link to this post

    It works but in my opinion this is not a real elegant solution. Since you are talking about a viemodel I guess you are using mvvm like me and in mvvm you really don't want events or whatever in code behind.
    I found a xaml only solution that also works. I don't use an event but directly bind to a property in the first row (Projects[0].Days) of the object that I am binding. If there is no data you will get a binding error and the header will stay empty but in my case that is exactly what I want.
    Here is the code:
    <telerik:GridViewDataColumn.Header>
        <TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=telerik:GridViewDataControl}, Path=DataContext.Projects[0].Days[0].Date}"/>
    </telerik:GridViewDataColumn.Header>

    You might want to add a converter to format the date to any format you like.
  6. M
    M avatar
    5 posts
    Member since:
    Mar 2012

    Posted 27 Mar 2012 Link to this post

    There is even a better solution without converter but with a stringformat so you really have zero code behind:

    <telerik:GridViewDataColumn.Header>
        <TextBlock>
            <TextBlock.Text>
                <Binding RelativeSource="{RelativeSource AncestorType=telerik:GridViewDataControl}" Path="DataContext.Projects[0].Days[0].Date" StringFormat="{}{0:D}"/>
            </TextBlock.Text>
        </TextBlock>
    </telerik:GridViewDataColumn.Header>
  7. Rohan
    Rohan avatar
    1 posts
    Member since:
    Sep 2011

    Posted 16 Jul 2014 in reply to M Link to this post

    Thank you for the solution. Worked like a charm!
Back to Top