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
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.
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?
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.
No, I don't.
I have
public sealed class UserListViewModel : ViewModel<Library.BusinessObjects.Users.UserList>
The UserList inherits directly from ReadOnlyListBase<UserList, UserInfo>
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).
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?
Or maybe it's just the Microsoft DataGrid does not check AutogenerateField = false on the Display attribute.
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
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.
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.
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
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 == null) return;
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
Well, you could create your own subclassed DataGrid with your desired behavior and thus not require codebehind in your View.
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