Skip to main content

Microsoft Silverlight

Answered Question Custom DataGrid.Header - BUG in RC0.RSS Feed

(0)

Bartlomiej
Bartlomiej

Member

Member

34 points

30 Posts

Custom DataGrid.Header - BUG in RC0.

This code works fine: 

DataGridTextColumn dgtc = new DataGridTextColumn();
dgtc.Header = "AAA"; // OK!!!
theDataGrid.Columns.Add(dgtc);

And this code can be compiled, but after running generates an ERROR (VS2008 Standard SP1, IE 7.0):

DataGridTextColumn dgtc = new DataGridTextColumn();
TextBlock tb = new TextBlock();
tb.Text = "AAA";
dgtc.Header = tb; // A BUG !!!
theDataGrid.Columns.Add(dgtc);

Any other custom header doesn't work, too. This bug destroyed all my project based on big number of DataGrids with custom DataGridTemplateColumns with custom Headers. This worked very nice in Beta2 !

B.

 

 

sladapter
sladapter

All-Star

All-Star

17439 points

3,172 Posts

Re: Custom DataGrid.Header - BUG in RC0.

 I found the same thing. How do we set the Header to a control now?

 

sladapter
Software Engineer
Aprimo, Inc

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

emikelsoft
emikelsoft

Member

Member

4 points

3 Posts

Re: Custom DataGrid.Header - BUG in RC0.

I found the same thing too, in additon binding the Header to a static resource is also not possible:

<data:DataGridTextColumn
Header="{Binding Field_Name, Source={StaticResource losTransport}}"
Binding="{Binding Name}"
/>

It raises AG_E_PARSER_BAD_PROPERTY_VALUE [Line: 82 Position: 13]

 

 

Tregarick
Tregarick

Member

Member

13 points

24 Posts

Re: Custom DataGrid.Header - BUG in RC0.

I have found the same issue and its a huge problem I have lots of checkbox columns and I use the header template to have a check all check box.  Used to work perfectly in Beta 2 but now if I use the XAML below I either get an exception in the renderer or my Visual Studio totally freezes and CPU maxes out like its got stuck in a loop I have had to remove all header template in my grids just to get it working.

<data:DataGridTemplateColumn>

<data:DataGridTemplateColumn.Header>

<CheckBox HorizontalAlignment="Center" x:Name="chkSelectAll" Click="chkSelectAll_Click" />

</data:DataGridTemplateColumn.Header>

 

 

Yi-Lun Luo - MSFT
Yi-Lun L...

All-Star

All-Star

25052 points

2,747 Posts

Answered Question

Re: Custom DataGrid.Header - BUG in RC0.

 

