Configurable HeaderTemplate - wpf

I have about 12 Expanders that need to have a custom HeaderTemplate. The HeaderTemplate has a textblock to display the header text, along with a button. The button has a custom control template so that I can display the button as a Path within a VisualBrush.
<Expander VerticalAlignment="Top"
Header="Fields"
IsExpanded="True">
<Expander.HeaderTemplate>
<DataTemplate>
<DockPanel LastChildFill="False">
<TextBlock VerticalAlignment="Center"
DockPanel.Dock="Left"
FontSize="14"
Foreground="{DynamicResource IdealForegroundColorBrush}"
Text="{Binding}"/>
<Button DockPanel.Dock="Right" Margin="0 0 10 0"
Command="{Binding RelativeSource={RelativeSource AncestorType=Expander}, Path=DataContext.AddFieldCommand}">
<Button.Template>
<ControlTemplate TargetType="Button">
<Grid>
<Rectangle Fill="Transparent" />
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Content="{TemplateBinding Content}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
</Grid>
</ControlTemplate>
</Button.Template>
<Rectangle Width="20" Height="20" Fill="{DynamicResource EditIconBrush}" />
</Button>
</DockPanel>
</DataTemplate>
</Expander.HeaderTemplate>
I would like to re-use this template across all 12 Expanders, but need to specify what icon and Command the button within the template will use. How would I go about doing that? Can this be achieved by breaking the data template up in to a series of templates? What I want to do is move the template into a static resource
<Expander VerticalAlignment="Top"
Header="Fields"
IsExpanded="True"
HeaderTemplate="{StaticResource IconHeader}">
I'm just not sure how to provide the template what Command and Icon resource it should use. Would a Dependency property make sense here?
Thanks.

I believe that there are at least to ways to achieve this. First is to inherit from Expander, extend it with your Dependency properties and bind to them inside DataTemplate.
Another way to do this is to create some attached properties, and bind to them inside DataTemplate as below:
public class TemplateConfig
{
public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached("Command", typeof (ICommand), typeof (TemplateConfig), new PropertyMetadata(default(ICommand)));
public static void SetCommand(DependencyObject element, ICommand value)
{
element.SetValue(CommandProperty, value);
}
public static ICommand GetCommand(DependencyObject element)
{
return (ICommand) element.GetValue(CommandProperty);
}
public static readonly DependencyProperty IconProperty = DependencyProperty.RegisterAttached("Icon", typeof (VisualBrush), typeof (TemplateConfig), new PropertyMetadata(default(VisualBrush)));
public static void SetIcon(DependencyObject element, VisualBrush value)
{
element.SetValue(IconProperty, value);
}
public static VisualBrush GetIcon(DependencyObject element)
{
return (VisualBrush) element.GetValue(IconProperty);
}
}
Header template:
<DataTemplate x:Key="ExpanderHeaderTemplate">
<DockPanel LastChildFill="False">
<TextBlock VerticalAlignment="Center"
DockPanel.Dock="Left"
FontSize="14"
Text="{Binding}" />
<Button Margin="0 0 10 0"
Command="{Binding Path=(test:TemplateConfig.Command),
RelativeSource={RelativeSource AncestorType=Expander}}"
DockPanel.Dock="Right">
<Button.Template>
<ControlTemplate TargetType="Button">
<Grid>
<Rectangle Fill="Transparent" />
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding Content}" />
</Grid>
</ControlTemplate>
</Button.Template>
<Rectangle Width="20"
Height="20"
Fill="{Binding Path=(test:TemplateConfig.Icon),
RelativeSource={RelativeSource AncestorType=Expander}}" />
</Button>
</DockPanel>
</DataTemplate>
and finally expander:
<Expander VerticalAlignment="Top"
Header="Fields"
HeaderTemplate="{StaticResource ExpanderHeaderTemplate}"
IsExpanded="True"
test:TemplateConfig.Command="{Binding SomeCommand}"
test:TemplateConfig.Icon="{StaticResource VisualBrush}" />
Hope this helps.

Related

WPF Cannot Capture Mouse events in Nested CustomControls

