Search code examples
wpfdrysubclassing

DRY - Subclassing WPF windows


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?


Solution

  • 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.