Hello, DataGrid is an SDK Control (not in the runtime, and is not written by the Silverlight team. It's written by the UIFX team, AKA the team responsible for Windows Forms and ASP.NET). Please understand the time was limited, so not every team made the break changes list. But I think they will include the break changes in RTW documentation. Anyway, here're the break changes for DataGrid I got internally (removed any internal links and confidential information):

  • DisplayMemberBinding renamed to Binding.
  • The ControlTemplate contract changed to use VisualState.
  • IEditableObject moved to System.ComponentModel.
  • SelectionChanged event now uses SelectionChangedEventHandler instead of EventHandler.
  • DataGridHeaders enum renamed to DataGridHeadersVisibility.
  • The PropertyInfo of DataGridAutoGeneratingColumnEventArgs was changed to PropertyName and PropertyType.

    Beta 2
    string name = e.Property.Name;
    Type type = e.Property.Type;

    RC
    string name = e.PropertyName;
    Type type = e.PropertyType;

 

  • GenerateElement and GenerateEditingElement now provide the containing cell

    Beta 2
    protected override FrameworkElement GenerateEditingElement(object dataItem)
    {
    }
    protected override FrameworkElement GenerateElement(object dataItem)
    {
    }

    RC
    protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
    {
    }
    protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
    {
    }

 

  • The following modifications were made to the DataGridColumnReorderingEventArgs:
    Beta 2:
    public object DragIndicatorContent { get; set}
    public FrameworkElement DropLocationIndicator { get; set}
    RC:
    public Control DragIndicator { get; set}
    public Control DropLocationIndicator { get; set}

 

  • CancelingEdit, CommittingEdit, and DataError events removed.
  • DataGridColumn.Header no longer supports visuals.
  • Explaination: Visuals cannot be duplicated so they cannot be used for the Header property due to column reordering. Use HeaderStyle instead:
    <data:DataGridTextColumn.HeaderStyle>
    <Style TargetType="dataprimitives:DataGridColumnHeader">
    <Setter Property="Template">
    <Setter.Value>
    <ControlTemplate>
    <Button Content="{TemplateBinding Content}" />
    </ControlTemplate>
    </Setter.Value>
    </Setter>
    </Style>
    </data:DataGridTextColumn.HeaderStyle>
  • Root element of DataGridRow changed from Grid to DataGridFrozenGrid.
  • Gridline renamed to GridLine.
  • These types moved from the System.Windows.Controls namespace to the System.Windows.Controls.Primitives namespace:
    DataGridCellsPresenter
    DataGridColumnHeadersPresenter
    DataGridDetailsPresenter
    DataGridRowsPresenter
    DataGridColumnHeader
    DataGridRowHeader
  • DataGridCheckBoxColumn.Content was removed.

Note now you have to use HeaderStyle if you want anything other than plain text.

shanaolanxing - I'll transfer to the Windows Azure team, and will have limited time to participate in the Silverlight forum. Apologize if I don't answer your questions in time.

Bartlomiej
Bartlomiej

Member

Member

34 points

30 Posts

Re: Custom DataGrid.Header - BUG in RC0.

Thanks for extending the breaking changes list.

As for the ColumnHeader style ... I've tried to use the example into App.xaml:

<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
             xmlns:dataprimitives="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data" 
             x:Class="GridHeaderTest.App"
             >
    <Application.Resources>

        <!--Style for ColumnHeader -->
        <Style x:Key="ColumnHeaderStyle"  TargetType="dataprimitives:DataGridColumnHeader">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate>
                        <Button Content="Button" />
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

    </Application.Resources>
</Application>
 
But after run (F5) I'm gettin an error: "Invalid attribute value dataprimitives:DataGridColumnHeader for property TargetType. [Line: 9 Position: 54]"
B. 
 

sladapter
sladapter

All-Star

All-Star

17439 points

3,172 Posts

Re: Custom DataGrid.Header - BUG in RC0.

 DataGridColumnHeader is in System.Windows.Controls.Primitives name space:

 xmlns:dataprimitives="clr-namespace:System.Windows.Controls.Primitives;assembly=System.Windows.Controls.Data"

 

sladapter
Software Engineer
Aprimo, Inc

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

Bartlomiej
Bartlomiej

Member

Member

34 points

30 Posts

Re: Custom DataGrid.Header - BUG in RC0.

Of course! It works. Thank you.

But my problem still exists. How can I comppose my custom header IN CODE after setting the Style ? The header depends on some parameters. In Beta2 I could do something like that:

DataGridTemplateColumn _dgtc = new DataGridTemplateColumn();
myListHeader lh = new myListHeader(); // myListHeader contains the HyperlinkButton

string myHeaderName = "myHeaderName1"
lh.LayoutRoot.Height = 18;
lh.theHyperlinkButton.FontSize = 15;
lh.theHyperlinkButton.FontFamily = new FontFamily("Arial");
lh.theHyperlinkButton.Foreground = new SolidColorBrush(Colors.Blue);
lh.theHyperlinkButton.Content = myHeaderName;

_dgtc.Header = lh;

It was clear and simple (especially the last line). How can I achieve it in RC?  Now I can only set the column header style: _dgtc.HeaderStyle = Application.Current.Resources["ColumnHeaderStyle"] as Style;

But each my column header is not only the "style" - it can be quite complex (have custom properties, events, ...). I' d like to believe, that I will not have to build custom Header ("myListHeader") by concatenating it from pieces of XAML strings (simmilarly like CellTemplate/CellEditingTemplate for DataGridTemplateColumn). Please give us back the simplicity of building column header like in Beta2 !  

B.

 

sladapter
sladapter

All-Star

All-Star

17439 points

3,172 Posts

Re: Custom DataGrid.Header - BUG in RC0.

If you have a Template defined in the Style:

<Style x:Key="ColumnHeaderStyle" TargetType="primitives:DataGridColumnHeader">              
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate>                                           
                            <Button Margin="5" Content="{TemplateBinding Content}"  HorizontalAlignment="Center" />                      
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>      

 

 _dgtc.HeaderStyle = Application.Current.Resources["ColumnHeaderStyle"] as Style;

 _dgtc.Header = "YourHeaderText";  // This header text is bind to the Content property of the DataGridColumnHeader, and will be set to your Button.Content property


sladapter
Software Engineer
Aprimo, Inc

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

Bartlomiej
Bartlomiej

Member

Member

34 points

30 Posts

Re: Custom DataGrid.Header - BUG in RC0.

It works. Nice.... Thanks. But my case is more complex. This is a piece of code:

The custom control "myListHeader":

<UserControl
    x:Class="GridHeaderTest.myListHeader"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    >
   
    <StackPanel x:Name="LayoutRoot" Background="Navy" Orientation="Vertical" >
        <HyperlinkButton x:Name="mySortHyperlinkButton" /  >
        <TextBox x:Name="myFilterTextBox" />
    </StackPanel>
   
</UserControl>

And how to achieve this in RC0 (possible in Beta2):

DataGridTextColumn _dgtc = new DataGridTextColumn();
myListHeader lh = new myListHeader();
int parameter = 0;

lh.mySortHyperlinkButton.Click += new RoutedEventHandler(mySortHyperlinkButton_Click);
lh.myFilterTextBox.LostFocus += new RoutedEventHandler(myFilterTextBox_LostFocus);
lh.SizeChanged += (s, e)  =>
            {
                parameter  += 1; // for example
            };

_dgtc.Header = lh; 

void myFilterTextBox_LostFocus(object sender, RoutedEventArgs e)
{
   // some code
}

void mySortHyperlinkButton_Click(object sender, RoutedEventArgs e)
{
   // some code 
}

In other words, how to set PARTICULAR INSTANCE of "myListHeader" ("lh" in this example) with its events (and sometimes parameters sent to the methods serving those events), into THIS PARTICULAR HeaderStyle. I must mention, that columns are created dynamically only (not in xaml).

B. 

 

 

sladapter
sladapter

All-Star

All-Star

17439 points

3,172 Posts

Re: Custom DataGrid.Header - BUG in RC0.

 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.

 

 

sladapter
Software Engineer
Aprimo, Inc

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

Bartlomiej
Bartlomiej

Member

Member

34 points

30 Posts

Re: Custom DataGrid.Header - BUG in RC0.

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

All-Star

17439 points

3,172 Posts

Re: Custom DataGrid.Header - BUG in RC0.

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.

 

 

 

 

 

sladapter
Software Engineer
Aprimo, Inc

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

Bartlomiej
Bartlomiej

Member

Member

34 points

30 Posts

Re: Custom DataGrid.Header - BUG in RC0.

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

All-Star

17439 points

3,172 Posts

Answered Question

Re: Custom DataGrid.Header - BUG in RC0.

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

 

sladapter
Software Engineer
Aprimo, Inc

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

Bartlomiej
Bartlomiej

Member

Member

34 points

30 Posts

Re: Custom DataGrid.Header - BUG in RC0.

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

All-Star

17439 points

3,172 Posts

Re: Custom DataGrid.Header - BUG in RC0.

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

 

 


sladapter
Software Engineer
Aprimo, Inc

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

Bartlomiej
Bartlomiej

Member

Member

34 points

30 Posts

Re: Custom DataGrid.Header - BUG in RC0.

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

All-Star

17439 points

3,172 Posts

Re: Custom DataGrid.Header - BUG in RC0.

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.

sladapter
Software Engineer
Aprimo, Inc

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

Bartlomiej
Bartlomiej

Member

Member

34 points

30 Posts

Re: Custom DataGrid.Header - BUG in RC0.

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. 

Bartlomiej
Bartlomiej

Member

Member

34 points

30 Posts

Re: Custom DataGrid.Header - BUG in RC0.

Not easy, but working solution is:

 public partial class myDataGridTemplateColumn : DataGridTemplateColumn
    {
        public myListHeader myLh;
    }


    public partial class myListHeader : UserControl
    {
        public myListHeader()
        {
            InitializeComponent();
            Loaded += new RoutedEventHandler(myListHeader_Loaded);
        }

         void myListHeader_Loaded(object sender, RoutedEventArgs e)
        {
            myListHeader lh = (myListHeader)sender;
            DataGridColumnHeader dgch = (DataGridColumnHeader)VisualTreeHelper.GetParent(this);
            DataGridColumnHeadersPresenter dgchp = (DataGridColumnHeadersPresenter)VisualTreeHelper.GetParent(dgch);
            Grid g = (Grid)VisualTreeHelper.GetParent(dgchp);
            Border b = (Border)VisualTreeHelper.GetParent(g);
            DataGrid dg = (DataGrid)VisualTreeHelper.GetParent(b);

            List dgtcList = new List();
            foreach (myDataGridTemplateColumn dgtc in dg.Columns)
            {
                dgtcList.Add(dgtc);
            }

            myDataGridTemplateColumn dgtc1 = dgtcList.FirstOrDefault(c => c.myLh == null) as myDataGridTemplateColumn;

            if (dgtc1 != null)
            {
                dgtc1.myLh = lh;  // that is what I wanted
                // ... some DataGrid additional event which tells the main application, that the header is loaded
            }
        }
    }

DataGrid must have myDataGridTemplateColumn columns instead of standard DataGridTemplateColumn.

The result is that we finaly have additional property of the myDataGridTemplateColumn which represents the myListHeader as in Beta2.

The "Loaded" event of the ListHeader was a good advice and inspired me. Thanks.

B.

chrisp_68
chrisp_68

Member

Member

18 points

38 Posts

Re: Custom DataGrid.Header - BUG in RC0.

Hi Bartlomeij

I need to do exactly the same as you and posted a question here

http://silverlight.net/forums/t/39909.aspx

and 

 http://silverlight.net/forums/t/40390.aspx

before seing this post.

Is this really the only and best way to add filtering controls to a column header?

I'm new to silverlight and at this point am tempted to go back to my old asp.net ways and drop silverlight, just to get the job done, but really dont want to if possible. I can't seem to get your example above working. Do you have a simple app that you can post that demonstrates this concept? I'd really appreciate it if you do.

Thanks

Christian

Bartlomiej
Bartlomiej

Member

Member

34 points

30 Posts

Re: Custom DataGrid.Header - BUG in RC0.

Hi.

Unfortunately, I have found no other better or simpler solution for this "little" problem. If "sladapter" didn't find it, that means that there is no such one ... Smile

As for my above workarround I had to do it in this way, because I had to build columns by code. If your application is based on xaml, maby there are some other simpler ways. After finding my "way" I didn't look for another - this is enough for me. Below is the whole, working test application which shows my concept. Comments are inside the code. First you have to make your own header user control (myListHeader). Then you must extend the DataGridTemplateColumn (myDataGridTemplateColumn has one more property "myLH" - this is the header, but accessable from code) and you must extend the DataGrid (myDataGrid has additional property "HeadersLoaded" which implements INotifyPropertyChanged). Finally you must fill the "myLH" property for all columns and change the HeaderLoaded property of the myDatGrid. The project's name is "GridHeaderTest".

 Help yourself ...

 App.xaml (I like store styles in App.xaml Application.Resources):

 

  <Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
             xmlns:primitives="clr-namespace:System.Windows.Controls.Primitives;assembly=System.Windows.Controls.Data" 
             x:Class="GridHeaderTest.App"
             xmlns:my1="clr-namespace:GridHeaderTest;assembly=GridHeaderTest"
             >
    <Application.Resources>

        <!--Style for ColumnHeader -->
        <Style x:Key="grid-header"  TargetType="primitives:DataGridColumnHeader">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate>
                        <my1:myListHeader />
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

    </Application.Resources>
</Application>
  

Page.xaml (be carefull: must be "<my1: myDataGrid ...>"  instead of "<data:DataGrid ... >"

 

<UserControl 
    x:Class="GridHeaderTest.Page"
    xmlns:basics="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"  
    xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"  
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="400" Height="300"
    xmlns:my1="clr-namespace:GridHeaderTest;assembly=GridHeaderTest"
    >
    <Grid x:Name="LayoutRoot" Background="White">
        <my1:myDataGrid x:Name="theDataGrid" />
    </Grid>
    
</UserControl>

 

Page.xaml.cs (new definitions for myDataGridTemplateColumn and myDataGrid and the main code):

  

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel;

namespace GridHeaderTest
{

    // we have to extend the standard DataGridTemplateColumn to have an instance of its custom header made by the style after setting the style
    public partial class myDataGridTemplateColumn : DataGridTemplateColumn
    {
        public myListHeader myLh;
    }


    // we must add an event which tell the DataGrid that all columns have headers
    public partial class myDataGrid : DataGrid, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public bool HeadersLoaded
        {
            set
            {
                if (PropertyChanged != null)
                    this.PropertyChanged(this, new PropertyChangedEventArgs("HeadersLoaded"));
            }
        }

        public myDataGrid()
        {
            HeadersLoaded = false;
        }

    }

    
    public partial class Page : UserControl
    {

        public Page()
        {

            InitializeComponent();

            myDataGridTemplateColumn col1 = new myDataGridTemplateColumn();
            myDataGridTemplateColumn col2 = new myDataGridTemplateColumn();
            myDataGridTemplateColumn col3 = new myDataGridTemplateColumn();
            myDataGridTemplateColumn col4 = new myDataGridTemplateColumn();

            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;
            col4.HeaderStyle = Application.Current.Resources["grid-header"] as Style;

            theDataGrid.Columns.Add(col1);
            theDataGrid.Columns.Add(col2);
            theDataGrid.Columns.Add(col3);
            theDataGrid.Columns.Add(col4);

            // Be carefull, this is not the end.
            // The problem is, that after above code, my new "myLh" property is not set (is null) !!!
            // And this code:
            // myListHeader lh = col1.myLh;
            // gives null
            // I think that style is set when the whole datagrid and columns are ready (next dissappointment) !!!
            // So we have to manipulate with "myListHeader.Load" event inside the definition of myListHeader control :( 

            theDataGrid.PropertyChanged += new PropertyChangedEventHandler(col1_PropertyChanged);

        }

        void col1_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            // here we are sure, that all columns have myLh property set and we can make with them what we want:
            myDataGrid myDG = (myDataGrid)sender;

            int i = 0;
            foreach (myDataGridTemplateColumn col in myDG.Columns)
            {
                i += 1;
                col.myLh.mySortHyperlinkButton.Content = "Column " + i;
                col.myLh.myFilterTextBox.Text = "Filter " + i;
                col.myLh.mySortHyperlinkButton.Click += new RoutedEventHandler(mySortHyperlinkButton_Click);
                col.myLh.myFilterTextBox.LostFocus += new RoutedEventHandler(myFilterTextBox_LostFocus);
            }

        }

        void myFilterTextBox_LostFocus(object sender, RoutedEventArgs e)
        {
            TextBox tb = (TextBox)sender;
            //theDataGrid.ItemSource = ...
        }

        void mySortHyperlinkButton_Click(object sender, RoutedEventArgs e)
        {
            HyperlinkButton hb = (HyperlinkButton)sender;
            //theDataGrid.ItemsSource = .....
        }

        


    }
 

myListHeader.xaml (a new user control; in my case each consists of hyperlinkbutton and textbox):

  

<UserControl 
    x:Class="GridHeaderTest.myListHeader"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    >
    
    <StackPanel x:Name="LayoutRoot" Background="Navy" Orientation="Vertical" >
        <HyperlinkButton x:Name="mySortHyperlinkButton" Content="Search"> </HyperlinkButton>
        <TextBox x:Name="myFilterTextBox"></TextBox>
    </StackPanel>
    
</UserControl>

 

myListHeader.xaml.cs (explanations inline):

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Controls.Primitives;

namespace GridHeaderTest
{

    public partial class myListHeader : UserControl
    {
        public myListHeader()
        {
            InitializeComponent();
            Loaded += new RoutedEventHandler(myListHeader_Loaded);
        }

         void myListHeader_Loaded(object sender, RoutedEventArgs e)
        {

            // The purpose of below code is to find the DataGrid "ground parent" of "this" header.
            // In Beta2 we could do it in one line, but it has unfortunately changed. 
            myListHeader lh = (myListHeader)sender;
            DataGridColumnHeader dgch = (DataGridColumnHeader)VisualTreeHelper.GetParent(this);
            DataGridColumnHeadersPresenter dgchp = (DataGridColumnHeadersPresenter)VisualTreeHelper.GetParent(dgch);
            Grid g = (Grid)VisualTreeHelper.GetParent(dgchp);
            Border b = (Border)VisualTreeHelper.GetParent(g);
            myDataGrid dg = (myDataGrid)VisualTreeHelper.GetParent(b);  // now we have te highest parent, I mean the DataGrid

            // then we have to make a list of columns of that DataGrid. Some of them have a new header and some not    
            List dgtcList = new List();
            foreach (myDataGridTemplateColumn dgtc in dg.Columns)
            {
                dgtcList.Add(dgtc);
            }

            // now we must find the first column, which has "myLh" property equals null. 
            // If there is no such columns, .FirstOrDefault() returns null.

            myDataGridTemplateColumn dgtc1 = dgtcList.FirstOrDefault(c => c.myLh == null) as myDataGridTemplateColumn;

            // The most important line in this solution:
            dgtc1.myLh = lh;  // that what I wanted

            // After all headers are set, we have to tell the myDataGrid, that all myLh properties are ready to use
            if (dgtcList.Where(c => c.myLh != null).Count() == dg.Columns.Count())
                dg.HeadersLoaded = true;

        }
    }
}

 

That is all I have. If you find simpler solution, hope you will remember me ... Smile 

B. 

 

 

 

 

 

 

chrisp_68
chrisp_68

Member

Member

18 points

38 Posts

Re: Custom DataGrid.Header - BUG in RC0.

Hi Bartlomeij,

Thanks so much for posting this. I'll give it a go right now. This morning, in desperation, I ended up looking at ComponentOne's Silverlight control library. They have a datagrid that seems to feature filtering in the header. See http://www.componentone.com/SuperProducts/StudioSilverlight/Live+Examples/ Click on "Studio for Silverlight Control Explorer" then select the datagrid in the treeview on the right. This may well do the job for me but I am not sure if the filtering is applied client side or server side or how it pages if at all and have not yet got an answer from their tech department. I don't like the idea of placing so much trust in a third part like this but if it really does everything that it says on the tin then perhaps I will consider it.

I, like you need to make my columns dynamically, so your sample will be very useful.

Thanks again

Christian

Bartlomiej
Bartlomiej

Member

Member

34 points

30 Posts

Re: Custom DataGrid.Header - BUG in RC0.

Thanks. Here is another nice example from DevExpress: http://demos.devexpress.com/AgDataGridDemos/ . Server side sorting, filtering and paging are reasons why I try to resolve DataGridColumnHeader on my own.

Have a nice research,

B.

chrisp_68
chrisp_68

Member

Member

18 points

38 Posts

Re: Custom DataGrid.Header - BUG in RC0.

Hi Bartlomiej (again) Sorry for bothering you, I'll go away soon I promise!

I've loaded you code, Had to change myListHeader_Loaded() as shown below

List<myDataGridTemplateColumn> dgtcList = new List<myDataGridTemplateColumn>();

It all compiles now but when I run it I get:

"Invalid attribute value primitives:DataGridColumnHeader for property TargetType. [Line: 9 Position: 48]"

at System.Windows.Application.LoadComponent(Object component, Uri resourceLocator)

at GridHeaderTest.App.InitializeComponent()

at GridHeaderTest.App..ctor()"

Any ideas? I have had a look and am lost. I was going to zip up my solution and post it but can't see how to include it here!

Christian

chrisp_68
chrisp_68

Member

Member

18 points

38 Posts

Re: Re: Custom DataGrid.Header - BUG in RC0.

I have now Solved the problem from my last post Sorry, I forgot to add:

xmlns:primitives="clr-namespace:System.Windows.Controls.Primitives;assembly=System.Windows.Controls.Data"

to App.xaml ! 

OK, I can go away now. Thanks again.

Christian

DrewPierce
DrewPierce

Member

Member

8 points

6 Posts

Re: Re: Custom DataGrid.Header - BUG in RC0.

test the ComponentOne datagrid control extensively for memory and performance before you plunge to much into it. i am waiting for them to clean it up as i am disappointed at the moment with it. With 77 rows in their online demo it should fly on thumb drags. Rather, it drags and lags. i have commented several times in their forums. I like C1, have used their stuff, but that baby is not ready for prime time.

 i compare it to just the DataGrid that is a screamer and non-memory leaker in comparison.

 

chrisp_68
chrisp_68

Member

Member

18 points

38 Posts

Re: Re: Re: Custom DataGrid.Header - BUG in RC0.

Hi Drew

Thanks for the heads up on this and yes I am in the middle of exactly such an evaluation.

I have 2 projects one that loads a componentOne datagrid the other uses the Microsft Silverlight datagrid with 18 columns (6 int, 6 string, 6 dateTime) and 100 rows.

I dont see the difference in performance that you did, sure the Microsoft one is quicker but not significantly, I'd expect this given the extra functionality in the ComponentOne App.

Also I dont see a huge difference in the memory footprint. Here I loaded each application 10 times, monitoring the memory used by iexplore. After 10 loads the Microsoft only app had caused the iexplore to increase in size by 35.3 Mb! and the ComponentOne App cause iexplore to increase in size by 37Mb.

Obviously both of these are totally unacceptable.

Looks like its back to good old Asp.Net datagrids.

DrewPierce
DrewPierce

Member

Member

8 points

6 Posts

Re: Re: Re: Custom DataGrid.Header - BUG in RC0.

the memory and performance issues to which irked me weren't with multiple loads but with grabbing the vertical scroll bar thumb and going up and down for minutes.

 though that might not be on the average person's radar, my users have many grids active at once and live on the page for hours doing data entry and scrolling. In my example I have a 200 row S/L grid with 10 columns pitted against the 77 rows on the online demo using componentOne.

perhaps the issue would go away were I to download the C1 library and implement it locally but I can't imagine the difference. To me their online demo loads their data from say XML and is locally cached once it comes up, so I don't see the need to spend that time doing that. (?)

iexplore goes from 90MB to 150MB in componentOne if you grab the vertical scrollbar and go up and down for 30 seconds. It is as if as you scroll vertically it re-instantiates other objects but to me the 77 rows should already be in memory and represented. So I can see it being a little slower, afterall it has more functionality, but it is 3x slower than my S/L comparison that has more rows.

so again, my focus is on scrolling (the memory used during scrolling, the performance during scrolling, and waiting and praying that the memory is released soon after scrolling).

As for memory, if it gets garbage collected soon that is fine, but i have a problem with the fact that new memory allocation has to be performed at all, and i am suspicious that it is a memory leak, because the memory does not shrink back down in my 10 minutes of testing of C1.

I am using IE7 and have had the same results against the C1 online demo's for the last 3 versions of the S/L releases.

Again i am a fan of C1. I look forward to using their stuff.

joer00
joer00

Member

Member

145 points

113 Posts

Re: Custom DataGrid.Header - BUG in RC0.

 this works in the browser but CRASHES in Blend. For the style I get an error "member content is not recoginzide or accesible" and trting to visualize my control in design mode gives me error HRESULT_EFail has returned from COM component.

 From day one I am working with the data grid I can only say its one of the most teriible designed parts of Silverlight ! Its completely frustrating !

Joe Robe
  • Unanswered Question
  • Answered Question
  • Announcement
Microsoft Communities