4
\$\begingroup\$

The Rubberduck 2.0 Code Inspections UI was recently redesigned (again!), this time featuring a GroupingGrid control (thanks @BrunoCosta!), and what appears to be a dynamic grouping:

enter image description here

Resources

The UserControl's resources include two CollectionViewSource definitions, which determine the sorting and grouping rules for the user-selectable grouping styles.

<UserControl x:Class="Rubberduck.UI.CodeInspections.InspectionResultsControl"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 xmlns:inspections="clr-namespace:Rubberduck.Inspections"
 xmlns:resx="clr-namespace:Rubberduck.UI"
 xmlns:codeInspections="clr-namespace:Rubberduck.UI.CodeInspections"
 xmlns:controls="clr-namespace:Rubberduck.UI.Controls"
 xmlns:componentModel="clr-namespace:System.ComponentModel;assembly=WindowsBase"
 ResxExtension.DefaultResxName="Rubberduck.UI.RubberduckUI" 
 Language="{UICulture}"
 mc:Ignorable="d" 
 d:DesignHeight="300" d:DesignWidth="300" 
 d:DataContext="{d:DesignInstance codeInspections:InspectionResultsViewModel}">
<UserControl.Resources>
 <BooleanToVisibilityConverter x:Key="BoolToVisibility"/>
 <Style x:Key="LinkButton" TargetType="Button" BasedOn="{StaticResource ResourceKey={x:Type Button}}">
 <!-- irrelevant -->
 </Style>
 <codeInspections:InspectionSeverityImageSourceConverter x:Key="SeverityIconConverter" />
 <codeInspections:InspectionImageSourceConverter x:Key="InspectionIconConverter" />
 <codeInspections:InspectionTypeConverter x:Key="InspectionTypeConverter" />
 <Style x:Key="IconStyle" TargetType="Image">
 <Setter Property="Height" Value="16" />
 <Setter Property="Width" Value="16" />
 <Setter Property="Margin" Value="4" />
 </Style>
 <CollectionViewSource x:Key="ResultsByInspectionType" Source="{Binding Results}">
 <CollectionViewSource.SortDescriptions>
 <componentModel:SortDescription PropertyName="Inspection.Name"/>
 </CollectionViewSource.SortDescriptions>
 <CollectionViewSource.GroupDescriptions>
 <PropertyGroupDescription PropertyName="Inspection" Converter="{StaticResource InspectionTypeConverter}" />
 </CollectionViewSource.GroupDescriptions>
 </CollectionViewSource>
 <CollectionViewSource x:Key="ResultsByModule" Source="{Binding Results}">
 <CollectionViewSource.SortDescriptions>
 <componentModel:SortDescription PropertyName="QualifiedSelection.QualifiedName.ComponentName"/>
 </CollectionViewSource.SortDescriptions>
 <CollectionViewSource.GroupDescriptions>
 <PropertyGroupDescription PropertyName="QualifiedSelection.QualifiedName" />
 </CollectionViewSource.GroupDescriptions>
 </CollectionViewSource>
</UserControl.Resources>

Content

So, here's the actual control:

<Grid>
 <Grid.RowDefinitions>
 <RowDefinition Height="30"/>
 <RowDefinition Height="*" MinHeight="64" />
 <RowDefinition Height="5"/>
 <RowDefinition Height="Auto" MinHeight="48"/>
 </Grid.RowDefinitions>
 <Border Grid.Row="0" Grid.RowSpan="3" Background="#FFEEF5FD" />
 <ToolBar Grid.Row="0">
 <Button Command="{Binding RefreshCommand}">
 <Image Height="16" Source="../../Resources/arrow-circle-double.png" />
 </Button>
 <Separator />
 <Menu>
 <MenuItem VerticalAlignment="Center" 
 Header="{Resx ResxName=Rubberduck.UI.RubberduckUI, Key=Fix}"
 ItemsSource="{Binding SelectedItem.QuickFixes}">
 <MenuItem.Icon>
 <Image Height="16" Source="../../Resources/tick.png" />
 </MenuItem.Icon>
 <MenuItem.ItemContainerStyle>
 <Style TargetType="{x:Type MenuItem}">
 <Setter Property="Command" Value="{Binding Path=DataContext.QuickFixCommand, RelativeSource={RelativeSource FindAncestor, AncestorType=Menu, AncestorLevel=1}}" />
 <Setter Property="CommandParameter" Value="{Binding}" />
 <Setter Property="Header" Value="{Binding Description}" />
 </Style>
 </MenuItem.ItemContainerStyle>
 </MenuItem>
 </Menu>
 <Menu>
 <MenuItem Header="Grouping">
 <MenuItem.Icon>
 <Image Height="16" Source="../../Resources/Microsoft/PNG/GroupBy_284_32.png" />
 </MenuItem.Icon>
 <MenuItem x:Name="GroupByInspectionType" VerticalAlignment="Center" Header="By inspection type" IsChecked="True" 
 IsCheckable="True" controls:MenuItemGroup.GroupName="GroupingStyle" />
 <MenuItem x:Name="GroupByModule" VerticalAlignment="Center" Header="By module" 
 IsCheckable="True" controls:MenuItemGroup.GroupName="GroupingStyle" />
 </MenuItem>
 </Menu>
 <Separator />
 <Button Command="{Binding CopyResultsCommand}">
 <Image Height="16" Source="../../Resources/document-copy.png" />
 </Button>
 </ToolBar>
 <controls:GroupingGrid Grid.Row="1" x:Name="ResultsByInspectionTypeGrid"
 ShowGroupingItemCount="True"
 SelectedItem="{Binding SelectedItem}"
 ItemsSource="{Binding Source={StaticResource ResultsByInspectionType}}"
 Visibility="{Binding IsChecked, ElementName=GroupByInspectionType, Converter={StaticResource BoolToVisibility}}">
 <DataGrid.Columns>
 <DataGridTemplateColumn Header="{Resx ResxName=Rubberduck.UI.RubberduckUI, Key=CodeInspectionResults_Type}">
 <DataGridTemplateColumn.CellTemplate>
 <DataTemplate DataType="inspections:ICodeInspectionResult">
 <Image Source="{Binding Inspection, Converter={StaticResource InspectionIconConverter}}" Height="16" />
 </DataTemplate>
 </DataGridTemplateColumn.CellTemplate>
 </DataGridTemplateColumn>
 <DataGridTextColumn Header="{Resx ResxName=Rubberduck.UI.RubberduckUI, Key=CodeInspectionResults_Location}" Binding="{Binding QualifiedSelection.QualifiedName}" />
 <DataGridTextColumn Header="{Resx ResxName=Rubberduck.UI.RubberduckUI, Key=CodeInspectionResults_Issue}" Binding="{Binding Description}" />
 </DataGrid.Columns>
 </controls:GroupingGrid>
 <controls:GroupingGrid Grid.Row="1" x:Name="ResultsByModuleGrid"
 ShowGroupingItemCount="True"
 SelectedItem="{Binding SelectedItem}"
 ItemsSource="{Binding Source={StaticResource ResultsByModule}}"
 Visibility="{Binding IsChecked, ElementName=GroupByModule, Converter={StaticResource BoolToVisibility}}">
 <DataGrid.Columns>
 <DataGridTemplateColumn Header="{Resx ResxName=Rubberduck.UI.RubberduckUI, Key=CodeInspectionResults_Type}">
 <DataGridTemplateColumn.CellTemplate>
 <DataTemplate DataType="inspections:ICodeInspectionResult">
 <Image Source="{Binding Inspection, Converter={StaticResource InspectionIconConverter}}" Height="16" />
 </DataTemplate>
 </DataGridTemplateColumn.CellTemplate>
 </DataGridTemplateColumn>
 <DataGridTextColumn Header="{Resx ResxName=Rubberduck.UI.RubberduckUI, Key=CodeInspectionResults_Location}" Binding="{Binding QualifiedSelection.QualifiedName}" />
 <DataGridTextColumn Header="{Resx ResxName=Rubberduck.UI.RubberduckUI, Key=CodeInspectionResults_Issue}" Binding="{Binding Description}" />
 </DataGrid.Columns>
 </controls:GroupingGrid>
 <resx:BusyIndicator Grid.Row="1" Width="36" Height="36" Visibility="{Binding IsBusy, Converter={StaticResource BoolToVisibility}}" />
 <GridSplitter Grid.Row="2" Height="5" ShowsPreview="True" Cursor="SizeNS" HorizontalAlignment="Stretch"/>
 <Border Grid.Row="3" BorderThickness="0,1,0,0" BorderBrush="DimGray">
 <StackPanel Orientation="Vertical" MinHeight="48" Background="Gainsboro">
 <StackPanel Margin="4" Orientation="Horizontal" HorizontalAlignment="Stretch">
 <Image Style="{StaticResource IconStyle}" VerticalAlignment="Center" 
 Source="{Binding SelectedItem.Inspection.Severity, Converter={StaticResource SeverityIconConverter}}"/>
 <TextBlock Margin="4" Text="{Binding SelectedItem.Inspection.Description}" FontWeight="Bold" TextWrapping="Wrap"/>
 </StackPanel>
 <TextBlock Margin="4" Text="{Binding SelectedItem.Inspection.Meta}" FontSize="10" TextWrapping="Wrap"/>
 <TextBlock Margin="8" Text="{Binding SelectedItem.QualifiedSelection}" FontSize="10" TextWrapping="Wrap" />
 <WrapPanel>
 <Button Style="{StaticResource LinkButton}" Margin="4"
 Visibility="{Binding CanExecuteQuickFixInModule, Converter={StaticResource BoolToVisibility}}"
 Command="{Binding QuickFixInModuleCommand}"
 Content="{Resx ResxName=Rubberduck.UI.RubberduckUI, Key=QuickFix_ThisModule}" />
 <Button Style="{StaticResource LinkButton}" Margin="4"
 Visibility="{Binding CanExecuteQuickFixInProject, Converter={StaticResource BoolToVisibility}}"
 Command="{Binding QuickFixInProjectCommand}"
 Content="{Resx ResxName=Rubberduck.UI.RubberduckUI, Key=QuickFix_ThisProject}" />
 <Button Style="{StaticResource LinkButton}" Margin="4"
 Visibility="{Binding CanDisableInspection, Converter={StaticResource BoolToVisibility}}"
 Command="{Binding DisableInspectionCommand}"
 Content="{Resx ResxName=Rubberduck.UI.RubberduckUI, Key=DisableThisInspection}" />
 </WrapPanel>
 </StackPanel>
 </Border>
