none
Ordering columns after AutoGenerateColumns is done

    Question

  • AutoGeneratedColumns appear to be generated in alphabetical order.

    In my case, I have two different ObservableCollections. One of type <a> and the other of type <b>. I have a checkbox in the xaml whose click event replaces DataGrid.ItemSource with one or the other collection.

    My grid uses AutoGenerateColumns since I can't define the columns in XAML that could deal with the switching. When the columns are autogenerated, they appear to come out in alphabetical order instead of the order of the properties in <a> or <b>. Alphabetical order is not what my customer wants.

    I investigated the loaded event. It fires only once - when ItemSource is set for the first time (I think). In any case, it does not fire again when the checkbox switches the item source. Since <a> and <b> have different structures, this one time event is not sufficient for ordering the columns. What event can I use that fires every time item source is switched?

    I've tried ordering the columns just after I set ItemSource but the columns collection isn't populated right then. The only other event that seems to fire is the AutoGenerateColumn handler. It gets called one at a time for each public property of the type loaded into ItemSource. However, another post advised that the DisplayIndex isn't available in this handler since the column isn't yet created.

    That same post mentioned using BeginInvoke in the AutoGenerateColumn handler but I don't know how that relates. While in that event I don't even know if the grid is finished processing the columns yet.

    Tuesday, November 17, 2009 8:17 PM

Answers

  • Well, I'll be a monkey's uncle.

    Setting the DisplayIndex through code seems to be a lost cause. However, since my types <a> and <b> are POCOs that are defined on the server and are shuttled to Silverlight over WCF, then I discovered that I can add Order to my POCO property's [DataMember] attributes and then, after a refresh of my service reference, the grid pays attention to the order!

    For example, here is what I've been calling <b>:

    [DataMember(Order = 0)]
    public int Year { get; set; }
    [DataMember(Order = 1)]
    public double ActiveTotal { get; set; }
    [DataMember(Order = 2)]
    public double PreviousTotal { get; set; }
    [DataMember(Order = 3)]
    public double Variance { get; set; }
    [DataMember(Order = 4)]
    public double PercentVariance { get; set; }
    string explanation = string.Empty;
    [DataMember(Order = 5)]
    public string Explanation
    {
    	get { return explanation; }
    	set
    	{
    		{ explanation = value; NotifyPropertyChanged("Explanation"); }
    	}
    }
    
    Tuesday, November 17, 2009 10:36 PM

All replies

  • After I read this post http://stackoverflow.com/questions/321417/silverlight-datagrid-exception-reordering-column-headers, I tried setting DisplayIndex in the AutoGenerateColumn handler and it works - sort of.

    I inspect each PropertyName and set e.Column.DisplayIndex appropriate for each column.:

    if (e.PropertyName == "Year")
    {
    	DataGridTextColumn tc = (DataGridTextColumn)e.Column;
    	tc.Binding.Mode = BindingMode.OneWay;
    	tc.DisplayIndex = 0;
    	e.Column.Header = string.Empty;
    	e.Column.DisplayIndex = 0;
    }
    else if (e.PropertyName == "ActiveTotal")
    {
    	DataGridTextColumn tc = (DataGridTextColumn)e.Column;
    	tc.Binding.Mode = BindingMode.OneWay;
    	tc.Binding.Converter = new DecimalRounder();
    	tc.DisplayIndex = 1;
    	e.Column.Header = "Active Total";
    	e.Column.DisplayIndex = 1;
    }
    else if (e.PropertyName == "PreviousTotal")
    {
    	DataGridTextColumn tc = (DataGridTextColumn)e.Column;
    	tc.Binding.Mode = BindingMode.OneWay;
    	tc.Binding.Converter = new DecimalRounder();
    	tc.DisplayIndex = 2;
    	e.Column.Header = "Previous Total";
    	e.Column.DisplayIndex = 2;
    }
    else if (e.PropertyName == "Variance")
    {
    	DataGridTextColumn tc = (DataGridTextColumn)e.Column;
    	tc.Binding.Mode = BindingMode.OneWay;
    	tc.Binding.Converter = new DecimalRounder();
    	tc.DisplayIndex = 3;
    	e.Column.Header = "Amount Variance";
    	e.Column.DisplayIndex = 3;
    }
    else if (e.PropertyName == "PercentVariance")
    {
    	DataGridTextColumn tc = (DataGridTextColumn)e.Column;
    	tc.Binding.Mode = BindingMode.OneWay;
    	tc.Binding.Converter = new DecimalRounder();
    	tc.Binding.ConverterParameter = "P1";
    	tc.DisplayIndex = 4;
    	e.Column.Header = "Percent Variance";
    	e.Column.DisplayIndex = 4;
    }
    else if (e.PropertyName == "Explanation")
    {
    	DataGridTextColumn tc = (DataGridTextColumn)e.Column;
    	tc.Binding.Mode = BindingMode.TwoWay;
    	tc.DisplayIndex = 5;
    	e.Column.Header = "Variance Explanation";
    	e.Column.IsReadOnly = false;
    	e.Column.DisplayIndex = 5;
    }

    but when I check the DisplayIndexes in the populated grid the indexes got changed around


    Year: 0 (ok)
    ActiveTotal: 1 (ok)
    Variance Explanation: 2 (supposed to be 5)
    Previous Total: 3 (supposed to be 2)
    Amount Variance: 4 (supposed to be 3)
    Percent Variance: 5 (supposed to be 4)


    Why is it doing that? Where else can I set DisplayIndex?
    Tuesday, November 17, 2009 9:28 PM
  • Just wondering, is your worst case scenario would be two Obeservable collection <a> and <b>. If that the case, using two seperate datagrid and showing and binding one of them depending on checkbox solves the problem? It does save a lot of coding and much more simpler...or maybe that could be your altenative option.

    Sharker

    Tuesday, November 17, 2009 10:24 PM
  • Well, I'll be a monkey's uncle.

    Setting the DisplayIndex through code seems to be a lost cause. However, since my types <a> and <b> are POCOs that are defined on the server and are shuttled to Silverlight over WCF, then I discovered that I can add Order to my POCO property's [DataMember] attributes and then, after a refresh of my service reference, the grid pays attention to the order!

    For example, here is what I've been calling <b>:

    [DataMember(Order = 0)]
    public int Year { get; set; }
    [DataMember(Order = 1)]
    public double ActiveTotal { get; set; }
    [DataMember(Order = 2)]
    public double PreviousTotal { get; set; }
    [DataMember(Order = 3)]
    public double Variance { get; set; }
    [DataMember(Order = 4)]
    public double PercentVariance { get; set; }
    string explanation = string.Empty;
    [DataMember(Order = 5)]
    public string Explanation
    {
    	get { return explanation; }
    	set
    	{
    		{ explanation = value; NotifyPropertyChanged("Explanation"); }
    	}
    }
    
    Tuesday, November 17, 2009 10:36 PM
  • I am having the same issue with the stackoverflow method. It seems logical and when debugging the code it seems everything is getting set to the correct order, but in the end it seems there is a few columns that get changed as you described. Creating POCO objects will solve my issue but using RIA Services and Entity Framework, I would really hate to make POCO objects just for sorting. Is there a place where I can set an order besides in the auto generated entities? If I were to make a change to my model it will be overwritten. Any ideas?
    Monday, December 28, 2009 3:06 PM