Custom button with property as StaticResource - wpf

I am trying to achieve the following thing: use an svg image into a custom button.
In order to do this I created a Custom button:
public class MainButton : Button
{
static MainButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MainButton),
new FrameworkPropertyMetadata(typeof(MainButton)));
}
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(MainButton),
new UIPropertyMetadata(""));
public object Image
{
get { return (object)GetValue(ImageProperty); }
set { SetValue(ImageProperty, value); }
}
public static readonly DependencyProperty ImageProperty =
DependencyProperty.Register("Image", typeof(object), typeof(MainButton),
new UIPropertyMetadata(""));
}
I took a svg file, opened it in inkscape and saved it as xaml file.
I opened Themes.xaml and added the created xaml image as a ControlTemplate
<ControlTemplate x:Key="Home">
<Viewbox Stretch="Uniform">
<Canvas .....
</Canvas>
</Viewbox>
</ControlTemplate>
And the button style is:
Style TargetType="{x:Type local:MainButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MainButton}">
<Canvas x:Name="sp" Width="80"
Height="80">
<StackPanel Canvas.Top="12"
Canvas.Left="0"
Canvas.ZIndex="2"
Width="80">
<ContentControl x:Name="Img" Template="{StaticResource Home}" />
</StackPanel>
<StackPanel x:Name="spText" Canvas.Top="45"
Canvas.Left="1"
Canvas.ZIndex="1"
Width="80">
<TextBlock x:Name="Txt" Text="{Binding Path=(local:MainButton.Text),
RelativeSource ={RelativeSource FindAncestor,
AncestorType ={x:Type Button}}}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Foreground="White"
FontSize="14"/>
</StackPanel>
...
As you can see I have hardcoded the StaticResource name <ContentControl x:Name="Img" Template="{StaticResource Home}" />
I want to be able to have a binding with property Image on this Template, something like
<ContentControl x:Name="Img" Template="{StaticResource {???Binding Image???}}" />
So that I can set the Image property of the button with the name of the StaticResource I want.
For example, having beside "Home" image, another one "Back" I would have two buttons in MainWindow declared like this:
<MyControls:MainButton Text="Go Home!" Image="Home" />
<MyControls:MainButton Text="Go Back!" Image="Back" />
Any advice is kindly taken. Thank you for your time.

You can achieve the same result without creating a custom button. For example, in the following code, I created an image into the grid resource and then I use it inside the button's content.
<Grid>
<Grid.Resources>
<DrawingImage x:Key="Home">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#FFFFFFFF" Geometry="F1 M 0,20L 30,20L 30,40L 0,40 Z ">
<GeometryDrawing.Pen>
<Pen Thickness="2" LineJoin="Round" Brush="#FF000000"/>
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Grid.Resources>
<Button>
<StackPanel Orientation="Horizontal">
<Image Source="{StaticResource ResourceKey=Home}" />
<TextBlock Text="Hello!" />
</StackPanel>
</Button>
</Grid>
To add more images, you can simply add another DrawingImage into the grid resource and then use it in the same way into another button.

Related

XAML to add header to radio button

