Skip to main content
Home Forums Silverlight Programming Report a Silverlight Bug Custom DataGrid.Header - BUG in RC0.
30 replies. Latest Post by joer00 on October 28, 2008.
(0)
Bartlomiej
Member
34 points
30 Posts
09-26-2008 11:54 AM |
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
All-Star
17439 points
3,172 Posts
09-26-2008 12:31 PM |
I found the same thing. How do we set the Header to a control now?
emikelsoft
4 points
3 Posts
09-27-2008 3:25 PM |
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
13 points
24 Posts
09-28-2008 7:07 AM |
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 L...
25052 points
2,747 Posts
09-29-2008 5:49 AM |
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):
Note now you have to use HeaderStyle if you want anything other than plain text.
09-29-2008 9:31 AM |
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]"
09-29-2008 10:03 AM |
DataGridColumnHeader is in System.Windows.Controls.Primitives name space:
xmlns:dataprimitives="clr-namespace:System.Windows.Controls.Primitives;assembly=System.Windows.Controls.Data"
09-29-2008 10:43 AM |
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 HyperlinkButtonstring 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;
But each my column header is not only the "style" - it can be quite complex (have custom properties, events, ...).
09-29-2008 10:53 AM |
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
09-29-2008 12:02 PM |
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).
09-29-2008 12:16 PM |
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.
09-29-2008 1:58 PM |
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.
09-29-2008 3:21 PM |
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.
09-30-2008 6:41 AM |
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 ...).
09-30-2008 12:13 PM |
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).
10-01-2008 11:25 AM |
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!
10-01-2008 11:43 AM |
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
10-01-2008 12:06 PM |
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.
10-02-2008 9:29 AM |
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.
10-02-2008 11:08 AM |
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".
10-03-2008 8:37 AM |
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.
chrisp_68
18 points
38 Posts
10-22-2008 5:39 AM |
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
10-22-2008 10:41 AM |
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 ...
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 ...
10-22-2008 12:02 PM |
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
10-22-2008 12:17 PM |
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,
10-22-2008 12:48 PM |
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
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()"
"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!
10-22-2008 12:52 PM |
I have now Solved the problem from my last post Sorry, I forgot to add:
OK, I can go away now. Thanks again.
DrewPierce
8 points
6 Posts
10-23-2008 2:25 PM |
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.
10-28-2008 6:20 AM |
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.
10-28-2008 8:28 AM |
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
145 points
113 Posts
10-28-2008 11:01 AM |
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 !