I have the following Control template in my app.xaml: (it is in a template setter in a style, so there is no key)
<ControlTemplate TargetType="{x:Type vm:State}">
<Grid Width="100" Height="60" >
<Border Margin="4"
IsHitTestVisible="{Binding ElementName=Radio_Edit02, Path=IsChecked}"
Padding="4"
BorderBrush="White"
BorderThickness="2"
CornerRadius="5">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="{Binding ItemName, RelativeSource={RelativeSource TemplatedParent}}"/>
</Border>
<Grid Margin="3" Background="Transparent">
<local:Anchor Width="6" Height="6" HorizontalAlignment="Center" VerticalAlignment="Top" Background="Red" Visibility="{Binding IsChecked, ElementName=Radio_Edit03, Converter={StaticResource bool2vizibility}}"/>
<local:Anchor Width="6" Height="6" HorizontalAlignment="Right" VerticalAlignment="Center" Background="Red" Visibility="{Binding IsChecked, ElementName=Radio_Edit03, Converter={StaticResource bool2vizibility}}"/>
<local:Anchor Width="6" Height="6" HorizontalAlignment="Left" VerticalAlignment="Center" Background="Red" Visibility="{Binding IsChecked, ElementName=Radio_Edit03, Converter={StaticResource bool2vizibility}}"/>
<local:Anchor Width="6" Height="6" HorizontalAlignment="Center" VerticalAlignment="Bottom" Background="Red" Visibility="{Binding IsChecked, ElementName=Radio_Edit03, Converter={StaticResource bool2vizibility}}"/>
</Grid>
</Grid>
</ControlTemplate>
State derives from thumb
public class State : Thumb, INotifyPropertyChanged
Anchor derives from UserControl
public class Anchor : UserControl, INotifyPropertyChanged
I want to capture a click of the State at one point in the app and of the Anchor in another point of the app.
I want to control when by the two radiobuttons, Radio_Edit02 & Radio_Edit03
when Radio_Edit02 is selected I want to capture the click on the border
when Radio_Edit03 is selected I want to capture the click on the anchor.
I keep getting the click event on the State and cannot get a click on the Anchor.
I tried adding IsHitTestVisible="{Binding ElementName=Radio_Edit03, Path=IsChecked}" to the anchor
but no luck
I tried the following assignement of event handlers in Anchor but neither worked
public Anchor() //constructor
{
//this//this.MouseLeftButtonUp += onAnchorSelect;
//this.PreviewMouseLeftButtonUp += onAnchorSelect;
}
signature for onAnchorSelect is
public void onAnchorSelect(object sender, MouseButtonEventArgs e)
How can I capture a click on the Anchor so that e.Source is the anchor element
thanks

WPF Dependency Property Bound To User Control

