How do I make a button shape a custom Path in WPF? - wpf

I have a button using the ControlTemplate below.
<ControlTemplate x:Key="ControlTemplate" TargetType="{x:Type Button}">
<Grid>
<Path x:Name="ButtonPath" Fill="{Binding IsSelected, Converter={StaticResource ArrowBackgroundColorConverter}, UpdateSourceTrigger=PropertyChanged}"
Data="{Binding Converter={StaticResource ArrowPathSelector}}" Stretch="UniformToFill" Margin="0 0 -35 0"></Path>
<TextBlock Grid.Column="0" Grid.Row="0" FontFamily="{StaticResource ApplicationFont}" FontSize="{StaticResource Heading3}" HorizontalAlignment="Center" Margin="35 0 0 0" VerticalAlignment="Center" Text="{Binding Title}" Foreground="White"/>
</Grid>
</ControlTemplate>
but when I click the Button in my app it is not outlining the Path but instead the original Button.
I cant quite figure out how to get the button to reflect the Path itself. Any help is greatly appreciated, Thanks!

If you want to change the shape of the focus rectangle (the dotted border indicating keyboard focus), you need to create a custom FocusVisualStyle that draws a dotted path in the same shape as your button content:
<Style x:Key="ButtonFocusRectangle" TargetType="Control">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Path Stroke="Black"
StrokeDashArray="2 2"
Data="M 0,0 50,50 100,0 Z"
Stretch="UniformToFill"
Margin="0 0 -35 0" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Then, on the button, set FocusVisualStyle="{StaticResource ButtonFocusRectangle}". Note that it would probably be cleaner to define a custom Style for the button, and have it apply both the Template and FocusVisualStyle for you.
Alternatively, you can just get rid of the focus visual entirely by setting FocusVisualStyle="{x:Null}". You might opt to do this, and simply draw a focus hint in your regular Template by, for example, changing the path's stroke with an IsKeyboardFocused trigger.
As an aside, you probably want to set Stretch to Uniform on the paths in both templates. UniformToFill can cause clipping.

Have you tried putting your Path and your textblock the other way around:
<ControlTemplate x:Key="ControlTemplate" TargetType="{x:Type Button}">
<Grid>
<TextBlock Grid.Column="0" Grid.Row="0" FontFamily="{StaticResource ApplicationFont}" FontSize="{StaticResource Heading3}" HorizontalAlignment="Center" Margin="35 0 0 0" VerticalAlignment="Center" Text="{Binding Title}" Foreground="White"/>
<Path x:Name="ButtonPath" Fill="{Binding IsSelected, Converter={StaticResource ArrowBackgroundColorConverter}, UpdateSourceTrigger=PropertyChanged}"
Data="{Binding Converter={StaticResource ArrowPathSelector}}" Stretch="UniformToFill" Margin="0 0 -35 0"></Path>
</Grid>
</ControlTemplate>

Related

Wpf change the background colour of an expander's header only

I have looked at many questions/answers but couldn't find what I was exactly looking for,
I am trying to change the background colour of the expander's header only and not have the same colour continue for the content within the expander. Preferably within xaml but a vb.net solution would suffice.
(Any comments or suggestions will be helpful)
If this is a duplicated question please direct me to the answer and leave the question open to help others avoid the same issue in the future!
Thanks.
I am not sure whether this is what you are exactly looking for, but you could change the header background by simply doing that:
<Expander VerticalAlignment="Center">
<Expander.Header>
<Grid Background="LightBlue">
<TextBlock Text="Expander Header"/>
</Grid>
</Expander.Header>
<StackPanel>
<TextBlock Text="Cotent"></TextBlock>
</StackPanel>
</Expander>
Or you could override the default Expander's Header DataTemplate by using HeaderTemplate
<Window.Resources>
<DataTemplate x:Key="HeaderText">
<Border Height="25" Background="LightBlue">
<TextBlock Text="{Binding}"
Margin="4 0"
VerticalAlignment="Center"
Foreground="White"
FontSize="11"
FontWeight="Normal"
/>
</Border>
</DataTemplate>
<Style TargetType="{x:Type Expander}">
<Setter Property="HeaderTemplate" Value="{StaticResource HeaderText}"/>
</Style>
</Window.Resources>
<Grid x:Name="LayoutRoot">
<Expander VerticalAlignment="Center" Header="Expander Header">
<StackPanel>
<TextBlock Text="Cotent"></TextBlock>
</StackPanel>
</Expander>
</Grid>
I had a lot of problems with getting the header background set.
I found the easiets weay to do it was to simply make a coloured rectangle and put it behind the expander. (use Margin and Height to make it fit)
<Rectangle Grid.Row="1" Grid.Column="0" Fill="LightBlue" Height="33" Margin="0,0,0,-35" />
or use a border if you want rounded corners:
<Border CornerRadius="15" Height="33" Margin="0,0,0,-35" Background="LightBlue" />

