Passing in an Image.Source to a XAML style - wpf

I am trying to implement a button style that has both an image and a caption that are specified when the style is used.
Here is my style:
<Style x:Key="ButtonStyle" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid Margin="0,0,0,0" Background="#FF9E9FA3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Source="{TemplateBinding Tag}" Grid.Column="0"/>
<Label Content="{TemplateBinding Content}" Grid.Column="1" FontSize="15" VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And I am using it like this:
<Button Tag="edit.png" Content="Edit" Style="{StaticResource ButtonStyle}" Grid.Column="2" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Button Tag="save.png" Content="Save" Style="{StaticResource ButtonStyle}" Grid.Column="3" HorizontalAlignment="Center" VerticalAlignment="Center"/>
Content works as expected and two buttons show up with the correct words in the label, but the images do not show up. If I use the ControlTemplate directly in (no style) and replace {TemplateBinding} with the image file name, the images do appear.
Obviously I am using the Tag wrong or the Image wrong.
Help?

It works if you replace
<Image Source="{TemplateBinding Tag}" />
by
<Image Source="{Binding Tag, RelativeSource={RelativeSource TemplatedParent}}"/>
because then the necessary type conversion from string to ImageSource is performed automatically.

Related

How can I embed a visual brush at the end of a multi-line TextBlock?

