How to set Button Height dynamically in RowDetails RowHeaderTemplate - wpf

I have a datagrid with a Deails-View which shows another datagrid. This second grid is normally not shown. In the row header there is a button with a left arrow, when this is clicked, the details-grid is shown and the arrow changes to a down-arrow. This all works perfectly, but there is only a "small area" in the rowheader which accepts clicks. This is the code of my RowHeader-Template:
<DataGrid.RowHeaderTemplate>
<DataTemplate>
<Button Click="DataGridRowHeader_Button_Click" Cursor="Hand" HorizontalAlignment="Center" Width="40" >
<Button.Style>
<Style TargetType="Button">
<Style.Setters>
<Setter Property="VerticalAlignment" Value="Top" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid >
<Border Background="Chartreuse" BorderBrush="Black" BorderThickness="5"></Border>
<Path Fill="Blue" Data="M 0,0 14,7 0,14 Z" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow}},Path=DetailsVisibility}" Value="Visible">
<Setter Property="Background" Value="Salmon" />
<Setter Property="Height" Value="125" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}" >
<Grid >
<Border Background="LightCoral" BorderBrush="Black" BorderThickness="1" />
<Path Fill="Blue" Data="M 0,0 14,0 7,14 Z" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</DataTemplate>
</DataGrid.RowHeaderTemplate>
The line with
Setter Property = "Height" Value="125"
expands the button
but the button does not fit into the row header. With A small value, the button shrinks, and with a high button, the row header expands unnecessarily.
I tried to bind the Heigth-value to some parameters, e. g.
<Setter Property="Height" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGridRowDetailsEventArgs}}, Path=DetailsElement.ActualHeight}" />
(DataGridRowDetailsEventArgs captures the correct height-value when I look in code-behind:
private void dg_RowDetailsVisibilityChanged(object sender, DataGridRowDetailsEventArgs e)
{
Trace.WriteLine(e.DetailsElement.ActualHeight);
}
). Using
<Setter Property="Height" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGridRowDetails}}, Path=ActualHeight}" />
does not work either, the RowHeader is always expanded to a value which seems to be the height of the whole Master-Datagrid.
Any ideas how I can size the button correctly?
(Sorry for the colors, they are used only to see the different parts of the UI).
Hucky

I think your problem lies within the ControlTemplate of the DataGridRowHeader where you can't properly align content (in your case your Button)
Instead of overriding the ContentTemplate every single time with full Xaml I tried another approach by using a MarkupExtension(AlignmentControlTemplateExtension), which does the necessary steps for me (clone and adjust the default ControlTemplate), so that I only have to do something like this
<Setter Property="Template" Value="{AlignmentControlTemplate Type={x:Type WhatEverType}}" />
To solve your problem you have to do only some few steps
Set the DataGridRowHeader ControlTemplate
<DataGrid.RowHeaderStyle>
<Style TargetType="DataGridRowHeader">
<Setter Property="Template" Value="{stackoverflow:AlignmentControlTemplate Type={x:Type DataGridRowHeader}}"></Setter>
</Style>
</DataGrid.RowHeaderStyle>
Set VerticalContentAlignment and/or HorizontalContentAlignment however you like it (in your case VerticalContentAlignment=Stretch)
<DataGrid.RowHeaderStyle>
<Style TargetType="DataGridRowHeader">
<Setter Property="VerticalContentAlignment" Value="Stretch"></Setter>
<Setter Property="Template" Value="{stackoverflow:AlignmentControlTemplate Type={x:Type DataGridRowHeader}}"></Setter>
</Style>
</DataGrid.RowHeaderStyle>
Some adjustments in your Style.Triggers
Replace
<Setter Property="Height" Value="125" />
with
<Setter Property="VerticalAlignment" Value="Stretch" />
Edit Final Result
<DataGrid>
<!-- Step 1 -->
<DataGrid.RowHeaderStyle>
<Style TargetType="DataGridRowHeader">
<Setter Property="VerticalContentAlignment" Value="Stretch"></Setter> <!-- Step 2 -->
<Setter Property="Template" Value="{stackoverflow:AlignmentControlTemplate Type={x:Type DataGridRowHeader}}"></Setter>
</Style>
</DataGrid.RowHeaderStyle>
<!-- /Step 1 -->
<DataGrid.RowHeaderTemplate>
<DataTemplate>
<Button Click="DataGridRowHeader_Button_Click" Cursor="Hand" HorizontalAlignment="Center" Width="40" >
<Button.Style>
<Style TargetType="Button">
<Style.Setters>
<Setter Property="VerticalAlignment" Value="Top" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid >
<Border Background="Chartreuse" BorderBrush="Black" BorderThickness="5"></Border>
<Path Fill="Blue" Data="M 0,0 14,7 0,14 Z" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridRow}},Path=DetailsVisibility}" Value="Visible">
<Setter Property="Background" Value="Salmon" />
<Setter Property="VerticalAlignment" Value="Stretch" /> <!-- Step 3 -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}" >
<Grid >
<Border Background="LightCoral" BorderBrush="Black" BorderThickness="1" />
<Path Fill="Blue" Data="M 0,0 14,0 7,14 Z" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</DataTemplate>
</DataGrid.RowHeaderTemplate>
...
</DataGrid>