Icon DataTemplate for Icon MenuItem

I create some graphics for menuitem.icon.
<DataTemplate x:Key="navigation_arrow">
<DockPanel LastChildFill="True" HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid>
<Canvas>
<Rectangle Fill="Red" Width="30" Height="20"/>
</Canvas>
</Grid>
</DockPanel>
</DataTemplate>
And then I try use this template for menuitem.icon
<MenuItem Header="" Icon="{Binding navigation_arrow}"/>
But it is empty menuitem. If I insert template code in MenuItem.Icon all work good
First: your binding is wrong. You must use this key as {DynamicResource navigation_arrow} or {StaticResource navigation_arrow}.
But It's not working. Your icon will be "System.Windows.DataTemplate" string, in this case.
DataTemplate means "It will apply a template for a data". You haven't got Data for Icon, so it cannot apply template for this.
You have to add concrete item as icon (like you mentioned) or create a style for it:
<Style TargetType="MenuItem">
<Setter Property="Icon">
<Setter.Value>
<DockPanel LastChildFill="True" HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid>
<Canvas>
<Rectangle Fill="Red" Width="30" Height="20"/>
</Canvas>
</Grid>
</DockPanel>
</Setter.Value>
</Setter>
</Style>
but the fancy way is:
use DrawingBrush from resource
You can use a ContentControl:
<MenuItem.Icon>
<ContentControl ContentTemplate="{StaticResource navigation_arrow}"/>
</MenuItem.Icon>

Binding Style template - a way that works?

I've been trying (failing) to create a button template, to be shared between libraries. The template is (currently) basically a bordered circle inside a bordered circle. In order to make the inner border's size smaller than the outer, I use a Converter on the binding. I'd like to pass in a property of the TemplatedParent as a ConverterParameter but it just doesn't work, apparently by design. The bit that doesn't work is because I'm trying to bind a ConveterParameter to a TemplatedParent property.
Here's my Style def (in a ResourceDictionary):
<SolidColorBrush x:Key="MyBorderFillColour">Yellow</SolidColorBrush>
<SolidColorBrush x:Key="MyBorderEdgeColour">#ff652f00</SolidColorBrush>
<SolidColorBrush x:Key="MyGeneralFillColour">#ffffffbd</SolidColorBrush>
<s:Int32 x:Key="MyBorderThickness">10</s:Int32>
<l:RelativeSizeConverter x:Key="RelativeSizeConverter" />
<Style x:Key="MyButtonStyle" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Ellipse
Fill="{StaticResource MyBorderFillColour}"
StrokeThickness="2"
Stroke="{StaticResource MyBorderEdgeColour}"
Height="{TemplateBinding Height}"
Width="{TemplateBinding Width}" />
<Ellipse StrokeThickness="2"
Stroke="{StaticResource MyBorderEdgeColour}"
Fill="{StaticResource MyGeneralFillColour}"
Height="{Binding Path=Height,
RelativeSource={RelativeSource TemplatedParent},
Converter={StaticResource RelativeSizeConverter},
ConverterParameter={StaticResource MyBorderThickness}}"
Width="{Binding Path=Width,
RelativeSource={RelativeSource TemplatedParent},
Converter={StaticResource RelativeSizeConverter},
ConverterParameter={TemplateBinding BorderThickness}}" />
<TextBlock
Text="{TemplateBinding Content}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="Black"
FontFamily="Calibri"
FontWeight="Bold"
FontSize="17" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The second Ellipse's Height works using a StaticResource, but Width doesn't work using the TemplateBinding approach. I've also tried
ConverterParameter={Binding Path=BorderThickness,RelativeSource={RelativeSource TemplatedParent}}
Any idea how to achieve my aim?!?
Thanks for any help,
This does not work because in order to provide a binding expression for a value, the value must be a dependency property. ConverterParameter is not a dependency property so you cannot bind its value.
How about using a slightly less generic approach, where you create a few slightly more specific value converters. You can then use a 'dot' path, so that your Button is passed as the value to the converter, you can then access teh width, border width etc... directly.
Width="{Binding Path=.,
RelativeSource={RelativeSource TemplatedParent},
Converter={StaticResource RelativeWidthWidthBorderSizeConverter}}"
You don't need to do anything so fancy here; the layout system will handle it for you (using the Margin property and the stretching behavior of the Ellipse in a container):
<Grid Height="{TemplateBinding Height}" Width="{TemplateBinding Width}">
<Ellipse
Fill="{StaticResource MyBorderFillColour}"
StrokeThickness="2"
Stroke="{StaticResource MyBorderEdgeColour}"/>
<Ellipse StrokeThickness="2" Margin="4"
Stroke="{StaticResource MyBorderEdgeColour}"
Fill="{StaticResource MyGeneralFillColour}"/>
<TextBlock
Text="{TemplateBinding Content}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="Black"
FontFamily="Calibri"
FontWeight="Bold"
FontSize="17" />
</Grid>
Note also that you should use a ContentPresenter for the Content instead of a TextBlock for more flexibility; this currently fails the Button contract if the user of the templated button supplies more complex Content.

