Powered by MSDN

US - English
NEW! Silverlight 5 is available Learn More

Custom DataGrid.Header - BUG in RC0. RSS

30 replies

Last post Oct 28, 2008 03:01 PM by joer00

(0)
  • sladapter

    sladapter

    All-Star

    43609 Points

    7910 Posts

    Re: Custom DataGrid.Header - BUG in RC0.

    Sep 29, 2008 04:16 PM | LINK

     Option1:

    Hookup button event in the template. But you have to define the header template in UserControl.Resources section instead of Application.Resources.

    Optoin2:

    Write a ListHeader UserControl, wrap up all the controls layout and event handler in that UserControl. Then use that UserControl in your DataGridHeaderTemplate. This way you can put the header template in the Application.Resouces.

     

     

    Sally Xu
    Software Engineer
    Aprimo, Inc

    Please remember to mark the replies as answers if they answered your question
  • Bartlomiej

    Bartlomiej

    Member

    68 Points

    45 Posts

    Re: Custom DataGrid.Header - BUG in RC0.

    Sep 29, 2008 05:58 PM | LINK

    Thank you. I think, I don't understand what you exactly mean by "wrap up all the controls layout and events ..". Even if I move all layouts and events of the ListHeader to the UserControl (into separate control in XAML + related code), I will not be able to set my own properties of ListHeader after settin the HeaderStyle. The main problem is that HeaderStyle generates its own instance of the ListHeader and there is no way to update its properties after setting the style. There is no way to set up those properties before setting the HeaderStyle, too. Or ... I don't know how to do it.

    B.

  • sladapter

    sladapter

    All-Star

    43609 Points

    7910 Posts

    Re: Custom DataGrid.Header - BUG in RC0.

    Sep 29, 2008 07:21 PM | LINK

    Here is the code I have tested:

    1) Create a GridHeader custom control

        [TemplatePart(Name = GridHeader.HeaderTextElement, Type = typeof(FrameworkElement))]  
        [TemplatePart(Name = GridHeader.FilterTextElement, Type = typeof(TextBox))]    
        public class GridHeader : Control
        {
           
            protected const string HeaderTextElement = "HeaderText";        
            protected const string FilterTextElement = "FilerText";
          
            Button header;
            TextBox filterText;

            #region Constructor
            public GridHeader()
            {
                this.DefaultStyleKey = typeof(GridHeader);
            }
            #endregion

            #region HeaderText
            /// <summary>
            /// Identifies the HeaderText dependency property.
            /// </summary>
            public static readonly DependencyProperty HeaderTextProperty = DependencyProperty.Register("HeaderText", typeof(string), typeof(GridHeader), null);

            /// <summary>
            /// Gets or sets the HeaderText possible Value of the int object.
            /// </summary>
            public string HeaderText
            {
                get { return (string)GetValue(HeaderTextProperty); }
                set { SetValue(HeaderTextProperty, value); }
            }
            #endregion HeaderText

            #region FilterText

            /// <summary>
            /// Identifies the FilterText dependency property.
            /// </summary>
            public static readonly DependencyProperty FilterTextProperty = DependencyProperty.Register("FilterText", typeof(string), typeof(GridHeader), null);

            /// <summary>
            /// Gets or sets the FilterText possible Value of the string object.
            /// </summary>
            public string FilterText
            {
                get { return (string)GetValue(FilterTextProperty); }
                set { SetValue(FilterTextProperty, value); }
            }
            #endregion FilterText
                    
            public override void OnApplyTemplate()
            {
                base.OnApplyTemplate();           
                this.filterText = this.GetTemplateChild("FilterText") as TextBox;
                this.header = this.GetTemplateChild("HeaderText") as Button;          
                if(this.filterText != null)
                    this.filterText.LostFocus += new RoutedEventHandler(filterText_LostFocus);
                if(this.header != null)
                    this.header.Click += new RoutedEventHandler(header_Click);
            }
          
            void header_Click(object sender, RoutedEventArgs e)
            {
                
            }

            void filterText_LostFocus(object sender, RoutedEventArgs e)
            {
                
            }
        }

     Put the following Template in the themes/generic.xaml file

     <Style TargetType="controls:GridHeader">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="controls:GridHeader">
                        <StackPanel x:Name="LayoutRoot" Margin="3" Background="{TemplateBinding Background}">
                            <StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
                                <TextBlock Text="{TemplateBinding HeaderText}" />   <!-- if you want default Sort -->

                                OR

                               <Button Content="{TemplateBinding HeaderText}" />   <!-- if you want to implement your own Sort -->                         

                                <!-- Your can add the Sort indicator image here -->
                            </StackPanel>                      
                            <TextBox Margin="2" x:Name="FilterTextBox" Text="{TemplateBinding FilterText}"/>
                        </StackPanel>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

     Define Grid-header Style in the app.xaml:

    <Application.Resources>
        
            <Style x:Key="grid-header" TargetType="primitives:DataGridColumnHeader">
                <Setter Property="VerticalAlignment" Value="Center" />
                <Setter Property="HorizontalAlignment" Value="Stretch" />
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate>
                            <my:GridHeader HeaderText="{TemplateBinding Content}"  />
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </Application.Resources>

     In your code:

    col.Header = "YourColHeaderText"; 

    col.HeaderStyle =  col.HeaderStyle = Application.Current.Resources["grid-header"] as Style;

     

    What I have found for sorting in RC0 DataGrid works differently from beta2:

    If you want to use Default Sort function, either use default header or define your own header template but do not put a button in the template. This way, when you click the header, the sort would work. If you put a button in the Header template, click that button won't fire default sort.  

    If you defined your own header template, the sort indicator image no longer shows up even sort still work.

     

     

     

     

     

    Sally Xu
    Software Engineer
    Aprimo, Inc

    Please remember to mark the replies as answers if they answered your question
  • Bartlomiej

    Bartlomiej

    Member

    68 Points

    45 Posts

    Re: Custom DataGrid.Header - BUG in RC0.

    Sep 30, 2008 10:41 AM | LINK

    Thank you very much! The code is working. I really appreciate your effort!

    But I think this approach doesn't solve my problem. The final code loks like below:

    public Page()
            {
                InitializeComponent();
    
                DataGridTextColumn col1 = new DataGridTextColumn();
                DataGridTextColumn col2 = new DataGridTextColumn();
                DataGridTextColumn col3 = new DataGridTextColumn();
    
                col1.HeaderStyle = Application.Current.Resources["grid-header"] as Style;
                col2.HeaderStyle = Application.Current.Resources["grid-header"] as Style;
                col3.HeaderStyle = Application.Current.Resources["grid-header"] as Style;
    
                theDataGrid.Columns.Add(col1);
                theDataGrid.Columns.Add(col2);
                theDataGrid.Columns.Add(col3);
    
            }

    Everything works OK with Generic.xaml and style in App.xaml. But I still can't set up properties of the GridHeader BEFORE or AFTER setting the Column Style. I supposed something like that:

     
    col1.Header.FilterText = .... // in order to take the last column text from db
    col1.FilterLostFocus += .... // in order to store the last column text into db
    ...
    
    col2.Header.FilterText = ...
    col2.FilterLostFocus += ....
    ...
    
     

    The "business" problem is that the client wants to store in db the width of the column after changing the size, last "sort order" on the column after changing the sort order and finally the last filter text put into the column header (maby there will be more "needs" in the future ...). 

    B.

     

  • sladapter

    sladapter

    All-Star

    43609 Points

    7910 Posts

    Re: Custom DataGrid.Header - BUG in RC0.

    Sep 30, 2008 04:13 PM | LINK

    Hi,  So far I can think of is this: Move the grid_header style (Template) to the UserControl.Resources, then hook up Header_Loaded Event.

    <UserControl.Resources>
        
            <Style x:Key="grid-header" TargetType="primitives:DataGridColumnHeader">
                <Setter Property="VerticalAlignment" Value="Center" />
                <Setter Property="HorizontalAlignment" Value="Stretch" />
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate>
                            <my:GridHeader HeaderText="{TemplateBinding Content}"  Loaded="Header_Loaded" />
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </UserControl.Resources> 

     

    private void GridHeader_Loaded(object sender, RoutedEventArgs e)
            {
                GridHeader h = sender as GridHeader;

               // Check h.HeaderText to know which col you are on, based on that, set the FilterText, and Hookup Event handler (make sure you add the public event in the custom control)
                h.FilterText = "YourFilterTest";

                h.FilterLostFocus += ....

             }  

    Well, I still like the way in beta2 better where we can set the control to the column.Header.  Now it's really hard to get access to the controls defined in template ( a cross-board issue for getting access to the controls in template in Silverlight).

     

    Sally Xu
    Software Engineer
    Aprimo, Inc

    Please remember to mark the replies as answers if they answered your question
  • Bartlomiej

    Bartlomiej

    Member

    68 Points

    45 Posts

    Re: Custom DataGrid.Header - BUG in RC0.

    Oct 01, 2008 03:25 PM | LINK

    Good idea. The problem is that this <UserControl.Resources> in my case shoul be inside the XAML control of DataGridTemplateColumn. I don't know yet how to create the XAML file for DataGridColumn type because at present each my DataGridTemplateColumn is created in code, without XAML. On the other hand, a simple creation of the XAML control "myDataGridTemplateColumn" doesn't work, because UserControl type cann't be added as a Column: "DataGrid.Columns.Add(myDataGridTemplateColumn)".  And (of course...) the base class of "myDataGridTemplateColumn" can't be a DataGridColumn type (must be "UserControl"). Thank you for all your help!

    B.

  • sladapter

    sladapter

    All-Star

    43609 Points

    7910 Posts

    Re: Custom DataGrid.Header - BUG in RC0.

    Oct 01, 2008 03:43 PM | LINK

    The more I play with this, the more I think the design change for how to set col header in RC0 is flawed, unless I haven't found the correct way to do this.

    The DataGridColumn.Header property is still Object type (just like in beta2), but you can not set anything other than the header text string to it which I think is wrong.

    I even wrote a custom Header that inherited from DataGridColumnHeader object, but there is no way for me to set it to the DataGridColumnHeader._headerCell to replace the original DataGridColumnHeader object which is set internally.

    I wrote a request in another thread asking this question. Hope someone from Microsoft can answer it.

    You can follow that thread: http://silverlight.net/forums/t/30731.aspx

     

     


    Sally Xu
    Software Engineer
    Aprimo, Inc

    Please remember to mark the replies as answers if they answered your question
  • Bartlomiej

    Bartlomiej

    Member

    68 Points

    45 Posts

    Re: Custom DataGrid.Header - BUG in RC0.

    Oct 01, 2008 04:06 PM | LINK

    It's nice to hear somebody supports me. I've written the "suggestion" in another thread, too (http://silverlight.net/forums/p/31442/99361.aspx) but there is no answer. After your suggestion of "Loaded" event I've also tried to find "Parent" of my GridHeader in order to find the DataGridHeader, but the code:

     void myListHeader_Loaded(object sender, RoutedEventArgs e)
            {
               object obj = ((myListHeader)sender).Parent;
            }

       gives null.

    My next idea was to find the myHeader inside the HeaderStyle.Setters collection or sth, but with no effect, too.

    In my opinion, the present type of using DataGrid.Header is completly useless when you want to make headers from code.

    Thanks for the link. I'm interested in every information about this subject.

    B.

  • sladapter

    sladapter

    All-Star

    43609 Points

    7910 Posts

    Re: Custom DataGrid.Header - BUG in RC0.

    Oct 02, 2008 01:29 PM | LINK

    For finding Parent for any control, you now need to use VisualTreeHelper.GetParent function instead of control.Parent property. Try that and see if you get the parent of the header.

    Sally Xu
    Software Engineer
    Aprimo, Inc

    Please remember to mark the replies as answers if they answered your question
  • Bartlomiej

    Bartlomiej

    Member

    68 Points

    45 Posts

    Re: Custom DataGrid.Header - BUG in RC0.

    Oct 02, 2008 03:08 PM | LINK

    Thanks for advice. Here is the tree of DataGridColumnHeader parents:

    DataGridColumnHeader obj = (DataGridColumnHeader)VisualTreeHelper.GetParent(this);
    DataGridColumnHeadersPresenter obj1 = (DataGridColumnHeadersPresenter)VisualTreeHelper.GetParent(obj);
    Grid obj2 = (Grid)VisualTreeHelper.GetParent(obj1);
    Border obj3 = (Border)VisualTreeHelper.GetParent(obj2);
    DataGrid obj4 = (DataGrid)VisualTreeHelper.GetParent(obj3);
    The problem is that the DataGridColumn of that header takes place in property "OwningColumn" (of the obj in this example) which is "Non-Public member".
    B.