I have a button subclass called MenuButton.
public class MenuButton : Button
{
public string Caption
{
get { return (string)GetValue(CaptionProperty); }
set { SetValue(CaptionProperty, value); }
}
public static readonly DependencyProperty CaptionProperty =
DependencyProperty.Register("Caption", typeof(string), typeof(MenuButton), new UIPropertyMetadata(null));
public UserControl Icon
{
get { return (UserControl)GetValue(IconProperty); }
set { SetValue(IconProperty, value); }
}
public static readonly DependencyProperty IconProperty =
DependencyProperty.Register("Icon", typeof(UserControl), typeof(MenuButton),
new PropertyMetadata(null));
}
In the style I want to show an icon that was created using paths from SVG files. I have created a User Control containing the XAML for the icon:
<UserControl x:Class="WpfApplication1.Views.ScopeIcon"
.
.
.
>
<Viewbox Height="55"
Width="55">
<Grid>
<Path Fill="LightBlue" Data="M98.219,48.111C97..."/>
<Path Fill="LightBlue" Data="M98.219,46.948C97...."/>
</Grid>
</Viewbox>
</UserControl>
And here's the style:
<Style TargetType="Button"
x:Key="TestButtonStyle">
<Setter Property="Height" Value="140"/>
<Setter Property="Width" Value="195"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:MenuButton}">
<Border x:Name="TheBorder">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50*"/>
<RowDefinition Height="50*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ContentPresenter Grid.Row="0"/> <===== THE USER CONTROL WILL GO HERE
<TextBlock Grid.Row="1"
Grid.Column="0"
Text="{TemplateBinding Caption}"
HorizontalAlignment="Center"
VerticalAlignment="Top"
Margin="5"
Foreground="White"
FontSize="14"
TextAlignment="Center"
TextWrapping="WrapWithOverflow"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I will set it here:
<ListBox Grid.Row="0"
ItemsSource="{Binding MainTools}" >
<ListBox.ItemTemplate>
<DataTemplate>
<controls:MenuButton Caption="{Binding Caption}"
Margin="2"
Width="100"
Style="{StaticResource TestButtonStyle}"
VerticalAlignment="Top"
Command="{Binding Path=ButtonClick}"
CommandParameter="{x:Static enums:Tabs.Oscilloscope}"
Icon=""/> <============= HOW DO I PUT THE SCOPEICON USER CONTROL HERE?
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I'm essentially trying to nest user controls, but I want to tell the button, in XAML, what UserControl to use for its icon.
Thank you
First, you need to update your style to set Content property on your ContentPresenter the same way you did for Text property on TextBlock.
<ContentPresenter Grid.Row="0" Content="{TemplateBinding Icon}"/>
And, then to set Icon on the MenuButton in your DataTemplate update your MenuButton element in DataTemplate to something like:
<controls:MenuButton Caption="Caption"
Margin="2"
Width="100"
Style="{StaticResource TestButtonStyle}"
VerticalAlignment="Top"
Command="{Binding Path=ButtonClick}"
CommandParameter="{x:Static enums:Tabs.Oscilloscope}">
<controls:MenuButton.Icon>
<views:ScopeIcon />
</controls:MenuButton.Icon>
</controls:MenuButton>

Add second image to content of button

This is the peace of code for the button:
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="border" CornerRadius="8">
<Border.Background>
SlateBlue
</Border.Background>
<StackPanel Orientation="Horizontal" Margin="3">
<Image Source="{Binding Path=Tag, RelativeSource={RelativeSource TemplatedParent}}" Height="30" Width="30" Stretch="Fill" Margin="0 0 0 0" />
<TextBlock Text="{Binding Path=Content,RelativeSource={RelativeSource TemplatedParent}}" FontSize="12" VerticalAlignment="Center" Padding="10,0,10,0" />
</StackPanel>
</Border>
</ControlTemplate>
</Button.Template>
As you can see, my button has an image and a text. However, I want to add another image after the text. How do I do this? I already use the Tag property once.
Here's how I use the button:
<myProject:ButtonWithTwoImages x:Name="btnHome" Tag="/Resources/Home.png" Content="Home" Command="{Binding Path=NavigateToCategoriesCommand}" Margin="20 0 0 0"/>
How do I add another image to the button?
I created a new user control called ButtonWithTwoImages, here is the code.
Xaml
<UserControl x:Class="WpfUserControl.ButtonWithTwoImages"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Border x:Name="border" CornerRadius="8">
<Border.Background>
SlateBlue
</Border.Background>
<StackPanel Orientation="Horizontal" Margin="3">
<Image Source="{Binding Path=Image1, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" Height="30" Width="30" Stretch="Fill" Margin="0 0 0 0" />
<TextBlock Text="{Binding Path=Content,RelativeSource={RelativeSource TemplatedParent}}" FontSize="12" VerticalAlignment="Center" Padding="10,0,10,0" />
<Image Source="{Binding Path=Image2, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" Height="30" Width="30" Stretch="Fill" Margin="0 0 0 0" />
</StackPanel>
</Border>
</UserControl>
Xaml.cs
public partial class ButtonWithTwoImages : UserControl
{
public ButtonWithTwoImages()
{
InitializeComponent();
}
public ImageSource Image1
{
get { return (ImageSource)GetValue(Image1Property); }
set { SetValue(Image1Property, value); }
}
public static readonly DependencyProperty Image1Property = DependencyProperty.Register("Image1", typeof(ImageSource), typeof(ButtonWithTwoImages), new PropertyMetadata(null));
public ImageSource Image2
{
get { return (ImageSource)GetValue(Image2Property); }
set { SetValue(Image2Property, value); }
}
public static readonly DependencyProperty Image2Property = DependencyProperty.Register("Image2", typeof(ImageSource), typeof(ButtonWithTwoImages), new PropertyMetadata(null));
}
How To Use It
<Window x:Class="WpfUserControl.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mylib="clr-namespace:WpfUserControl">
<Grid>
<mylib:ButtonWithTwoImages Image1="Images\A.jpg" Image2="Images\B.jpg"></mylib:ButtonWithTwoImages>
</Grid>
</Window>

