Choose image Source path from resourcedictionary - wpf

I have a checkbox, that I style with custom images.
I need to choose an image at runtime, depending on theme.
I want to just change image and not all template.
This is my checkbox's template:
<Style TargetType="{x:Type CheckBox}" x:Key="FlatCheckbox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<StackPanel Orientation="Horizontal">
<Image x:Name="checkboxImage" Source="/checked.png" Width="32"/>
<ContentPresenter/>
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="checkboxImage" Property="Source" Value="/unchecked.png"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
So I want to take just the path of Source of checkboxImage from ResourceDictionary( which I can choose in runtime.)
For example if I could put a key ImagePath in ResourceDictionary and use something like this
<Image x:Name="checkboxImage" Source="{ImagePath}" Width="32"/>
How to achive this?

You may declare a BitmapImage as XAML resource and reference it as DynamicResource, e.g. like:
<Window.Resources>
<BitmapImage x:Key="TestImage" UriSource="Image1.jpg"/>
<Style x:Key="TestStyle" TargetType="ContentControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ContentControl">
<Image Source="{DynamicResource TestImage}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<ContentControl Style="{StaticResource TestStyle}"/>
</Grid>
Then you can replace the resource by some other BitmapImage like
public MainWindow()
{
InitializeComponent();
Resources["TestImage"] =
new BitmapImage(new Uri("pack://application:,,,/Image2.jpg"));
}

Related

WPF - Using Control Template just on one element

My first steps in WPF and C# and i don't get it to work... :(
I have an application with two grids inside one window. I have to change the style of the first grid, so i started reading and reached using ControlTemplate.
My Grid now looks as i wanted it. But i only want that the first grid looks this way. the second one on the same page should have another style.
Is it possible to bind the controltemplate only on one grid. Maybe by name or soemthing identifying?
My ControlTemplate Code for now i have written in window.resources looks:
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid>
<Border
Name="Border"
Margin="0,0,0,0"
Background="Black"
BorderBrush="Black"
BorderThickness="0,2,0,0"
CornerRadius="0,0,0,0" >
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center"
HorizontalAlignment="Center"
ContentSource="Header"
Margin="0,0,0,20"
RecognizesAccessKey="True"/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Border" Property="BorderBrush" Value="#FF454E54" />
<Setter TargetName="Border" Property="Background" Value="#FF0A3651" />
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="Border" Property="BorderBrush" Value="White" />
<Setter TargetName="Border" Property="Background" Value="#FF454E54" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
You could give the Style or ControlTemplate an x:Key ( <Style x:Key="key" TargetType="{x:Type TabItem}"> ) and apply it to the element you want by setting the element's Style or Template property like this:
<TabItem Style="{StaticResource key}">
A Style without an x:Key is implicit and will be applied to all elements whose type matches the specified TargetType of the Style.
Instead of writing your template under Window.Resources do it under your Grid.Resources
Try this. All this is doing is setting the style of the TabItem directly.
<TabItem> <!-- This is your TabItem control -->
<TabItem.Style>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<!-- ControlTemplate here -->
</Controltemplate
</Setter.Value>
</Setter>
</Style>
</TabItem.Style>
</TabItem>

WPF: How to use BasedOn Style

I am new to WPF and I created the following simple style example. But it doesn't work properly and button's content doesn't show although I can still click on it. Can anyone tell me why it is broken?
<Window.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border BorderBrush="Blue"
BorderThickness="5"
Background="Aqua"
Width="80"
Height="40">
<ContentPresenter></ContentPresenter>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="Grid" x:Name="GridWithMarginStyle">
<Setter Property="Margin" Value="12"></Setter>
</Style>
</Window.Resources>
<StackPanel>
<StackPanel.Resources>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
<EventSetter Event="Button.Click" Handler="ButtonHandler" />
<Setter Property="Background" Value="Red"></Setter>
<Setter Property="Foreground" Value="White"></Setter>
</Style>
</StackPanel.Resources>
<Button Name="OkBtn">OK</Button>
<Button Name="CancelBtn" Click="CancelBtn_Click">Cancel</Button>
</StackPanel>
You are using the BasedOn property in the correct way. The problem is that your ContentPresenter is not binded to the control it renders (i.e. the button).
Just try to replace your ControlTemplate XAML with this one:
<ControlTemplate TargetType="{x:Type Button}">
<Border BorderBrush="Blue"
BorderThickness="5"
Background="Aqua"
Width="80"
Height="40">
<ContentPresenter Content="{TemplateBinding Content}" />
</Border>
</ControlTemplate>
By using TemplateBinding you can bind the ContentPresenter to the Content property of your templated control.

WPF template for yes/no ToggleButton