So with a lot of looking around I am hoping to make a GroupBox that acts like a Radio button. The header section would act as the bullet. I took some code from this question
Styling a GroupBox
that is how I want it to look. But I want to have it as a Radio button. So I put in this code (mind you I've only been doing WPF for a week or 2 now)
<Style TargetType="{x:Type RadioButton}" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RadioButton}">
<BulletDecorator>
<BulletDecorator.Bullet>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border x:Name="SelectedBorder"
Grid.Row="0"
Margin="4"
BorderBrush="Black"
BorderThickness="1"
Background="#25A0DA">
<Label x:Name="SelectedLabel" Foreground="Wheat">
<ContentPresenter Margin="4" />
</Label>
</Border>
<Border>
</Border>
</Grid>
</BulletDecorator.Bullet>
</BulletDecorator>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="true">
<Setter TargetName="SelectedBorder" Property="Background" Value="PaleGreen"/>
<Setter TargetName="SelectedLabel"
Property="Foreground"
Value="Black" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I have a feeling that I can add a label to the second row of my grid, but then I don't know how to access it. I have that template in a test project in the Window.Resources section (I plan on moving it to a resource dictionary in my main project)
the xaml for my window is this
<Grid>
<GroupBox Name="grpDoor" Margin ="8" Grid.Row="0" Grid.Column="0">
<GroupBox.Header>
WPF RadioButton Template
</GroupBox.Header>
<StackPanel Margin ="8">
<RadioButton FontSize="15" Content="Dhaka" Margin="4" IsChecked="False"/>
<RadioButton FontSize="15" Content="Munshiganj" Margin="4" IsChecked="True" />
<RadioButton FontSize="15" Content="Gazipur" Margin="4" IsChecked="False" />
</StackPanel>
</GroupBox>
</Grid>
I then hoping for something like this (not sure how I'd do it yet though)
<Grid>
<GroupBox Name="grpDoor" Margin ="8" Grid.Row="0" Grid.Column="0">
<GroupBox.Header>
WPF RadioButton Template
</GroupBox.Header>
<StackPanel Margin ="8">
<RadioButton FontSize="15"
Content="Dhaka"
Margin="4"
IsChecked="False">
<RadioButton.Description>
This is a description that would show under my Header
</RadioButton.Description>
</RadioButton>
<RadioButton FontSize="15"
Content="Munshiganj"
Margin="4"
IsChecked="True">
<RadioButton.Description>
This is a description that would show under my Header
</RadioButton.Description>
</RadioButton>
<RadioButton FontSize="15"
Content="Gazipur"
Margin="4"
IsChecked="False">
<RadioButton.Description>
This is a description that would show under my Header
</RadioButton.Description>
</RadioButton>
</StackPanel>
</GroupBox>
</Grid>
Based on your clarification, here is a very simple example with a RadioButton that looks like a GroupBox.
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:SimpleViewModel/>
</Window.DataContext>
<Window.Resources>
<DataTemplate DataType="{x:Type local:SimpleOption}">
<RadioButton GroupName="choice" IsChecked="{Binding Path=IsSelected, Mode=TwoWay}">
<RadioButton.Template>
<ControlTemplate TargetType="{x:Type RadioButton}">
<GroupBox x:Name="OptionBox" Header="{Binding Path=DisplayName, Mode=OneWay}">
<TextBlock Text="{Binding Path=Description, Mode=OneWay}"/>
</GroupBox>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding Path=IsSelected, Mode=OneWay}" Value="True">
<Setter TargetName="OptionBox" Property="Background" Value="Blue"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</RadioButton.Template>
</RadioButton>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding Path=Options, Mode=OneWay}"/>
</Grid>
</Window>
public class SimpleViewModel
{
public SimpleViewModel()
{
Options = new ObservableCollection<SimpleOption>();
var _with1 = Options;
_with1.Add(new SimpleOption {
DisplayName = "Dhaka",
Description = "This is a description for Dhaka."
});
_with1.Add(new SimpleOption {
DisplayName = "Munshiganj",
Description = "This is a description for Munshiganj.",
IsSelected = true
});
_with1.Add(new SimpleOption {
DisplayName = "Gazipur",
Description = "This is a description for Gazipur."
});
}
public ObservableCollection<SimpleOption> Options { get; set; }
}
public class SimpleOption : INotifyPropertyChanged
{
public string DisplayName {
get { return _displayName; }
set {
_displayName = value;
NotifyPropertyChanged("DisplayName");
}
}
private string _displayName;
public string Description {
get { return _description; }
set {
_description = value;
NotifyPropertyChanged("Description");
}
}
private string _description;
public bool IsSelected {
get { return _isSelected; }
set {
_isSelected = value;
NotifyPropertyChanged("IsSelected");
}
}
private bool _isSelected;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged;
public delegate void PropertyChangedEventHandler(object sender, PropertyChangedEventArgs e);
}
I'd do it with a custom attached property. That way, you can bind to it from a ViewModel, or apply it directly in XAML.
First, create a new class in your Style assembly:
public static class RadioButtonExtender
{
public static readonly DependencyProperty DescriptionProperty = DependencyProperty.RegisterAttached(
"Description",
typeof(string),
typeof(RadioButtonExtender),
new FrameworkPropertyMetadata(null));
[AttachedPropertyBrowsableForType(typeof(RadioButton))]
public static string GetDescription(RadioButton obj)
{
return (string)obj.GetValue(DescriptionProperty);
}
public static void SetDescription(RadioButton obj, string value)
{
obj.SetValue(DescriptionProperty, value);
}
}
And your style's Bullet would change so that the label is:
<Label x:Name="SelectedLabel"
Foreground="Wheat"
Content="{Binding (prop:RadioButtonExtender.Description), RelativeSource={RelativeSource TemplatedParent}} />
You could then use it in your final XAML:
<RadioButton FontSize="15"
Content="Dhaka"
Margin="4"
IsChecked="False">
<prop:RadioButtonExtender.Description>
This is a description that would show under my Header
</prop:RadioButtonExtender.Description>
</RadioButton>
As an added bonus, since you're creating the Style in a separate assembly, you can create a custom XAML namespace to make using your property easier.