How do I create a button that has a uniform width and height?

I would like to create a button whose size is that of it's largest edge.
The reason for this is I want the button to be a small information circle embedded inside a DataGrid column.
Currently I have:
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="i" Padding="2" Margin="0"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Width="{Binding Source=Self, Path=Height}">
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Ellipse Fill="#FFF4F4F5" Stroke="#FF6695EB"/>
<ContentPresenter Margin="0"
RecognizesAccessKey="False"
SnapsToDevicePixels="True"
VerticalAlignment="Center"
HorizontalAlignment="Center"
/>
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Create a class derived from Button and override the Measuring, e.g.
public class SquareButton : Button
{
protected override Size MeasureOverride(Size constraint)
{
Size size = base.MeasureOverride(constraint);
size.Height = size.Width = Math.Max(size.Height, size.Width);
return size;
}
}
You can then use it something like this:
<local:SquareButton
Content="i" Padding="2" Margin="0"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<local:SquareButton.Template>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Ellipse Fill="#FFF4F4F5" Stroke="#FF6695EB"/>
<ContentPresenter Margin="0"
RecognizesAccessKey="False"
SnapsToDevicePixels="True"
VerticalAlignment="Center"
HorizontalAlignment="Center"
/>
</Grid>
</ControlTemplate>
</local:SquareButton.Template>
</local:SquareButton>
Make a custom control - SquareButton
public class SquareButton : Button
{
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
if (e.Property == HeightProperty || e.Property == WidthProperty)
this.Height = this.Width = Math.Max(this.Width, this.Height);
base.OnPropertyChanged(e);
}
}

Creating an image+text button with a control template?

I am tired of creating the same image+text button over and over again, and I would like to move the markup to a control template. Here is my problem: I need to provide template bindings to add the image and text to the templated button, and the Button control doesn't seem to have properties that I can bind to.
My template looks like this so far (with '???' for the unknown template bindings):
<ControlTemplate x:Key="ImageButtonTemplate" TargetType="{x:Type Button}">
<StackPanel Height="Auto" Orientation="Horizontal">
<Image Source="{TemplateBinding ???}" Width="24" Height="24" Stretch="Fill"/>
<TextBlock Text="{TemplateBinding ???}" HorizontalAlignment="Left" Foreground="{DynamicResource TaskButtonTextBrush}" FontWeight="Bold" Margin="5,0,0,0" VerticalAlignment="Center" FontSize="12" />
</StackPanel>
</ControlTemplate>
Is it possible to create this image+text button using a control template, or do I have to go to a user control to do it? If it can be done with a control template, how do I set up the template bindings?
Define a CustomControl like this
in .cs
public class MyButton : Button
{
static MyButton()
{
//set DefaultStyleKeyProperty
}
public ImageSource ImageSource
{
get { return (ImageSource)GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}
// Using a DependencyProperty as the backing store for ImageSource. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ImageSourceProperty =
DependencyProperty.Register("ImageSource", typeof(ImageSource), typeof(MyButton), new UIPropertyMetadata(null));
}
in generic.xaml of themes folder
<Style TargetType="{x:Type MyButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type MyButton}">
<StackPanel Height="Auto" Orientation="Horizontal">
<Image Source="{TemplateBinding ImageSource}" Width="24" Height="24" Stretch="Fill"/>
<TextBlock Text="{TemplateBinding Content}" HorizontalAlignment="Left" Foreground="{DynamicResource TaskButtonTextBrush}" FontWeight="Bold" Margin="5,0,0,0" VerticalAlignment="Center" FontSize="12" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Resources