</Grid>

I'd like to bring your attention to this part:

<controls:GroupingGrid Grid.Row="1" x:Name="ResultsByModuleGrid"
 ShowGroupingItemCount="True"
 SelectedItem="{Binding SelectedItem}"
 ItemsSource="{Binding Source={StaticResource ResultsByModule}}"
 Visibility="{Binding IsChecked, ElementName=GroupByModule, Converter={StaticResource BoolToVisibility}}">

And:

<controls:GroupingGrid Grid.Row="1" x:Name="ResultsByInspectionTypeGrid"
 ShowGroupingItemCount="True"
 SelectedItem="{Binding SelectedItem}"
 ItemsSource="{Binding Source={StaticResource ResultsByInspectionType}}"
 Visibility="{Binding IsChecked, ElementName=GroupByInspectionType, Converter={StaticResource BoolToVisibility}}">

It completely reeks of a hack, but I haven't managed to get anything else to work. Is there any better way to do this? Copy-Pasta can't be the right way... right? ...Anything else smells in this markup?

I can provide the C# ViewModel and related classes if additional context is needed, but I wanted this post to be about the markup.

asked Feb 16, 2016 at 6:37
\$\endgroup\$
1
  • 2
    \$\begingroup\$ FWIW, for somebody who doesn't know wpf, this looks perfectly reasonable, nice code. I'm more concerned that further optimization attempts might be venturing into over-engineering-land, and risk increasing complexity rather than reducing it \$\endgroup\$ Commented Feb 16, 2016 at 7:23

1 Answer 1

1
\$\begingroup\$

Apparently toggling visibility of controls like that, is common practice in WPF; there's no way to toggle the ItemsSource in xaml, so what you have is pretty much as good as it gets.

Minor things stick out though:

xmlns:resx="clr-namespace:Rubberduck.UI"

The only thing in that namespace is the BusyIndicator control, which would be better off in the controls namespace.

Probably an oversight, but every label is localized, except the ones in the "Grouping" menu, which are hard-coded:

<MenuItem Header="Grouping">

The x:Name attributes on the GroupingGrid controls shouldn't be needed (a quick search in the Rubberduck repository shows it's not referenced anywhere), get rid of them.

answered Feb 16, 2016 at 21:01
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.