Silverlight ZOrder doesn' work for me in Bing control

I'm trying to make my popup widget to be on top in a map but setting Canvas.ZOrder doesn't help.
Here is XAML:
<m:Map x:Name="MainMap"
Margin="0,6,3,3"
ZoomLevel="{Binding MapZoomLevel, Mode=TwoWay}"
Center="{Binding MapCenter, Mode=TwoWay}"
CopyrightVisibility="Collapsed"
CredentialsProvider="{Binding BingCredentialsProvider}"
UseInertia="True"
Mode="Road" Grid.Column="2" Grid.Row="1">
<m:MapItemsControl ItemsSource="{Binding Source={StaticResource WorkLayerData}}">
<m:MapItemsControl.ItemTemplate>
<DataTemplate>
<Canvas
m:MapLayer.Position="{Binding Location}">
<Button
Width="{Binding PushpinWidth}" Height="{Binding PushpinWidth}"
Margin="{Binding PushpinMargin}"
Style="{StaticResource LooklessButtonStyle}"
Command="{Binding DataContext.SelectedPushpinChangedCommand, ElementName=LayoutRoot}"
CommandParameter="{Binding}"
Cursor="Hand">
<Ellipse
Width="{Binding PushpinWidth}" Height="{Binding PushpinWidth}" Stroke="Black" Fill="{Binding IsGPSDataRecent, Converter={StaticResource BoolToGreenRedBrushConverter}}" StrokeThickness="1">
<ToolTipService.ToolTip>
<TextBlock Text="{Binding DeviceId}" />
</ToolTipService.ToolTip>
</Ellipse>
</Button>
<!-- Show black dot over actual GPS point -->
<Ellipse
Width="10" Height="10" Stroke="Black" Fill="Black" StrokeThickness="1"
Margin="-5,-5,0,0"
Visibility="{Binding IsSelected, Converter={StaticResource BoolToVisibilityConverter}}" />
<Border
Width="200"
BorderThickness="1" BorderBrush="DarkGray"
Visibility="{Binding IsSelected, Converter={StaticResource BoolToVisibilityConverter}}">
<Border.Effect>
<DropShadowEffect BlurRadius="5" Color="#FF000000" Opacity="0.5" ShadowDepth="2" />
</Border.Effect>
<ContentControl Template="{StaticResource TrackedAssetControlTemplate}" />
</Border>
</Canvas>
</DataTemplate>
</m:MapItemsControl.ItemTemplate>
</m:MapItemsControl>
</m:Map>
I tried to set ZIndex on a border but no luck.
Here is how it looks when IsSelected = true (see other dots with ZIndex higher on top)
In order to bring an item in a MapItemsControl to front it is necessary to set the ZIndex of the item container. You can do that in code behind by retrieving the item container from the MapItemsControl's ItemContainerGenerator.
In case you don't want that, you could apply an attached helper property to the top-level container (the Canvas) in the DataTemplate for your map items. As this Canvas is the direct child of the item container, the helper property would have to set the ZIndex of the visual parent of the Canvas. If that sounds weird, here is the code for the attached property, in a helper class called MapItem:
public class MapItem
{
public static readonly DependencyProperty ZIndexProperty =
DependencyProperty.RegisterAttached("ZIndex", typeof(int),
typeof(MapItem), new PropertyMetadata(ZIndexChanged));
public static int GetZIndex(DependencyObject obj)
{
return (int)obj.GetValue(ZIndexProperty);
}
public static void SetZIndex(DependencyObject obj, int value)
{
obj.SetValue(ZIndexProperty, value);
}
private static void ZIndexChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
// set ZIndex on parent of obj
Canvas.SetZIndex((UIElement)VisualTreeHelper.GetParent(obj), (int)e.NewValue);
}
}
In your DataTemplate you may now bind the helper property to one of your VM properties, perhaps by using an appropriate binding converter:
<DataTemplate x:Key="MapItemDataTemplate">
<!-- setting the helper property MapItem.ZIndex on Canvas
sets the Canvas.ZIndex property on the item container -->
<Canvas local:MapItem.ZIndex="{Binding ...}">
...
</Canvas>
</DataTemplate>

Windows Explorer Tooltip in WPF