Related

Border per column header

I want to create a table on wpf that each column header has a round corner this is what i got so far:
as you can see, i have the desired outcome with a little un desired outcome.
the undesired out come is that the all data grid header itself (not the columns) is getting the same border, i need to make it transparent, how can i do that?
this is the part of the style:
<DataGrid.ColumnHeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="SeparatorBrush" Value="Transparent"/>
<Setter Property="Margin" Value="2"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Border CornerRadius="5 5 0 0" BorderThickness="1" BorderBrush="Black">
<TextBlock Text="{Binding }"/>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.ColumnHeaderStyle>
for some reasons, DataGrid has a blank DataGridColumnHeader in its template. that blank columns doesn't have DataContext value (null). So change border brush to transparent in a DataTrigger:
<ControlTemplate>
<Grid>
<Border CornerRadius="5 5 0 0" BorderThickness="1" >
<TextBlock Text="{Binding}"/>
<Border.Style>
<Style TargetType="Border">
<Setter Property="BorderBrush" Value="Black"/>
<Style.Triggers>
<DataTrigger Binding="{Binding}" Value="{x:Null}">
<Setter Property="BorderBrush" Value="Transparent"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
</Grid>
</ControlTemplate>
improved version, which uses ContentPresenter in header template and test Content in a trigger
<ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
<Grid>
<Border CornerRadius="5 5 0 0" BorderThickness="1" >
<ContentPresenter/>
<Border.Style>
<Style TargetType="Border">
<Setter Property="BorderBrush" Value="Black"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Content, RelativeSource={RelativeSource TemplatedParent}}" Value="{x:Null}">
<Setter Property="BorderBrush" Value="Transparent"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
</Grid>
</ControlTemplate>
header is not necessary a text, e.g.:
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=Name}">
<DataGridTextColumn.Header>
<Border Background="Cyan">
<TextBlock Text="NAME" Margin="5"/>
</Border>
</DataGridTextColumn.Header>
</DataGridTextColumn>
</DataGrid.Columns>

Two buttons with TextBox in the middle, ControlTemplate

