0

I have several WPF windows which will be very similar except for the columns on a DataGrid (the DataContext will be ObservableCollections of different objects), the text in some Labels and a Button click handler.

For each window, the <DataGrid.Columns> part of the DataGrid is different. It uses AutoGenerateColumns="False" and shows different columns for the different objects.

I wonder if it's possible to subclass a base WPF window so I can just write the <DataGrid.Columns> part on the XAML for each subclass instead of writing it in code.

Or what other techniques exist for abiding by the DRY principle on WPF while still using XAML?

user3557327
  • 1,109
  • 13
  • 22
  • Have you read up on the MVVM design pattern? Sounds like you could use a single view with different view models, or possibly even a single view model (with the only difference being different data for each window). – Mike Strobel May 28 '14 at 15:55
  • Yes, I'd be using different ViewModels for each type of object in the ObservableCollection, subclassing or using a template for that is no issue. The problem is with avoiding repetition in the View. – user3557327 May 28 '14 at 15:59
  • 1
    Why can you not use the same view for each window? If the only differences are things like text, make those text strings bindable properties of the view model. Do the same for the `ICommand` your button hooks up to (don't use click event handlers). Have your grid columns populated from the underlying data source. – Mike Strobel May 28 '14 at 16:10
  • How do I populate DataGrid Columns from the datasource if I use `AutoGenerateColumns="False"`? I don't want to show all the fields in the data and I need to add an extra column with a button. – user3557327 May 28 '14 at 16:15
  • You could use an `ITypedList` to expose only the property descriptors for the columns you want. You could wrap the result rows. You could annotate non-displayed properties with `[Browsable(false)]`. You could call into the view model to retrieve a list of column names. There are many solutions. – Mike Strobel May 28 '14 at 17:02

2 Answers2

0

I would do it with one window and different DataTemplates. However, if you want to use inheritance then you could override the DataTemplate in the Window.Resources using the key that is referenced by the base Window. The DataTemplate would have the Xaml for the whole datagrid.

Bartosz Wójtowicz
  • 1,321
  • 10
  • 18
0

How do I populate DataGrid Columns from the datasource...

Yes, you have come across a limitation here. The Columns property is not bindable; in fact it isn't even settable, you can only add and remove from the collection. There is a workaround at this question: How do I bind a WPF DataGrid to a variable number of columns?

So theoretically, you could add the columns to <Application.Resources>, then databind an attached property as in the question above, and write a value converter that builds a column collection based on the datasource value, pulling from Application.Current.Resources. But this seems more convoluted than it needs to be.

I think you could just use a style trigger that replaces some Content with different DataGrids:

<ContentControl>
    <ContentControl.Style>
        <Style TargetType="ContentControl">
            <Setter Property="Content">
                <Setter.Value>
                    <DataGrid Style="{StaticResource CommonStyle}">
                        <DataGrid.Columns>
                            ... default columns go here ...
                        </DataGrid.Columns>
                    </DataGrid>
                <Setter.Value>
            </Setter>
            <Style.Triggers>
                <DataTrigger Binding="{Binding SomeCondition}" Value="True">
                    <Setter Property="Content">
                        <DataGrid Style="{StaticResource CommonStyle}">
                            <DataGrid.Columns>
                                ... alternate columns ...
                            </DataGrid.Columns>
                        </DataGrid>
                    </Setter>
                </DataTrigger>
                ... additional triggers as needed ...
            </Style.Triggers>
        </Style>
    </ContentControl.Style>
</ContentControl>

This could be part of a larger common view template -- no need to create separate view classes.

Community
  • 1
  • 1
nmclean
  • 7,564
  • 2
  • 28
  • 37
  • I think it's generally advised against to use UI elements in setter values, but you can accomplish pretty much the same thing by replacing the `Content` setter with a `ContentTemplate` setter. You'd just need to wrap the `DataGrid` in a `DataTemplate`. – Mike Strobel May 28 '14 at 17:50
  • @MikeStrobel If we use content templates, we would also need to add `Content="{Binding}"`. I'd rather swap `ContentControl` for `Control` and set `ControlTemplate`. – nmclean May 28 '14 at 17:56
  • @nmclean Ah, yes, if you want the binding context to carry over, then you're right. – Mike Strobel May 28 '14 at 19:12