its not that hard what i want, but i'm pulling my hairs for days!
i just want the same tooltip behaviour like the WIndows Explorer:
overlay a partially hidden tree/list element with the tooltip that displays the full element
i use the following datatemplate in my treeview
<HierarchicalDataTemplate DataType="{x:Type TreeVM:SurveyorTreeViewItemViewModel}" ItemsSource="{Binding Children, Converter={StaticResource surveyorSortableCollectionViewConverter}}">
<StackPanel x:Name="SurveyorStackPanel" Margin="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Orientation="Horizontal" Height="20" Width="auto">
... (Textblocks, properties, usercontrol, border,... )
<StackPanel.ToolTip>
<ToolTip Placement="RelativePoint" Padding="0" HasDropShadow="False"
DataContext="{Binding ElementName=SurveyorStackPanel}">
<Rectangle HorizontalAlignment="Left" VerticalAlignment="Center"
Width="{Binding ElementName=SurveyorStackPanel, Path=Width}"
Height="{Binding ElementName=SurveyorStackPanel, Path=Height}">
<Rectangle.Fill>
<VisualBrush AutoLayoutContent="True" AlignmentX="Left"
Visual="{Binding}" Stretch="None"/>
</Rectangle.Fill>
</Rectangle>
</ToolTip>
</StackPanel.ToolTip>
</StackPanel>
</HierarchicalDataTemplate>
As you can see, i'm trying to use Visualbrush. but this doesnt work. it only shows what you see on the screen.
I have tried with static resource and binding on a new stackpanel thats in the tooltip, but that only leaves with a blanc tooltip.
Do i something wrong? do i have to use alternatives?
i'm pretty new in WPF. i know the basics, but binding/resources is kinda new for me
EDIT
here is the static source i tried:
<ToolTip x:Key="reflectingTooltip" DataContext="{Binding Path=PlacementTarget, RelativeSource={x:Static RelativeSource.Self}}" Placement="RelativePoint" Padding="0" HasDropShadow="False">
<Rectangle Width="{Binding ActualWidth}" Height="{Binding Path=ActualHeight}" Margin="0"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Rectangle.Fill>
<VisualBrush Visual="{Binding}" Stretch="None" AlignmentX="Left" />
</Rectangle.Fill>
</Rectangle>
</ToolTip>
EDIT 2
Here are a few pics from the situation i have now:
the whole element must be shown when tooltip shows.
before tooltip: http://desmond.imageshack.us/Himg832/scaled.php?server=832&filename=beforedo.png&res=landing
when tooltip is shown: http://desmond.imageshack.us/Himg842/scaled.php?server=842&filename=afterbl.png&res=landing
tooltip has too large height and only shows what screens shows. only problem is to 'fiil in' the hidden text.
VisualBrush renders as a bitmap exactly the same thing you are providing by the 'Visual' property, and it does so without any modification to that thing: it renders them exactly as they are now.
If you want to display something else, you have to provide that something else.. Could you try with something like that: ?
<Window x:Class="UncutTooltip.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">
<StackPanel Orientation="Horizontal">
<ListBox ItemsSource="{Binding}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="Width" Value="250" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Transparent">
<TextBlock Text="{Binding TheText}"
TextTrimming="CharacterEllipsis">
</TextBlock>
<Grid.ToolTip>
<TextBlock Text="{Binding TheText}"
TextTrimming="CharacterEllipsis">
</TextBlock>
</Grid.ToolTip>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Border Background="Red" >
<TextBlock Margin="5" Foreground="WhiteSmoke" FontSize="18"
Text="The end of window:)" TextAlignment="Center">
<TextBlock.LayoutTransform>
<RotateTransform Angle="-90" />
</TextBlock.LayoutTransform>
</TextBlock>
</Border>
</StackPanel>
</Window>
---
using System.Collections.Generic;
using System.Windows;
namespace UncutTooltip
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new List<Item>
{
new Item { TheText = "its not that hard what i want, but i'm pulling my hairs for days!" },
new Item { TheText = "i just want the same tooltip behaviour like the WIndows Explorer: overlay a partially hidden tree/list element with the tooltip that displays the full element" },
new Item { TheText = "i use the following datatemplate in my treeview" },
new Item { TheText = "As you can see, i'm trying to use Visualbrush. but this doesnt work. it only shows what you see on the screen." },
new Item { TheText = "I have tried with static resource and binding on a new stackpanel thats in the tooltip, but that only leaves with a blanc tooltip." },
new Item { TheText = "Do i something wrong? do i have to use alternatives? i'm pretty new in WPF. i know the basics, but binding/resources is kinda new for me" },
};
}
}
public class Item
{
public string TheText { get; set; }
}
}
Edit:
Now, change the tooltip contents to i.e.:
<Grid.ToolTip>
<ListBox ItemsSource="{Binding TheWholeList}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<!--<Setter Property="Width" Value="250" />-->
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Transparent">
<TextBlock Text="{Binding TheText}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid.ToolTip>
and also change the data definition to:
public class Item
{
public string TheText { get; set; }
public IList<Item> TheWholeList { get; set; }
}
var tmp = new List<Item>
{
.........
};
foreach (var it in tmp)
it.TheWholeList = tmp;
this.DataContext = tmp;
Note that I've commented out the width constraint in the tooltip's listbox, it will present an untruncated list of untruncated elements..
Edit #2:
<StackPanel Orientation="Horizontal">
<ListBox x:Name="listbox" ItemsSource="{DynamicResource blah}"> // <---- HERE
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="Width" Value="250" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Transparent">
<TextBlock Text="{Binding TheText}" TextTrimming="CharacterEllipsis" />
<Grid.ToolTip>
<ToolTip DataContext="{DynamicResource blah}"> // <---- HERE
<TextBlock Text="{Binding [2].TheText}" /> // <---- just example of binding to a one specific item
<!-- <ListBox ItemsSource="{Binding}"> another eaxmple: bind to whole list.. -->
</ToolTip>
</Grid.ToolTip>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
public class Item
{
public string TheText { get; set; }
}
public MainWindow()
{
InitializeComponent();
Resources["blah"] = new List<Item> // <---- HERE
{
new Item { TheText = ........
........
In the last example, I've changed the window.DataContext binding, to a binding to a DynamicResource. In the window init, I've also changed the way the data is passed to the window. I've changed the tooltip template to include the Tooltip explicitely, and bound it to the same resource. This way, the inner tooltip's textblock is able to read the 3rd row of the datasource directly - this proves it is bound to the list, not to the Item.
However, this is crappy approach. It will work only with explicit Tooltip, only with Tooltip.DataContext=resource, and probably, it is the only working shape of such approach.. Probably it'd be possible to hack into the tooltip with attached behaviours and search it's parent window and get the bindings to work, but usually, it's not worth.. Could you try binding to the Item's properties like in the second sample?

ContentControl Template through Property

I have a Usercontrol with a ControlTemplate DependencyProperty (named MyItemTemplate).
public ControlTemplate MyContentControl
{
get { return (ControlTemplate)GetValue(MyContentControlProperty); }
set { SetValue(MyContentControlProperty, value); }
}
public static readonly DependencyProperty MyContentControlProperty =
DependencyProperty.Register("MyContentControl", typeof(ControlTemplate), typeof(MyScroll),
new PropertyMetadata(new ControlTemplate()));
In the xaml of my UserControl I want to use the "MyItemTemplate" as a template for a ContentControl like that :
<ContentControl x:Name="MyContentControl" Template="{Binding MyItemTemplate}" />
I know that the Template="{Binding MyItemTemplate}" is wrong, but I wonder how to do it...
Thanks
You can use a RelativeSource binding to reference a custom DependencyProperty on your UserControl
<ContentControl Template="{Binding
RelativeSource={RelativeSource AncestorType={x:Type local:MyUserControl}},
Path=MyItemTemplate}" />
Edit
If you're working in Silverlight 4.0 or lower, which doesn't support RelativeSource bindings, then give your UserControl tag a Name and use an ElementName binding
<UserControl x:Name="MyUserControl" ...>
<ContentControl Template="{Binding ElementName=MyUserControl, Path=MyItemTemplate}" />
</UserControl>
Have your template as a static resource (defined in your XAML somewhere).
<DataTemplate x:Key="DetailedTemplate">
<Border BorderBrush="Blue" Margin="3" Padding="3" BorderThickness="2" CornerRadius="5" Background="Beige">
<StackPanel Orientation="Horizontal">
<Image Margin="10" Width="250" Height="200" Stretch="Fill" Source="{Binding Path=ImageHref}">
<Image.BitmapEffect>
<DropShadowBitmapEffect />
</Image.BitmapEffect>
</Image>
<StackPanel Orientation="Vertical" VerticalAlignment="Center">
<TextBlock FontSize="25" Foreground="Goldenrod" Text="{Binding Path=ImageName}" />
<Label Content="{Binding Path=ImageRating,Converter={StaticResource RatingConverter}}" />
</StackPanel>
</StackPanel>
</Border>
</DataTemplate>
<DataTemplate x:Key="SimpleTemplate">
<Border BorderBrush="Blue" Margin="3" Padding="3" BorderThickness="2" CornerRadius="5" Background="Beige">
<StackPanel HorizontalAlignment="Center">
<Image Margin="10" Width="250" Height="200" Stretch="Fill" Source="{Binding Path=ImageHref}">
<Image.BitmapEffect>
<DropShadowBitmapEffect />
</Image.BitmapEffect>
</Image>
</StackPanel>
</Border>
</DataTemplate>
For example, in XAML:
<ListBox x:Name="lbResults" Grid.Row="1" Grid.Column="0" Height="240"
HorizontalContentAlignment="Stretch" ItemsSource="{StaticResource FavoriteImages}"
ItemTemplate="{StaticResource SimpleTemplate}" />
Then in the code behind something like:
//pull the detailed template from resources, identified by the DetailedTemplate key
DataTemplate detail = this.FindResource("DetailedTemplate") as DataTemplate;
lbResults.ItemTemplate = detail;
and
//pull the summary template from resources, identified by the SimpleTemplate key
DataTemplate summary = this.FindResource("SimpleTemplate") as DataTemplate;
lbResults.ItemTemplate = summary;
Although the best answer is Rachel's, here are some alternatives.
If this logic is not critical, you'd better put the template into resources and get it using StaticResource:
<UserControl>
<UserControl.Resources>
<ControlTemplate x:Key="template">
...
</ControlTemplate>
</UserControl.Resources>
<ContentControl Template="{StaticResource template}"/>
</UserControl>
If you still need to set it from the UserControl's property, you may either define a change callback.
XAML:
<UserControl>
<ContentControl x:Name="contentControl"/>
</UserControl>
Code-behind:
public ControlTemplate MyContentControl
{
get { return (ControlTemplate)GetValue(MyContentControlProperty); }
set { SetValue(MyContentControlProperty, value); }
}
public static readonly DependencyProperty MyContentControlProperty =
DependencyProperty.Register("MyContentControl", typeof(ControlTemplate), typeof(MyScroll), new PropertyMetadata(null, OnMyContentControlChanged));
static void OnMyContentControlChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var userControl = (MyScroll)sender;
userControl.contentControl.Template = e.NewValue as ControlTemplate;
}
And the last option is using a Custom Control.
Code:
public class MyScroll : SomeParentControl
{
public MyScroll()
{
this.DefaultStyleKey = typeof(MyScroll);
}
public ControlTemplate MyContentControl
{
get { return (ControlTemplate)GetValue(MyContentControlProperty); }
set { SetValue(MyContentControlProperty, value); }
}
public static readonly DependencyProperty MyContentControlProperty =
DependencyProperty.Register("MyContentControl", typeof(ControlTemplate), typeof(MyScroll), new PropertyMetadata(null));
}
The template:
<!-- This is a template for what have been your UserControl -->
<ControlTemplate TargetType="{x:Type someNameSpaceAlias:MyScroll}">
<!-- And this is the 'MyContentControl' -->
<ContentControl Template="{TemplateBinding MyContentControl}"/>
</ControlTemplate>

How to get image source in Control Template

I've added one button like this:
<Button x:Name="myButton"
Style="{StaticResource myButtonStyle}"
Height="36"
VerticalAlignment="Top"
Click="myButton_Click">
<Grid>
<Image Height="*" Width="31" Source="{Binding Path=Image}" />
<TextBlock Text="{Binding Path=DisplayName}" HorizontalAlignment="Center" />
</Grid>
</Button>
Where 'Image' is the source of the required image in string. And the style is as follows:
<Style x:Key="myButtonStyle"
TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="myButtonRootBorder">
<StackPanel Orientation="Horizontal">
<Image Source="{??}" Width="{??}" Height="{??}" />
<!--ContentPresenter-->
<ContentPresenter Grid.Column="1"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding Content}" />
</StackPanel
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Now how to set the height, width and source of the image in the control template.
Please help. Thanks in advance.
For width and height you can use {TemplateBinding Width} and {TemplateBinding Height}. If you want the image to be customizable you should inherit the button class and add an imagesource property:
public class ImageButton : Button {
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(ImageButton), new UIPropertyMetadata(null));
}
Then you can use {TemplateBinding ImageSource} for the image.

Resources