Wednesday, October 14, 2009

Silverlight DataGrid: Letting Users Manage Layout

We have a few datagrids that contain a lot of columns, and different columns are important to different users. They want to see only a subset of what is available, and use different fields as keys to the dataset. So, we had to come up with a way for the users to:
1. Rearrrange the columns
2. Make columns visibile/invisible
3. Freeze some columns in the beginning (keys) so they remain visible during horizontal scrolling.
4. Carry over the user preferences to future sessions.

We managed to come up with a generic user control to do this for any datagrid, and I think it is worth sharing here. Here is an application that demonstrates the capability:



It is just the same datagrid from previous posts, with some extraneous fields to play around with. You get the idea.

The code for the user control can be found here

To use the DataGridLayoutChooser, you would write some xaml like this:


   1:  <lcl:DataGridLayoutChooser Name="dgLayoutChooser" AppKey="dgLayout" Grid.RowSpan="2"/>
The property AppKey is the key that it uses to save the preferences in isolated storage, for use in future sessions.

When "Loaded" event handler for your app/usercontrol, you want to specify the DataGrid that the Chooser is formatting:


   1:  void Home_Loaded(object sender, RoutedEventArgs e)
   2:  {
   3:      dgLayoutChooser.GridToFormat = dg;
   4:      dgLayoutChooser.InitLayout();
   5:  }
InitLayout() applies any layout that the user may have saved from a previous session.
Now, when the user wants to manage the layout (Click event in the demo, for instance), you call the ShowLayoutDialog() method on the Chooser:


   1:  void ManageLayout(object sender, RoutedEventArgs e)
   2:  {
   3:      dgLayoutChooser.ShowLayoutDialog();
   4:  }
We will take a deeper look at the user-control code in the next post. The DataGridLayoutChooser is a very good candidate to be made into a real control, by the way. I will make that available in a few weeks, when I get around to it. In the meantime, I hope you find this UserControl as useful as we did.

9 comments:

  1. Hi Jose,
    i have used your user control in a project, but sometimes it produces an error showed below, after that we cannot open the page anyway. But, when i change "AppKey" property it works for a while, and reproduces this error. I handled the exception saying that "Value does not fall within the expected range". Any idea? thanks...


    Webpage error details


    Message: Unhandled Error in Silverlight Application
    Code: 4004
    Category: ManagedRuntimeError
    Message: System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values.
    Parameter name: index
    at System.Windows.PresentationFrameworkCollection`1.InsertDependencyObject(Int32 index, DependencyObject value)
    at System.Windows.Controls.UIElementCollection.InsertInternal(Int32 index, UIElement value)
    at System.Windows.PresentationFrameworkCollection`1.Insert(Int32 index, T value)
    at System.Windows.Controls.DataGrid.InsertDisplayedColumnHeader(DataGridColumn dataGridColumn)
    at System.Windows.Controls.DataGrid.OnApplyTemplate()
    at System.Windows.FrameworkElement.OnApplyTemplate(IntPtr nativeTarget)

    Line: 54
    Char: 13
    Code: 0
    URI: http://localhost:2139/Default.aspx

    ReplyDelete
  2. Yakup, I will have to check into that. Off the top of my head, it looks like the datagrid that is being managed, has a different number of columns than before, so it tries to apply the layout to the datagrid. You are doing "InitLayout()" after assigning the GridToFormat, right?

    ReplyDelete
  3. Thanks Jose for your reply.
    Yes i am calling "InitLayout()" after setting GridToFormat property. Maybe you are right about different number of columns because i should hide a specific column(showing image) in datagrid_Loaded() event according to admin setting .

    I can also say that I am using your control dynamically showed below.

    DataGridLayoutChooser dglaychoser = new DataGridLayoutChooser();
    dglaychoser.AppKey = "dgStockLayout";
    dglaychoser.GridToFormat = dgStockList;
    dglaychoser.InitLayout();
    Canvas cvs = new Canvas();
    cvs.HorizontalAlignment = HorizontalAlignment.Center;
    cvs.Children.Add(dglaychoser);
    LayoutRoot.Children.Add(cvs);
    LayoutRoot.UpdateLayout();

    ReplyDelete
  4. Very cool, Yakup. There is a property on DataGridLayoutChooser called "ZeroBasedStartIndex". You can move the hidden column to the beginning of the datagrid, and set this property to 1. Then, the chooser shows the user only columns from second onwards. This may eliminate your problem?

    ReplyDelete
  5. Now it is working properly Jose, thanks for your attention. I will try your advice as soon as I face the problem again.

    Have a nice day...

    ReplyDelete
  6. Hi Jose,


    I have been trying to implement your datagrid chooser in my project and it worked really well except when I changed some columns orders and then hit save, and the next time I tried to open the project it threw me the following error...

    An unhandled exception ('Unhandled Error in Silverlight Application Code:4004 Category: ManagedRuntimeError Messeage: System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values.

    after i changed some columns orders and hit save, the next time I tried to run the app it threw me this error.

    the code is really simple, in the xaml, I declared a datagrid named dg, with a DataGridLayoutChooser named dgLayoutChooser.

    in the MainPage code behind I have...

    public MainPage()
    {
    InitializeComponent();

    this.Loaded += new RoutedEventHandler(MainPage_Loaded);
    }

    void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
    dgLayoutChooser.GridToFormat = dg;
    dgLayoutChooser.InitLayout();
    }

    private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
    {
    dgLayoutChooser.ShowLayoutDialog();
    }

    I haven't changed any of your code...

    But I also have played around with your demo page and it works just fine... is there anything i did wrong?

    Thanks in adv,
    Xin

    ReplyDelete
  7. Xin,

    There are a couple of possibilities:
    1. The datagrid is not loaded at the time you call updateLayout(). This may happen if you are instantiating and loading the datagrid programmatically. You can veify this by moving UpdateLayout() to the handler of dg.Loaded event.
    2. You may have a variable number of columns in the datagrid? This is the problem that Yakup Yalcin had (our dialogue in the comments above)..
    Let me know if any of this helps.

    -Jose

    ReplyDelete
  8. Hi Jose,

    Thanks for your help it works now! :)

    Xin

    ReplyDelete
  9. were is the source code for this it would be great to have for my project. If you still have the source code please repost. or if there is an alternative then please let me know that too.

    ReplyDelete