Customize a TextBlock in WPF

I need to "decorate" a textBlock with a custom background (say, when IsSelected)
Actually I have two separate elements textBlock and its background rectangle:
<TextBlock x:Name="StopText" Text="Eiffel Tower"
Canvas.Left="17"
FontSize="14"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
FontFamily="Giddyup Std"
Canvas.Top="-16"
Padding="5">
</TextBlock>
<Rectangle x:Name="ShadowRectangle"
Fill="SkyBlue"
Canvas.Left="18"
Width="{Binding ElementName=StopText, Path=ActualWidth}"
Height="{Binding ElementName=StopText, Path=ActualHeight}"
RadiusX="5" RadiusY="5" Opacity="0.2" Canvas.Top="-17"
LayoutTransform="{Binding ElementName=StopText, Path=LayoutTransform}"/>
Now, when I want to Hide the textBlock I need to hide the rectangle too, etc. I think there is should be a more elegant way to do it.
Any remarks appreciated.
EDIT
Border approach
<Border x:Name="ShadowRectangle"
Background="Transparent"
Canvas.Left="18"
Canvas.Top="-17"
CornerRadius="5"
Opacity="0.2"
LayoutTransform="{Binding ElementName=StopText,
Path=LayoutTransform}">
<TextBlock x:Name="StopText" Text="Eiffel Tower"
FontSize="14" VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" FontFamily="Giddyup Std"
Canvas.Top="-16" Padding="5">
</TextBlock>
</Border>
In a solution similar to your border approach, if you want no dependencies between the opacity of the border and the textblock, you could go with something like this:
<grid>
<rectangle />
<textblock />
<grid>
this should put the textblock over the rectangle since they are on the same grid cell.
They you only have to change the Rectangle.Fill when it's selected. You could do that using a DataTrigger on the Rectangle style.
You can make a Custom Control instead.
Here is a link for some ideers:
http://www.codeguru.com/cpp/i-n/internet/xml/article.php/c12521
Bind the visibility property for both the textblock and Rectangle to a bool in your viewmodel and use the BoolToVisibilityConverter to convert the bool value to a visibility value. Your XAML will look something like this:
<TextBlock x:Name="StopText" Text="Eiffel Tower"
Canvas.Left="17"
FontSize="14"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
FontFamily="Giddyup Std"
Canvas.Top="-16"
Padding="5"
Visibility="{Binding IsVis, Converter={StaticResource BooleanToVisibilityConverter}}">
</TextBlock>
<Rectangle x:Name="ShadowRectangle"
Fill="SkyBlue"
Canvas.Left="18"
Width="{Binding ElementName=StopText, Path=ActualWidth}"
Height="{Binding ElementName=StopText, Path=ActualHeight}"
RadiusX="5" RadiusY="5" Opacity="0.2" Canvas.Top="-17"
Visibility="{Binding IsVis, Converter={StaticResource BooleanToVisibilityConverter}}"/>
Using either the Border or Rectangle approach, you should be able to bind the Visibility of the decorator (Border or Rectangle) to the Visibility of the TextBlock.
<TextBlock x:Name="StopText" Text="Eiffel Tower"
Canvas.Left="17"
FontSize="14"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
FontFamily="Giddyup Std"
Canvas.Top="-16"
Padding="5">
</TextBlock>
<Rectangle x:Name="ShadowRectangle"
Fill="Red"
Canvas.Left="18"
Width="{Binding ElementName=StopText, Path=ActualWidth}"
Height="{Binding ElementName=StopText, Path=ActualHeight}"
RadiusX="5" RadiusY="5" Opacity="0.2" Canvas.Top="-17"
LayoutTransform="{Binding ElementName=StopText, Path=LayoutTransform}"
Visibility="{Binding ElementName=StopText}"/>
Let me suggest to use templated label (textblock wont accept templates and took more characters to type -). For your convenience, I wrapped it in a style:
<Window x:Class="_4203457.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">
<Window.Resources>
<Style TargetType="{x:Type Label}">
<Setter Property="FontSize" Value="14"/>
<Setter Property="FontFamily" Value="Giddyup Std"/>
<Setter Property="Padding" Value="5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Label}">
<Grid>
<TextBlock Text="{TemplateBinding Content}"
FontSize="14"
Padding="5"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
FontFamily="Giddyup Std"
/>
<Rectangle x:Name="ShadowRectangle"
Fill="SkyBlue"
RadiusX="5" RadiusY="5" Opacity="0.5"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid Height="27" Width="454">
<Label Content="Eiffel Tower"/>
</Grid>