I have a LinkButton template for external hyperlinks. Such a LinkButton should have a little icon at the end (a VisualBrush, not an image file) of its text to inform the user that clicking this link will open a browser window and open some external website, like in this example:
(source: this wikipedia article's reference section)
My current naive implementation of the style with the template:
<Style x:Key="LinkButtonExternalBaseStyle" TargetType="Button">
<ControlTemplate TargetType="Button">
<Grid Name="grdLinkButtonExternal" Background="{TemplateBinding Background}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="3"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0"
Text="{TemplateBinding Content}"
TextWrapping="Wrap"
/>
<Rectangle
Grid.Column="2"
Name="imgExternalHyperlinkIcon"
Style="{StaticResource RectangleExternalHyperlinkIconStyle}"
/>
</Grid>
</ControlTemplate>
</Style>
(RectangleExternalHyperlinkIconStyle is a rectangle style having a VisualBrush as its Fill, representing the icon in question) along with:
<Button
Command="{Binding OpenExternalHyperlinkCommand}"
CommandParameter="{Binding Gizmo.Hyperlink}"
Style="{StaticResource LinkButtonExternal1Style}"
Content="{Binding Gizmo.Hyperlink.AbsoluteUri}"
HorizontalAlignment="Left"
/>
results in:
where the image is always next to the (possibly multi-line) hyperlink and not inlined. For shorter links it looks fine:
So how can I achieve a multi-line hyperlink with an inline icon like in the example above?
After having checked multiple stackoverflow posts and other google results, I stumbled upon <InLineUIContainer> and was able to solve this issue by "splitting" the TextBlock into multiple parts, binding the Content to a Run element and then actually inlining the icon:
<Style x:Key="LinkButtonExternalBaseStyle" TargetType="Button">
<ControlTemplate TargetType="Button">
<TextBlock TextWrapping="Wrap">
<Run Text="{TemplateBinding Content}"/>
<InlineUIContainer>
<Rectangle
Name="imgExternalHyperlinkIcon"
Style="{StaticResource RectangleExternalHyperlinkIconStyle}"
Margin="3,0,0,0"
/>
</InlineUIContainer>
</TextBlock>
</ControlTemplate>
</Style>
resulting in:

The width of StackPanel inside the button is very narrow.(WPF)

I want to make a button with an image similar to the following:
For which I have written the following in xaml:
<Button
Style="{DynamicResource BotonRedondo2}"
Width ="AUTO"
Cursor="Hand"
Background="#3b5998"
Grid.Column="0">
<StackPanel Width ="Auto" Orientation="Horizontal" >
<Image HorizontalAlignment="Left" Source="/Test;component/Imagenes/face.png"/>
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="LOGIN WITH XXXXX" />
<StackPanel>
</Button>
But the result is as follows:
The width of the StackPanel does not match the width of the button.
Any suggestions on how to solve the problem?
Comments or questions are welcome
UPDATE
Maybe I should indicate that the button is in a column of a grid as follows:
<Grid
Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button
Style="{DynamicResource BotonRedondo2}"
Width ="AUTO"
Cursor="Hand"
Background="#3b5998"
Grid.Column="0">
<StackPanel Width ="Auto" Orientation="Horizontal" >
<Image HorizontalAlignment="Left" Source="/Test;component/Imagenes/face.png"/>
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="LOGIN WITH XXXXX" />
</StackPanel>
</Button>
<Button
Style="{DynamicResource BotonRedondo2}"
Grid.Column="1"
Width ="Auto"
Background="Orange"
Content="LOGIN2"
Margin="3"/>
</Grid>
If I remove "Auto", the button looks like this:
UPDATE
The style that applies to the button is as follows:
<Style TargetType="Button" x:Key="BotonRedondo2">
<Setter Property="Background" Value="Blue"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="Height" Value="35"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border BorderThickness="0"
Background="{TemplateBinding Background}"
CornerRadius="4">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
StackPanel in Horizontal orientation stacks items next to each other. It basically doesn't care about the HorizontalAlignment of items inside it.
You can use a Grid instead:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" HorizontalAlignment="Left" Source="/Test;component/Imagenes/face.png"/>
<TextBlock Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" Text="LOGIN WITH XXXXX" />
</Grid>
The above will give you the desired look.
You need to remove Width="Auto" from the Button and to set Button's property HorizontalContentAlignment="Stretch".
Also, consider using DockPanel or Grid instead of StackPanel:
<Button HorizontalContentAlignment="Stretch">
<DockPanel>
<Image Source="/Test;component/Imagenes/face.png"/>
<TextBlock
VerticalAlignment="Center"
HorizontalAlignment="Center"
Text="LOGIN WITH XXXXX" />
</DockPanel>
</Button>
In the style of the button it should be written like this:
CornerRadius="4">
<ContentPresenter HorizontalAlignment="Stretch" VerticalAlignment="Center"/>

Custom style tooltip and binding to button WPF with code xaml?

I would like to create a style for the tooltip and use it always binding to each button.
I can only do so from XAML?
I have done this for now:
<Window.Resources>
<Style x:Key="{x:Type ToolTip}" TargetType="{x:Type ToolTip}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToolTip}">
<Grid Width="200" Height="Auto" MinHeight="80">
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="1" FontWeight="Bold"
HorizontalAlignment="Left" VerticalAlignment="Center"
Text="{Binding Path=TitleTT}"/>
<Image Grid.Column="0" Grid.RowSpan="2" Source="{Binding Path=ImageTT}"
HorizontalAlignment="Stretch" VerticalAlignment="Center"/>
<TextBlock Grid.Row="1" Grid.Column="1" TextWrapping="Wrap" Margin="2"
HorizontalAlignment="Left" VerticalAlignment="Center"
Text="{Binding Path=DescriptionTT}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
Now, I want to build a button in another user control.
What should I do?
What's wrong? why can not join anything?
Thanks for the help.
Code Button:
<Button Name="button_conf" Content="{DynamicResource Button_Confirm}" Margin="18,10"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Click="button_conf_Click">
<Button.ToolTip>
<ToolTip Style=ToolTip TitleTT="{DynamicResource ToolTip_title__confirm_defsce}"
ImageTT="/Images/xsd.jpg" DescriptionTT="The button confirm....:"/>
</Button.ToolTip>
</Button>
I can see at least the following two typos:
1.
<Style x:Key="{x:Type ToolTip}" TargetType="{x:Type ToolTip}"
should be
<Style x:Key="ToolTipStyle" TargetType="{x:Type ToolTip}"
Of course, you can use another name than ToolTipStyle.
2.
<ToolTip Style=ToolTip
should be
<ToolTip Style="{StaticResource ToolTipStyle}"
Apart from that, the properties TitleTT, ImageTT and DescriptionTT do not exist on the ToolTip control.

16x16 pixel images in RibbonApplicationMenuItem gets stretched

I am having a ribbon application menu that looks like this:
<ribbon:RibbonWindow>
<DockPanel>
<ribbon:Ribbon DockPanel.Dock="Top">
<ribbon:Ribbon.ApplicationMenu>
<ribbon:RibbonApplicationMenu>
<ribbon:RibbonApplicationMenuItem Header="Users"
ImageSource="Users16x16.png"
Command="{Binding FooBinding}"/>
</ribbon:RibbonApplicationMenu>
</ribbon:Ribbon.ApplicationMenu>
</ribbon:Ribbon>
</DockPanel>
</ribbon:RibbonWindow>
The resulting image looks like this, stretched.
So how do I have a ribbon application menu item with a height that adapts to the image size instead of stretching?
Change the control template like:
<ribbon:RibbonApplicationMenuItem Command="{Binding FooBinding}">
<ribbon:RibbonApplicationMenuItem.Template>
<ControlTemplate>
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="Users16x16.png"/>
<TextBlock Grid.Column="1">Users</TextBlock>
</Grid>
</ControlTemplate>
</ribbon:RibbonApplicationMenuItem.Template>
</ribbon:RibbonApplicationMenuItem>
If you want to use control template as Style in your dictionary:
<Style x:Key="16x16ImageStyle" TargetType="{x:Type ribbon:RibbonApplicationMenuItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ribbon:RibbonApplicationMenuItem}">
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Image VerticalAlignment="Center" HorizontalAlignment="Center" Stretch="None"
Grid.Column="0" Source="{TemplateBinding ImageSource}"/>
<TextBlock Grid.Column="1" Text="{TemplateBinding Header}"></TextBlock>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and recall it in your ribbon:
<ribbon:RibbonApplicationMenuItem Header="Users"
ImageSource="Users16x16.png"
Command="{Binding FooBinding}"
Style="{StaticResource 16x16ImageStyle}"/>

Template Binding in Control template

I have the following control template.
I wish to set the source property for the image control in the control
template using Template Binding.
But since this is a control template for button control and the button control doesn't
have source property, i can't use TemplateBinding in this case.
<ControlTemplate x:Key="BtnTemplate" TargetType="Button">
<Border CornerRadius="5" Margin="15" Cursor="Hand">
<StackPanel>
<Image Name="Img" Style="{StaticResource ImageStyle}" Source="temp.jpg" Height="100" Width="100" Margin="5"></Image>
<Label Content="{TemplateBinding Content}" Background="Transparent" Margin="2"></Label>
</StackPanel>
</Border>
</ControlTemplate>
Since i have to set different images for different instances of button, i can't hardcode the path as well.
Please let me know how to tackle this situation.
I'd suggest using dynamic resources, e.g. define the template as follows:
<ControlTemplate x:Key="buttonTemplate" TargetType="Button">
<Border CornerRadius="5" Margin="15" Cursor="Hand">
<StackPanel Orientation="Horizontal" Background="Yellow">
<Image Source="{DynamicResource ResourceKey=Img}" Height="100" Width="100" Margin="5"></Image>
<Label Content="{TemplateBinding Content}" Background="Transparent" Margin="2"></Label>
</StackPanel>
</Border>
</ControlTemplate>
And use it like this:
<Button Content="Button" Template="{StaticResource ResourceKey=buttonTemplate}">
<Button.Resources>
<ImageSource x:Key="Img">SomeUri.png/</ImageSource>
</Button.Resources>
</Button>
TemplateBinding is a lightweight "binding", it doesn't support some features of traditional Binding, such as automatically type conversion using the known type converters associated with the target property (such as converting the string URI into a BitmapSource instance).
The following code can work properly:
<Window x:Class="GridScroll.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window2">
<Window.Resources>
<Style TargetType="{x:Type Button}" x:Key="ButtonStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border CornerRadius="5" Margin="15" Cursor="Hand" Background="Red">
<StackPanel Orientation="Horizontal" Background="White">
<Image Name="Img" Source="{Binding Path=Tag, RelativeSource={RelativeSource TemplatedParent}}" Margin="5"></Image>
<Label Content="{TemplateBinding Content}" Margin="2"></Label>
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<StackPanel Orientation="Horizontal">
<Button Style="{StaticResource ButtonStyle}" Tag="a.jpeg" Content="a"/>
<Button Style="{StaticResource ButtonStyle}" Tag="b.png" Content="b"/>
</StackPanel>
You haven't really said how you expect consumers of your button to set the source. You could use the Button.Tag property, for example, and then bind to that in your template. Or you could define your own control:
public class ImageButton : Button
{
// add Source dependency property a la Image
}
And then the template:
<ControlTemplate TargetType="ImageButton">
<Border CornerRadius="5" Margin="15" Cursor="Hand">
<StackPanel>
<Image Name="Img" Style="{StaticResource ImageStyle}" Source="{TempateBinding Source}" Height="100" Width="100" Margin="5"></Image>
<Label Content="{TemplateBinding Content}" Background="Transparent" Margin="2"></Label>
</StackPanel>
</Border>
</ControlTemplate>
I'm not sure that I understood your problem very well but why don't you use ContentPresenter? It allows to move the code for your Image at the higher level.
<ControlTemplate x:Key="BtnTemplate" TargetType="Button">
...
<ContentPresenter/>
</ControlTemplate>
...
<Button Template="{StaticResource BtnTemplate}">
<Image .../>
</Button>

Resources