Having a problem with this style, I can't find a way to make the following layout:
So I need just the corners of the buttons on the outside to be rounded, but I don't know how to do this, as the ControlTemplate for the buttons can't have the CornerRadius property as I would have to round all the corners.
This is the style I have got now, it just produces the style as seen above but with no CornerRadius.
P.S. There does seem to be an issue with the far left button going behind the TextBox which I noticed when the TextBox was slightly transparent. Not sure what is causing this!
XAML
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Controls">
<Style TargetType="{x:Type local:NumericUpDown}">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="HorizontalContentAlignment" Value="Right" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Focusable" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:NumericUpDown}">
<ControlTemplate.Resources>
<Style TargetType="Button">
<Setter Property="Padding" Value="5" />
<Setter Property="Background" Value="#434953" />
<Setter Property="MinWidth" Value="30" />
<Setter Property="MinHeight" Value="25" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" ClipToBounds="True">
<Border x:Name="Border1"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="#434953">
<ContentPresenter Content="{Binding Path=Content, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Button}}}" />
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Border1" Property="Background" Value="#834953"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ControlTemplate.Resources>
<Border Focusable="{TemplateBinding Focusable}">
<DockPanel Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
VerticalAlignment="Center"
Focusable="False">
<Button x:Name="PART_DecreaseButton" DockPanel.Dock="Left">
<Button.Content>
<Path Data="M0,0 L1,0 0.5,1Z"
Fill="White"
Width="8"
Height="6"
Stretch="Fill"/>
</Button.Content>
</Button>
<Button x:Name="PART_IncreaseButton" DockPanel.Dock="Right">
<Button.Content>
<Path Data="M0,1 L1,1 0.5,0Z"
Width="8"
Height="6"
Fill="White"
Stretch="Fill" />
</Button.Content>
</Button>
<TextBox x:Name="PART_TextBox"
Foreground="White"
HorizontalContentAlignment="Center"
HorizontalAlignment="Stretch"
Background="#22252b"
BorderThickness="0"
MinWidth="35" IsEnabled="True" Focusable="False" />
</DockPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True" SourceName="PART_DecreaseButton">
<Setter Property="Background" Value="Red" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
you can encapsulate the whole thing and apply opacity mask like in this tutorial
http://wpf.2000things.com/2012/05/11/556-clipping-to-a-border-using-an-opacity-mask/

WPF TextBox with background color and path (image)

