Hiding properties in datagrid

Hiding properties in datagrid

Old forum URL: forums.lhotka.net/forums/t/12039.aspx


TygreWolf posted on Tuesday, June 25, 2013

Hello,

I have a simple WPF application using CSLA 4.5.30, with a DataGrid bound to a CollectionViewSource, which in turn is bound to a ViewModel that inherits from a CSLA ViewModel<T> object.  In my case, it's a UserList (CSLA style) containing UserInfo objects.

The data grid by default has the AutoGenerateColumns property set to true.  When displayed, it shows both the IsBusy and IsSelfBusy properties of the Model, along with the data associated with the UserInfo object.

Why are the IsBusy and IsSelfBusy properties exposed as columns on the data grid, and what is the best way to make sure they are hidden?

-Jason

JonnyBee replied on Tuesday, June 25, 2013

Hi, 

It is a balance between hiding values from databinding and expose them for databinding so that you can f.ex bind the enabled value of a button to IsDirty.

TygreWolf replied on Tuesday, June 25, 2013

So other than turning off AutoGenerateColumns and manually defining each column in the XAML, is there any other (read: better) way to hide those two properties?

JonnyBee replied on Tuesday, June 25, 2013

Do you use your own intermediate base classes?

In the Csla.Core.BusinessBase class the IsBusy, IsSelfBusy, IsDirty etc all have:

    [Browsable(false)]
    [Display(AutoGenerateField = false)]

attributes attached to them so none of them should show up by default in DataGrid.

And this has been unchanged for more than a year.

 

TygreWolf replied on Tuesday, June 25, 2013

No, I don't.

I have

public
 sealed class UserListViewModel : ViewModel<Library.BusinessObjects.Users.UserList>

The UserList inherits directly from  ReadOnlyListBase<UserListUserInfo>

And the XAML

<
UserControl.Resources>
    <vm:UserListViewModel x:Key="UserListViewModel"/>
    <CollectionViewSource x:Key="UserListModelViewSource" Source="{Binding Path=Model, Source={StaticResource UserListViewModel}}"/>
</UserControl.Resources>
<DataGrid x:Name="DgUsers" ItemsSource="{Binding Source={StaticResource UserListModelViewSource}}"/>

This displays a data grid that, as far as I can see, has 2 issues.  The first, is that IsBusy and IsSelfBusy are displayed as columns in the grid.  The second is that the column headers do not match what is defined in my UserInfo class (whether I use FriendlyName in the RegisterProperty constructor, or just use the Display attribute).

JonnyBee replied on Tuesday, June 25, 2013

Can you verify that the base class for UserInfo has:

    /// <summary>
    /// Gets a value indicating whether this
    /// object or any of its child objects are
    /// running an async operation.
    /// </summary>
    [Browsable(false)]
    [Display(AutoGenerateField = false)]
    public virtual bool IsBusy
    {
      get { return IsSelfBusy || (_fieldManager != null && FieldManager.IsBusy()); }
    }

    /// <summary>
    /// Gets a value indicating whether this
    /// object is
    /// running an async operation.
    /// </summary>
    [Browsable(false)]
    [Display(AutoGenerateField = false)]
    public virtual bool IsSelfBusy
    {
      get { return _isBusy || LoadManager.IsLoading; }
    }

and that these properties is not overriden in your UserInfo class.

Which DataGrid do you use?

JonnyBee replied on Tuesday, June 25, 2013

Or maybe it's just the Microsoft DataGrid does not check AutogenerateField = false on the Display attribute.

http://social.msdn.microsoft.com/Forums/vstudio/en-US/825b9300-5479-41d8-8c6c-30cfeb226c6e/hide-column-inside-a-wpf-datagrid

TygreWolf replied on Tuesday, June 25, 2013

I can't validate the code of the base class.  The CSLA reference I'm using is downloaded from NuGet.  I can tell you that it's version 4.5.30.0.  Also, my UserInfo class does not override either of those properties.

All in all, these are pretty simplistic classes.  I'm not really sure how the DataGrid performs the data binding under the hood, so it's quite possible that it's ignoring certain aspects of the binding and/or CSLA framework.

Then again, it could just be something stupid that I'm doing (or not doing).

I've put together a sample solution that shows this behavior.  It's a VS2012 solution.  The file is Sample.zip and can be found at http://www.fileswap.com/dl/PgVg7YriLE/.

-Jason

TygreWolf replied on Tuesday, June 25, 2013

Just an FYI, I've also tried adjusting the XAML to:

<
DataGrid x:Name="DgUsers" AutoGenerateColumns="False" ItemsSource="{Binding Source={StaticResource UserListModelViewSource}}">
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Path=UserId}" Header="{Binding Source=UserIdProperty, Path=FriendlyName}"/>
        <DataGridTextColumn Binding="{Binding Path=FirstName}" Header="{Binding Source=FirstNameProperty, Path=FriendlyName}"/>
        <DataGridTextColumn Binding="{Binding Path=LastName}" Header="{Binding Source=LastNameProperty, Path=FriendlyName}"/>
    </DataGrid.Columns>
</DataGrid>

This successfully limits what columns are shown on the data grid, thereby keeping IsBusy and IsSelfBusy from being displayed, but the binding to the Header property from the friendly name does not work.  Having to define the columns individually is not an ideal solution, and having to define the display name of the column header by static text (Header = "User ID") is an even less ideal solution.

thaehn replied on Tuesday, June 25, 2013

I typically have a root object that hosts my list object, so the binding would be Model.MyList.  By doing this only the items on the list show up and I can use auto generated columns.

TygreWolf replied on Wednesday, June 26, 2013

Understood, but I don't think that type of redundancy should be necessary.  It would actually take less code just to define the columns in the XAML.  It also doesn't solve the column naming/friendly name/display attribute  issue.

-Jason

TygreWolf replied on Wednesday, June 26, 2013

So based on the post found here, WPF doesn't support data annotations.

That being the case, I added code in the code-behind (something I hate doing) that takes care of both the displaying of extra columns, and the column header naming issue:

private void DgUsers_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
        {
            var pd = (PropertyDescriptor)e.PropertyDescriptor;
            var da = (DisplayAttribute) pd.Attributes[typeof (DisplayAttribute)];

            if (da == nullreturn;
            
            var autoGenerateField = da.GetAutoGenerateField();
            if (autoGenerateField != null && !autoGenerateField.Value)
            {
                e.Cancel = true;
            }

            if (!string.IsNullOrEmpty(da.Name))
            {
                e.Column.Header = da.Name;
            }
        }

It's not the most elegant solution, since I'd rather have everything hooked up via data binding and defined in the XAML, rather than writing code-behind, but it works.  I'm not sure if there's another possible solution, or if WPF will support the data annotations in the future.

-Jason

JonnyBee replied on Wednesday, June 26, 2013

Well, you could create your own subclassed DataGrid with your desired behavior and thus not require codebehind in your View.

TygreWolf replied on Wednesday, June 26, 2013

Yes Jason likes this post.

Perhaps in the future.  It'd be nice to have some automated grouping functionality, as well.  For now, I'll stick with the basic requirements my application needs to perform its intended duty.

-Jason

Copyright (c) Marimer LLC