Remove the border of image

I want to remove the border of image which is displayed when i click on it .
**<Image x:Name="ImagePresenter" Source="{StaticResource IconDocument}" />**
<Rectangle x:Name="focusElement" Opacity="0" Stroke="{StaticResource color_506_brush}" StrokeThickness="1" IsHitTestVisible="False" RadiusX="4" RadiusY="4"/>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
Adding an image to a Silverlight UserControl results in just the image with no border displaying for me.
Xaml:
<Image x:Name="ImageText" Source="TestImage.png"/>
I assume that isn't going to help you. Could you give a bit more detail in your question and see if I can find a better answer?
Your question is not quite clear to me as silverlight doesnt add any border to image unless something in your code tells it to.
Try setting StrokeThickness to zero. (If required modify the template to set the strokethickness to zero).
Hope this helps
Yes you all were right by default the image doesn't have border. As i had two columns in my defined style it was creating two rectangles for each(one for my textblock another for image).To get rid of this I used one column defination and it worked. :)
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image x:Name="ImagePresenter" Grid.Column="0" Source="{StaticResource IconDocument}" Width="15" HorizontalAlignment="Left"/>
<TextBlock x:Name="TextPresenter" Grid.Column="0" Text="{Binding DocumentName}" Style="{StaticResource DocumentViewerOutputTextItem}" Margin="15 4 4 0"
Visibility="{Binding IsSelected, Converter={StaticResource InvertVisibilityConverter}, RelativeSource={RelativeSource TemplatedParent}}"/>
<TextBlock Grid.Column="0" Text="{Binding DocumentName}" Style="{StaticResource DocumentViewerOutputTextItem_MouseOver}" Margin="15 4 4 0"
Visibility="{Binding IsSelected, Converter={StaticResource VisibilityConverter}, RelativeSource={RelativeSource TemplatedParent}}"/>
<Rectangle x:Name="focusElement" Opacity="0" Stroke="{StaticResource color_506_brush}" StrokeThickness="1" IsHitTestVisible="False" RadiusX="4" RadiusY="4"/>
</Grid>
</Grid>
</ControlTemplate>

Resources