Can anyone help me with a simple template to make a WPF ToggleButton show "yes" or "no" depending on it's toggle state?
I dont want it to look like a button though, just a piece of text that either shows as yes or no.
I'd like it as a template that I can just add as a style to all my existing Toggle controls.
I tried with Blend but I am new to templating and didn't know how to do it.
Building up on #Batuu's reply, to be just left with the content as text just update the style to
Updated
<Style x:Key="OnOffToggleStyle" TargetType="ToggleButton">
<Setter Property="Content" Value="Off"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Content" Value="On">
</Setter>
</Trigger>
</Style.Triggers>
</Style>
Addition here compared to original reply is
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
You basically set the template of the button to just be the content that it holds.
You can do this easily with a style:
<Window x:Class="WpfTest.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 x:Key="OnOffToggleStyle" TargetType="ToggleButton">
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Content">
<Setter.Value>
<TextBlock Text="Yes"/>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsChecked" Value="False">
<Setter Property="Content">
<Setter.Value>
<TextBlock Text="No"/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<ToggleButton Style="{StaticResource OnOffToggleStyle}"/>
</Grid>
</Window>

What does a Property Setter correspond to when used in conjunction with ControlTemplates?

I have the following Style (stripped for brevity) and have some questions based on it. To my understanding, if a ControlTemplate replaces the entire visual tree of a control for which the Style is based on, what effect do the property Setters have then?
In this example, don't the property Setters for FontSize, Margin, Height etc. correspond to the respective properties on the CheckBox itself? If you replace the Template property of a control, what will these Setters then correspond to if the CheckBox is no longer rendering it's default appearance?
<Style x:Key="KeyName" TargetType="CheckBox">
<Setter Property="FontSize" Value="11" />
<Setter Property="Margin" Value="0 0 1 0" />
<Setter Property="VerticalAlignment" Value="Top" />
<Setter Property="Height" Value="18" />
... common property setters etc.
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="CheckBox">
<Border>
<StackPanel>
<Ellipse Name="Ellipse" Width="7" Height="7" />
<ContentPresenter Content="{TemplateBinding Content}" />
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Trigger.Setters>
<Setter Property="Foreground" Value="WhiteSmoke" />
</Trigger.Setters>
</Trigger>
... custom triggers etc ...
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
They are a way to provide an initial default value for properties on the object that's being styled, they don't auto force anything on the Template for you. They can however be used in the control template.
Values that are set using the setters in a style can be overridden by local values in the xaml. for example.
This xaml file draws a single label that has had it's style altered to include a grid that takes on the background color, I've defaulted the color to red in the setter and it appears as red.
<Window x:Class="ContextMenu.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="Background" Value="Red"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Label}">
<Grid Background="{TemplateBinding Background}">
<TextBlock Text="{TemplateBinding Content}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Label>Test</Label>
</Window>
if I was to change the label line to blue on the instance of the label, you can see this overrides the setter.
<Window x:Class="ContextMenu.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="Background" Value="Red"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Label}">
<Grid Background="{TemplateBinding Background}">
<TextBlock Text="{TemplateBinding Content}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Label Background="Blue">Test</Label>
</Window>

Bring up ContextMenu when IsMouseOver on a Button using only XAML

I am trying to use XAML (only, no codebehind) to bring up the ContextMenu of a button.
I have this my button here
<Button x:Name="btn" Style="{StaticResource mybutton}" >
<Button.ContextMenu>
<ContextMenu>
<TextBlock Text="Information"/>
</ContextMenu>
</Button.ContextMenu>
</Button>
The Style for the button here
<Style TargetType="{x:Type Button}" x:Key="mybutton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="ContextMenu.IsOpen" Value="True"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
My google-fu is failing me for what seems like an easy solution. I really would prefer to avoid using codebehind (MouseEnter/MouseLeave events).
Thank you in advance.
Try to apply "Setter" for a ContextMenu within the ControlTemplate, by providing it's name in the "TargetName" property. For example:
<Button Width="100" Height="100" x:Name="btn">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border CornerRadius="2" BorderThickness="3" BorderBrush="DarkGray" x:Name="border">
<Border.ContextMenu>
<ContextMenu x:Name="cmenu">
<TextBlock>Information</TextBlock>
</ContextMenu>
</Border.ContextMenu>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="ContextMenu.IsOpen" Value="True" TargetName="cmenu"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
This is what you want i guess -
http://social.msdn.microsoft.com/forums/en-US/wpf/thread/adafe007-9637-4f28-8366-8f14ead2bd75
All you need to do is capture the mouse event that you want to trigger the context menu.
It is best to use a mouse_up event in codebehind. With MouseOver the user feels fooled as the context menu is gone when the mouse moves towards an entry...
In VB the code looks like:
Private Sub image_MouseUp(sender As Object, e As MouseButtonEventArgs) Handles image.MouseUp
anyControl.ContextMenu.IsOpen = Not OrteListBox.ContextMenu.IsOpen
End Sub
anyCountrol stands for the sender or any other control that holds the ContextMenu

Resources