I've got the below style for TextBoxes in my WPF app:
<!-- http://www.thexamlproject.com/ -->
<Style x:Key="LockIcon" TargetType="Path">
<Setter Property="Height" Value="16" />
<Setter Property="Width" Value="16" />
<Setter Property="Stretch" Value="Uniform" />
<Setter Property="Data"
Value="F1 M 80.6667,55.1172L 80.6667,35.3333C 80.6667,15.851 64.815,3.05176e-005 45.3333,3.05176e-005C 25.8509,3.05176e-005 9.99998,15.851 9.99998,35.3333L 9.99998,55.1172L 0,56L 0,129.333L 45.3333,133.333L 90.6667,129.333L 90.6667,56L 80.6667,55.1172 Z M 20.6667,54.1771L 20.6667,35.3333C 20.6667,21.7318 31.7317,10.6667 45.3333,10.6667C 58.9349,10.6667 70,21.7318 70,35.3333L 70,54.1771L 45.3333,52L 20.6667,54.1771 Z " />
</Style>
<VisualBrush x:Key="LockBrush" Stretch="None" AlignmentX="Left">
<VisualBrush.Visual>
<Border BorderThickness="4" BorderBrush="Transparent">
<Path Style="{StaticResource LockIcon}" Fill="Red" />
</Border>
</VisualBrush.Visual>
</VisualBrush>
<Style x:Key="{x:Type TextBox}" TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" Value="{StaticResource LockBrush}"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="Padding" Value="22,4"/>
</Trigger>
</Style.Triggers>
</Style>
I can't seem to find a good way to apply this VisualBrush image AND a background color to the entire TextBox. I either get strange stretching issues or simply cannot fill the whole TextBox (controls stretch and are different widths depending on the rendering view).
Ideas?
Instead of a VisualBrush use a Control Template.
XAML
<Style x:Key="LockIcon"
TargetType="Path">
<Setter Property="Height"
Value="16" />
<Setter Property="Width"
Value="16" />
<Setter Property="Stretch"
Value="Uniform" />
<Setter Property="Data"
Value="F1 M 80.6667,55.1172L 80.6667,35.3333C 80.6667,15.851 64.815,3.05176e-005 45.3333,3.05176e-005C 25.8509,3.05176e-005 9.99998,15.851 9.99998,35.3333L 9.99998,55.1172L 0,56L 0,129.333L 45.3333,133.333L 90.6667,129.333L 90.6667,56L 80.6667,55.1172 Z M 20.6667,54.1771L 20.6667,35.3333C 20.6667,21.7318 31.7317,10.6667 45.3333,10.6667C 58.9349,10.6667 70,21.7318 70,35.3333L 70,54.1771L 45.3333,52L 20.6667,54.1771 Z " />
</Style>
<Style TargetType='TextBox'
x:Key='WithLockIcon'>
<Setter Property='MinHeight'
Value='26' />
<Setter Property="Padding"
Value="22,4" />
<Style.Triggers>
<Trigger Property="IsEnabled"
Value="false">
<!-- Use a control template instead of
Background property -->
<!--<Setter Property="Background"
Value="{StaticResource LockBrush}" />-->
<!--<Setter Property="Padding"
Value="22,4" />-->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBoxBase}">
<!-- Set background here!-->
<Border BorderThickness="4"
BorderBrush="Transparent"
HorizontalAlignment='Stretch'>
<StackPanel Background='#dddddd'
Orientation='Horizontal'>
<Path Style="{StaticResource LockIcon}"
Fill="Red" />
<ScrollViewer Margin="0"
x:Name="PART_ContentHost">
</ScrollViewer>
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel>
<TextBox x:Name='textbox1'
Text='Example Text'
IsEnabled='False'
Style='{StaticResource WithLockIcon}' />
<ToggleButton Checked='ToggleButton_Checked'
Unchecked='ToggleButton_Unchecked'
Content='Change State' />
</StackPanel>
I've achieved a reasonably close simulation in a similar situation.
<Window x:Class="VisualBrushMargins.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
Background="{DynamicResource WindowBackground}">
<Window.Resources>
<VisualBrush x:Key="WindowBackground" Stretch="UniformToFill">
<VisualBrush.Visual>
<Grid>
<Border Background="DarkGreen" Width="250" Height="150" />
<Canvas Height="130" Width="180" Background="DarkBlue">
<Path>
<!-- path goes here -->
</Path>
<Ellipse Width="100" Height="100" Stroke="Red" />
</Canvas>
</Grid>
</VisualBrush.Visual>
</VisualBrush>
</Window.Resources>
<Grid/>
</Window>
The path fits the canvas. Sizing the canvas and the border differently allows to add a "margin" to the brush -- note how it behaves on window resize. (Canvas is colored blue for illustrational purpose -- actually it's supposed to be transparent)

How do I add different icons to treeview items in a wpf application using xaml?

I have a treeview which has been populated using a dataset. Now I need to add different icons to the treeview nodes. How do I add them using xaml?
You need to add a custom template for the TreeViewItem like this:
<TreeView.Resources>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Tag="{Binding}" />
<Image Source="{Binding Converter={StaticResource CustomImagePathConvertor}}" />
<TextBlock Text="{Binding}" />
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</TreeView.Resources>
You can change Image Path and use Template to implement it:
<ToggleButton x:Name="Expander" ClickMode="Press">
<ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Focusable" Value="False"/>
<Setter Property="Width" Value="16"/>
<Setter Property="Height" Value="16"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Grid Height="16" Width="16">
<Image x:Name="imgBkg" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Source" TargetName="imgBkg" Value="/XXXX;component/YourImage.png"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ToggleButton.Style>
</ToggleButton>

I just need a simple WPF ToolTip Style that displays multiple lines

Due to all the noise about fancy, super, huge, and blah, blah, blah, tooltips, I cannot find the answer.
I just need a simple style that sets TextWrapping="Wrap" and allows me to set a width.
One that duplicates the existing / default style, but just word wraps.
<Window.Resources>
<Style TargetType="{x:Type ToolTip}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock TextWrapping="Wrap" Text="{Binding}" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Rectangle Width="100" Height="100" Fill="Red">
<Rectangle.ToolTip>
<ToolTip Width="100">
This is some text with text wrapping.
</ToolTip>
</Rectangle.ToolTip>
</Rectangle>
</Grid>
This example is assuming you want to be able to set the width on a per-usage basis. If you want to set it as part of the style, add it to the TextBlock element.
This style prevents a tooltip from popping up on empty strings.
<Style TargetType="ToolTip">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToolTip">
<TextBlock Text="{TemplateBinding Content}"
MaxWidth="400"
TextWrapping="Wrap"/>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Content" Value="">
<Setter Property="Visibility" Value="Collapsed" />
</Trigger>
</Style.Triggers>
</Style>
Or using ContentTemplate:
<Style TargetType="{x:Type ToolTip}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding}"
MaxWidth="400"
TextWrapping='Wrap' />
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Content" Value="">
<Setter Property="Visibility" Value="Collapsed" />
</Trigger>
</Style.Triggers>
</Style>
If you just want to get the effects below, have a read at this post.

Resources