I have a DataGrid which I am binding to a PagedCollectionView which is grouped and sorted. The contents of the DataGrid are not editable although one column contains a clickable link.
I have restricted the SelectionMode of the DataGrid to DataGridSelectionMode.Single which stops multiple row selection which is good. But the selected row also has a selected Cell which draws in a slightly lighter colour than the rest of the selected row and has a border.
Basically I'd like to have a SelectedRow but not a SelectedCell (from a UI/Display perspective).
It feels like it should be simple matter of setting a property, but I get the feeling maybe I have to edit the DataGrids template and/or mess with the VisualStateManager.
I'm happy to switch to another control other than DataGrid but I do need to be able to display Grouping.
I found 'a' way of making the individual cells appear to be not selected, although I'm not sure if its the best way.
Edit the CellStyle for the DataGrid, Find the Rectangle named FocusVisual. This is the Rectangle thats used to indicate a selected Cell. Set its Fill & Stroke to Transparent, I also set its StrokeThickness to 0. Don't delete the Rectangle entirely because other things are expecting it to be there.
The xaml looked something like this:
<Style x:Key="NonSelectableDataGridCellStyle" TargetType="data:DataGridCell">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="data:DataGridCell">
<Grid x:Name="Root" Background="{TemplateBinding Background}">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CurrentStates">
<VisualState x:Name="Regular"/>
<VisualState x:Name="Current">
<Storyboard>
<DoubleAnimation Duration="0"
Storyboard.TargetName="FocusVisual"
Storyboard.TargetProperty="Opacity" To="1"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="ValidationStates">
<VisualState x:Name="Valid"/>
<VisualState x:Name="Invalid">
<Storyboard>
<DoubleAnimation Duration="0" Storyboard.TargetName="InvalidVisualElement" Storyboard.TargetProperty="Opacity" To="1"/>
<ColorAnimation Duration="0" Storyboard.TargetName="FocusVisual" Storyboard.TargetProperty="(Fill).Color" To="#FFFFFFFF"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Rectangle x:Name="FocusVisual"
Fill="Transparent"
Stroke="Transparent"
StrokeThickness="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
IsHitTestVisible="false"
Opacity="0"
/>
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}"/>
<Rectangle x:Name="InvalidVisualElement" Stroke="#FFDC000C" StrokeThickness="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" IsHitTestVisible="False" Opacity="0"/>
<Rectangle x:Name="RightGridLine" VerticalAlignment="Stretch" Width="1" Grid.Column="1"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and you add the CellStyle to the DataGrid
<data:DataGrid x:Name="uiDataGrid"
CellStyle="{StaticResource NonSelectableDataGridCellStyle}"
>
...
</data:DataGrid>
You can also add "invisible" column:
<sdk:DataGrid.Columns>
<sdk:DataGridTextColumn Binding="{Binding Path=Nothing}" MinWidth="0" MaxWidth="0" />
And make it current whenever current cell is changed:
Private Sub _CurrentCellChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.CurrentCellChanged
If Me.CurrentColumn IsNot Nothing Then Me.CurrentColumn = Me.Columns(0)
End Sub
This was suitable for me.
Related
I am trying to use the VisualStateManager inside my ControlTemplate for a ToggleButton. I want the ToggleButton to look one way when checked and another when unchecked. I also want the ToggleButton to look different when it's disabled. The issue I'm having is the Unchecked VisualState seems to be trumping the Disabled VisualState.
The documentation states that "Each VisualStateGroup contains a collection of VisualState objects that are mutually exclusive." That's nice, but what about mutual exclusivity between groups?
Anyhow, here's my ControlTemplate. How can I get the TextBlock to use different color for each of the three states; Checked, Unchecked and Disabled?
<Style x:Key="GraphToggleButtonStyle" TargetType="{x:Type ToggleButton}">
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border Background="Transparent">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Disabled">
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="(TextBlock.Foreground).(SolidColorBrush.Color)" Storyboard.TargetName="TextBlock" To="Pink" Duration="0" />
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="CheckStates">
<VisualState x:Name="Checked">
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="(TextBlock.Foreground).(SolidColorBrush.Color)" Storyboard.TargetName="TextBlock" To="#3AA5DB" Duration="0" />
</Storyboard>
</VisualState>
<VisualState x:Name="Unchecked">
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="(TextBlock.Foreground).(SolidColorBrush.Color)" Storyboard.TargetName="TextBlock" To="Green" Duration="0" />
</Storyboard>
</VisualState>
<VisualState x:Name="Indeterminate" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="Border" CornerRadius="4" Background="{TemplateBinding Background}">
<StackPanel>
<TextBlock Name="TextBlock" FontFamily="/Resources/#Entypo" Text="🔆" FontSize="87" Foreground="#909090" HorizontalAlignment="Center" Margin="0,-25,0,0" />
<TextBlock FontFamily="Proxima Nova Rg" Text="Stimulator" FontSize="18" Foreground="{StaticResource BlackBrush}" HorizontalAlignment="Center" Margin="0,12,0,0" />
</StackPanel>
</Border>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Each VisualStateGroup contains a collection of VisualState objects that are mutually exclusive
This is actually true. However there is still one more requirement, that's each VisualState should not affect the same properties. In this case your Unchecked state and Disabled state affect the same property Foreground of the same element.
So I don't think we can have any elegant solution for this. We just have this work-around (this is in fact used commonly when styling element in WPF). We need some fake element called DisabledTextBlock, this should be placed in the same Grid with the original element TextBlock. Once the Disabled state comes, that fake element should be shown and hide the original one as well as hide all the effect of the Unchecked (or Checked) state and bring the effect of Disabled to the front. Here is the working code:
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border Background="Transparent">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CheckStates">
<!-- unchanged -->
</VisualStateGroup>
<VisualStateGroup x:Name="CommonStates">
<VisualState Name="Disabled">
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
Storyboard.TargetName="DisabledTextBlock" To="1" Duration="0" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="Border" CornerRadius="4" Background="{TemplateBinding Background}">
<StackPanel>
<Grid>
<TextBlock Name="TextBlock" FontFamily="/Resources/#Entypo" Text="🔆"
FontSize="87" Foreground="#909090" HorizontalAlignment="Center"
Margin="0,-25,0,0" Background="Transparent"/>
<!-- the fake element -->
<TextBlock Name="DisabledTextBlock" Opacity="0"
FontFamily="{Binding FontFamily, ElementName=TextBlock}"
Text="{Binding Text,ElementName=TextBlock}"
FontSize="{Binding FontSize,ElementName=TextBlock}"
Foreground="Pink" HorizontalAlignment="Center"
Margin="{Binding Margin, ElementName=TextBlock}"
Background="Transparent"
FontStyle="{Binding FontStyle,ElementName=TextBlock}"
FontWeight="{Binding FontSize, ElementName=TextBlock}"/>
</Grid>
<TextBlock FontFamily="Proxima Nova Rg" Text="Stimulator" FontSize="18"
Foreground="Black" HorizontalAlignment="Center" Margin="0,12,0,0"/>
</StackPanel>
</Border>
</Border>
</ControlTemplate>
You may have some new requirement, however the idea here is clear. That's the only work-around I think.
I have a UserControl with some buttons. My button have the default FocusVisualStyle (the border around the button).
When I move my user control with the mouse, this dotted border does not move with it. When you hover over another part of the screen it moves into the correct position.
I don't want to set the FocusVisualStyle to null, because I need to see what element is focused. I tried to create my own FocusVisualStyle but it behaves like the default one.
Can I somehow synchronize this border with the rest of the elements?
Thanks for help
As stated by bitbonk, the RenderTransform does not cause another arrange pass so the focus visual will not move. You can read this Dr. WPF article which discusses the issue as well as providing a couple of workarounds. The easiest in your case is to just put an AdornerDecorator within your UserControl around the content of your UserControl so that the AdornerLayer is moved too.
When you use RenderTransform the FocusVisualStyle rectangle is not moved with the button because it is rendered in the layout pass. That is the whole point of the RenderTransform: transform any visual and ignore the layout of the rest of the visual tree.
You will have to use LayoutTransform, or Button.Margin or Canvas.Left, Canvas.Top to move your button.
This (somewhat cryptic) excerpt from the MSDN speaks to the more general form of this issue:
Focus visual styles act exclusively for keyboard focus. As such, focus visual styles are a type of accessibility feature. If you want UI changes for any type of focus, whether via mouse, keyboard, or programmatically, then you should not use focus visual styles, and should instead use setters and triggers in styles or templates that are working from the value of general focus properties such as IsFocused or IsFocusWithin.
Today, that last bit about using triggers would probably be better replaced with a suggestion that you use the Visual State Manager. Here's some XAML that does it that way, but also preserves the original setting for a Button's FocusVisualStyle. That setting draws a dashed rectangle two pixels inside a Button when it receives focus from the keyboard. My Storyboards for the Focused and Unfocused visual states add and remove a solid rectangle, four pixels inside a Button, whenever it gains or loses the focus for any reason.
If you create a pair of Buttons in a WPF Window and manipulate the focus by keyboard and by mouse, clicking those Buttons with the spacebar and the mouse, you'll see that a Button often has the focus when the FocusVisualStyle doesn't appear. Again, this (according to the excerpt above) is by design. (I'm puzzled as to how this is an "accessibility feature," as it can easily neglect to show which control will be clicked the next time the spacebar is pressed. My inclination is just not to use it at all.)
NOTE: The code below is for a custom control derived from Button, called "XLButton." You can change the Style's TargetType to Button if you don't want to create a custom control to try this.
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ExLuminaControls">
<Style x:Key="FocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="2"
SnapsToDevicePixels="true"
Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"
StrokeThickness="1"
StrokeDashArray="1 2"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<SolidColorBrush x:Key="Button.Static.Border" Color="#FF707070"/>
<Style TargetType="{x:Type local:XLButton}">
<Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
<Setter Property="BorderBrush" Value="{StaticResource Button.Static.Border}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="{x:Null}"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:XLButton}">
<Grid>
<Border x:Name="border"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
SnapsToDevicePixels="true"/>
<Rectangle x:Name ="glow" Fill="White" Opacity="0"/>
<Rectangle x:Name="shade" Fill="Black" Opacity="0"/>
<ContentPresenter x:Name="contentPresenter"
Focusable="False"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
<Rectangle x:Name="dis" Fill="Gray" Opacity="0"/>
<Rectangle x:Name="foc" Margin="4" SnapsToDevicePixels="true"
Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"
StrokeThickness="1" Opacity="0"/>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="CommonStates">
<VisualState Name="Normal">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="glow"
Storyboard.TargetProperty="Opacity"
To="0" Duration="0:0:.1"/>
</Storyboard>
</VisualState>
<VisualState Name="MouseOver">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="glow"
Storyboard.TargetProperty="Opacity"
To=".25" Duration="0:0:.1"/>
</Storyboard>
</VisualState>
<VisualState Name="Pressed">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="shade"
Storyboard.TargetProperty="Opacity"
To=".25" Duration="0:0:0"/>
</Storyboard>
</VisualState>
<VisualState Name="Disabled">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="dis"
Storyboard.TargetProperty="Opacity"
To=".25" Duration="0:0:0"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup Name="FocusStates">
<VisualState Name="Focused">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="foc"
Storyboard.TargetProperty="Opacity"
To="1" Duration="0:0:.1"/>
</Storyboard>
</VisualState>
<VisualState Name="Unfocused">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="foc"
Storyboard.TargetProperty="Opacity"
To="0" Duration="0:0:.1"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
I am trying to create a hyperlink button with an image where image would have a hover effect (changed on MouseOver). I was able to create a hyperlink button with one image, but stuck on hover effect on the image. Please help!
Here is my User Control:
<UserControl.Resources>
<Style x:Key="NavigationButton_Middle" TargetType="HyperlinkButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="HyperlinkButton">
<Grid Background="{TemplateBinding Background}" Cursor="{TemplateBinding Cursor}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimation Storyboard.TargetName="Background"
Storyboard.TargetProperty="Color"
To="White"
Duration="00:00:0.1">
</ColorAnimation>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed"/>
<VisualState x:Name="Disabled"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="Edge" BorderBrush="#FFD9D9D9" BorderThickness="1" CornerRadius="1" Height="32" Width="48">
<Border.Background>
<SolidColorBrush x:Name="Background" Color="#FFF4F4F4" />
</Border.Background>
<ContentPresenter Content="{TemplateBinding Content}" />
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
XAML:
<HyperlinkButton x:Name="HomeButton" Style="{StaticResource NavigationButton_Middle}" ToolTipService.ToolTip="Home" Visibility="Visible" Margin="0" HorizontalAlignment="Right">
<Image Source="home.png" Margin="13,5,13,5" Height="20" Width="20"/>
</HyperlinkButton>
Thank you for your help in advance!
Since you want to change the actual content of the button (which is unknown to the Style), it is hard to do without hurting the re-usability of the style. However, if you don't mind that, you can move the Image inside the Template, where the ContentPresenter is now. Then you can modify it by name from your storyboard.
I have a custom style for Buttons in my Silverlight project and want to set the Foreground (and other properties) of the text in the button. But my idea is to use a ContentPresenter in the style. This way, I can still put whatever I want in the Button.
But if there's text as Content, I want to be able to set certain properties, like Foreground, FontFamily, FontSize, etc. I also want to change these properties on hover, etc.
This is my style (simplified):
<Style x:Key="ButtonStyle" TargetType="Button">
<!-- There are other properties here, but I left them out for brevity -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="ButtonBorder"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}">
<ContentPresenter x:Name="ButtonContent"
Content="{TemplateBinding Content}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
</ContentPresenter>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver">
<Storyboard>
<!-- I would like to change the Foreground here -->
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
All information I find, tell me to add TextBlock.Foreground="..." to the ContentPresenter, i.e.:
<ContentPresenter x:Name="ButtonContent"
Content="{TemplateBinding Content}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
TextBlock.Foreground="{TemplateBinding Foreground}">
</ContentPresenter>
However, this doesn't work for me. I get an error saying that the attachable property Foreground isn't available for the type TextBlock. Is this because that solution will only work in WPF, and how can I then set this property in Silverlight?
I know I can just set the Foreground via a <Setter> tag, but then how can I change it on mouse over? I want to set the Foreground of the ContentPresenter, so that in the MouseOver VisualState, I can change it.
(sorry for my bad english)
You can just set the properties on the Button, it should work:
<Button Style="{StaticResource ButtonStyle}" Content="Test" Foreground="Red" FontFamily="Georgia" FontSize="25"/>
Or you can create a style for your text button:
<Style x:Key="ButtonTextStyle" TargetType="Button" BasedOn="{StaticResource ButtonStyle}">
<Setter Property="Foreground" Value="Red"/>
</Style>
and use it:
<Button Style="{StaticResource ButtonTextStyle}" Content="Test"/>
EDIT:
Try to put a ContentControl in the place of the ContentPresenter - So you will have the ForegroundProperty and on you VisualState, inside your StoryBoard:
<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ButtonContent">
<DiscreteObjectKeyFrame KeyTime="0" Value="Red"/>
</ObjectAnimationUsingKeyFrames>
Have a look at the base line control template used for Button # http://msdn.microsoft.com/en-us/library/cc278069(v=vs.95).aspx, I would recommend you take that control and then adjust the mouse over based on your requirements.
So for you to on mouse over to red, I would do something like the following
<VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimation Duration="0" Storyboard.TargetName="BackgroundAnimation" Storyboard.TargetProperty="Opacity" To="1"/>
<ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)" To="Red"/>
<ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[2].(GradientStop.Color)" To="Red"/>
<ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[3].(GradientStop.Color)" To="Red"/>
</Storyboard>
</VisualState>
NB this was for the silverlight 3 beta, RTM seems to handle grouping entirely differently.
I've a grid that is working fine, which I'm using the GroupDescriptions property like so:
<data:DataGrid.GroupDescriptions>
<cm:PropertyGroupDescription PropertyName="ClientName" />
</data:DataGrid.GroupDescriptions>
This works brilliantly, it's only when the datagrid is rendered, it shows the grouping by "ClientName". Obviously I'd like that to be "Client Name". I cannot for the life of me see a property that would allow me to set this?
FWIW this has changed in SL3 RTM:
Silverlight 3 Beta
[Xaml]
<dataGrid.GroupDescriptions>
<windata:PropertyGroupDescription PropertyName=”State” />
</dataGrid.GroupDescriptions>
Silverlight 3 RTM
[c#]
pagedCollectionView.GroupDescriptions.Add(new PropertyGroupDescription(“State”));
You need to use the PagedCollectionView class to accomplish this, it can't be done in Xaml anymore. See http://msdn.microsoft.com/en-us/library/dd833072(VS.95).aspx
And from that link, here's how to change the text in the group header:
When items are grouped in the DataGrid, each group has a header. You can change the appearance of the DataGridRowGroupHeader by defining a custom Style and adding it to the RowGroupHeaderStyles collection. If you have multiple levels of grouping, you can apply different styles to each group level. Styles are applied in the order in which they are defined. For example, if you define two styles, the first will be applied to top level row groups. The second style will be applied to all row groups at the second level and lower. The DataContext of the DataGridRowGroupHeader is the CollectionViewGroup that the header represents.
So a quick and dirty example would be:
<dataControls:DataGrid x:Name="Grid">
<dataControls:DataGrid.RowGroupHeaderStyles>
<Style TargetType="dataControls:DataGridRowGroupHeader">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<TextBlock Text="My text."/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</dataControls:DataGrid.RowGroupHeaderStyles>
</dataControls:DataGrid>
In practice you probably want to include the other control parts that are specified for DataGridRowGroupHeader so that you can expand and collapse them. As with everything WPF, instead of just setting a "GroupText" property you have to go whole 9 yards!
Having the same problem as DaRKoN_ and having read James Cadd insightful answer I solved my own problem. By using Blend I have extracted the template for the DataGridRowGroupHeader. You can use the code below to customize it:
<data:DataGrid>
<data:DataGrid.RowGroupHeaderStyles>
<Style TargetType="data:DataGridRowGroupHeader">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="data:DataGridRowGroupHeader">
<dataPrimitives:DataGridFrozenGrid x:Name="Root" Background="{TemplateBinding Background}">
<dataPrimitives:DataGridFrozenGrid.Resources>
<ControlTemplate x:Key="ToggleButtonTemplate" TargetType="ToggleButton">
<Grid Background="Transparent">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimation Duration="0" Storyboard.TargetName="CollapsedArrow" Storyboard.TargetProperty="(Stroke).Color" To="#FF6DBDD1"/>
<ColorAnimation Duration="0" Storyboard.TargetName="ExpandedArrow" Storyboard.TargetProperty="(Fill).Color" To="#FF6DBDD1"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ColorAnimation Duration="0" Storyboard.TargetName="CollapsedArrow" Storyboard.TargetProperty="(Stroke).Color" To="#FF6DBDD1"/>
<ColorAnimation Duration="0" Storyboard.TargetName="ExpandedArrow" Storyboard.TargetProperty="(Fill).Color" To="#FF6DBDD1"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimation Duration="0" Storyboard.TargetName="CollapsedArrow" Storyboard.TargetProperty="Opacity" To=".5"/>
<DoubleAnimation Duration="0" Storyboard.TargetName="ExpandedArrow" Storyboard.TargetProperty="Opacity" To=".5"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="CheckStates">
<VisualState x:Name="Checked"/>
<VisualState x:Name="Unchecked">
<Storyboard>
<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="CollapsedArrow" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="ExpandedArrow" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Path x:Name="CollapsedArrow" Stretch="Uniform" Stroke="#FF414345" HorizontalAlignment="Center" VerticalAlignment="Center" Width="5" Visibility="Collapsed" Data="F1 M 0,0 L 0,1 L .6,.5 L 0,0 Z"/>
<Path x:Name="ExpandedArrow" Fill="#FF414345" Stretch="Uniform" HorizontalAlignment="Center" VerticalAlignment="Center" Width="6" Data="F1 M 0,1 L 1,1 L 1,0 L 0,1 Z"/>
</Grid>
</ControlTemplate>
</dataPrimitives:DataGridFrozenGrid.Resources>
<dataPrimitives:DataGridFrozenGrid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</dataPrimitives:DataGridFrozenGrid.ColumnDefinitions>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CurrentStates">
<VisualState x:Name="Regular"/>
<VisualState x:Name="Current">
<Storyboard>
<DoubleAnimation Duration="0" Storyboard.TargetName="FocusVisual" Storyboard.TargetProperty="Opacity" To="1"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<dataPrimitives:DataGridFrozenGrid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</dataPrimitives:DataGridFrozenGrid.RowDefinitions>
<Rectangle Fill="#FFFFFFFF" Height="1" Grid.Column="1" Grid.ColumnSpan="5"/>
<Rectangle x:Name="IndentSpacer" Grid.Column="1" Grid.Row="1"/>
<ToggleButton x:Name="ExpanderButton" Height="15" Margin="2,0,0,0" Width="15" Template="{StaticResource ToggleButtonTemplate}" Grid.Column="2" Grid.Row="1"/>
<StackPanel Margin="0,1,0,1" VerticalAlignment="Center" Grid.Column="3" Grid.Row="1" Orientation="Horizontal">
<TextBlock x:Name="PropertyNameElement" Margin="4,0,0,0" Visibility="{TemplateBinding PropertyNameVisibility}"/>
<TextBlock Margin="4,0,0,0" Text="{Binding Name}"/>
<TextBlock x:Name="ItemCountElement" Margin="4,0,0,0" Visibility="{TemplateBinding ItemCountVisibility}"/>
</StackPanel>
<Rectangle Fill="#FFD3D3D3" Height="1" Grid.Column="1" Grid.ColumnSpan="5" Grid.Row="2"/>
<Rectangle x:Name="FocusVisual" Stroke="#FF6DBDD1" StrokeThickness="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" IsHitTestVisible="false" Opacity="0" Grid.Column="1" Grid.ColumnSpan="4" Grid.RowSpan="3"/>
<dataPrimitives:DataGridRowHeader x:Name="RowHeader" Grid.RowSpan="3" dataPrimitives:DataGridFrozenGrid.IsFrozen="True"/>
</dataPrimitives:DataGridFrozenGrid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</data:DataGrid.RowGroupHeaderStyles>
</data:DataGrid>
I have added blank lines before and after the TextBlock that contains the offending text. You can completely turn it off by using the following XAML:
<data:DataGrid>
<data:DataGrid.RowGroupHeaderStyles>
<Style TargetType="data:DataGridRowGroupHeader">
<Setter Property="PropertyNameVisibility" Value="Collapsed"/>
</Style>
<data:DataGrid.RowGroupHeaderStyles>
</data:DataGrid>
If you want to modify the text you will have both remove the name of the TextBlock and add a Text attribute:
<TextBlock Margin="4,0,0,0" Visibility="{TemplateBinding PropertyNameVisibility}" Text="Client Name:"/>
The DataGridRowGroupHeader will look for a TextBlock named PropertyNameElement and set the text from code, and to avoid that happening you will have to remove the name from the template.
I have implemented the template graciously provided by Martin Liversage (thanks!) but I can't seem to control the height of the group data row. Adjusting the size of the rectangles etc just takes up more room in the row, hiding other parts. It seems the row height is being controlled by the container for the row, outside the influence of the template.
TIA
Toby
Found the answer!
Refer to http://msdn.microsoft.com/en-us/library/cc278075(VS.95).aspx
This defines all the control styles and templates and zooming into the DataGrid and viewing the Default Template you will find:
<data:DataGrid.RowGroupHeaderStyles>
<Style TargetType="local:DataGridRowGroupHeader">
<Setter Property="Cursor" Value="Arrow" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="Background" Value="#FFE4E8EA" />
<Setter Property="Height" Value="20"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:DataGridRowGroupHeader">
...snip...
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</data:DataGrid.RowGroupHeaderStyles>
Setting the Height property does the job.
Set the Name property of the Display attribute applied to the property you're grouping by:
[System.ComponentModel.DataAnnotations.Display( Name = "My Property" )]
public string MyProperty {get;set;}
looking at the Display class, one might think GroupName would be the property you set, but alas it it not.
I understand 'muddying up' the model with display data isn't always how everyone (myself included) likes to do things .. but in contrast to the amount of work required to do it in the above ways .. I think it wins out this time.