WPF how to preserve themes in a custom control - wpf

How can i Create a reusable custom button with image and Text like this :
and when I use it in a window it must respect the theme used in that window.
Currently i use the following code :
<Button x:Name="cmdSave" Margin="5" Width="100" VerticalAlignment="Center" ToolTip="Save...">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="70"/>
</Grid.ColumnDefinitions>
<Image Source="..\images\icons\yellowfloppy.ico" Grid.Column="0" VerticalAlignment="Center" Margin="2"/>
<TextBlock Text="Enregistrer" Grid.Column="1" Foreground="{StaticResource GreenForeGroundTitle}" VerticalAlignment="Center" Margin="2"/>
</Grid>
</Button>
The problem is i have to repeat the previous code each time i want to display a custom button.
So I tried the following custom control :
public class MyButton : Button
{
public ImageSource ImageButton
{
get { return (ImageSource)GetValue(ImageButtonProperty); }
set { SetValue(ImageButtonProperty, value); }
}
// Using a DependencyProperty as the backing store for ImageButton. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ImageButtonProperty =
DependencyProperty.Register("ImageButton", typeof(ImageSource), typeof(MyButton), new UIPropertyMetadata(null));
public string TextButton
{
get { return (string)GetValue(TextButtonProperty); }
set { SetValue(TextButtonProperty, value); }
}
// Using a DependencyProperty as the backing store for TextButton. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TextButtonProperty =
DependencyProperty.Register("TextButton", typeof(string), typeof(MyButton), new PropertyMetadata(""));
static MyButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyButton), new FrameworkPropertyMetadata(typeof(MyButton)));
}
}
and in Generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyButton">
<Style TargetType="{x:Type local:MyButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyButton}">
<Border CornerRadius="3" BorderBrush="AliceBlue" BorderThickness="2">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Image Source="{Binding ImageButton, RelativeSource={RelativeSource TemplatedParent}}" Grid.Column="0" VerticalAlignment="Center"/>
<TextBlock Text="{TemplateBinding TextButton}" Grid.Column="1" VerticalAlignment="Center"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and here the test window :
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="WpfmyButtonTest.MainWindow"
xmlns:b="clr-namespace:MyButton;assembly=MyButton"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ShinyBlue.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<b:MyButton ImageButton="commercial.png" TextButton="Test" Margin="274,225,40,40"/>
</Grid>
But this code doesn't display what i expect, so All behavior of a normal button disappeared and the ShinyBlue theme is not applied.
Thank you in advance.

I finally found an answer to this question. See link here.
In fact, it is sufficient to incorporate a button inside a usercontrol like this :
XAML
<UserControl x:Class="Knom.WPF.ImageButton.ImageButton2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="UC">
<Grid>
<Button>
<StackPanel Orientation="Horizontal" Margin="10">
<Image Source="{Binding ElementName=UC, Path=Image}"
Width="{Binding ElementName=UC, Path=ImageWidth}"
Height="{Binding ElementName=UC, Path=ImageHeight}"/>
<TextBlock Text="{Binding ElementName=UC, Path=Text}"
Margin="10,0,0,0"/>
</StackPanel>
</Button>
</Grid>
Code Behind
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Knom.WPF.ImageButton
{
/// <summary>
/// Interaction logic for ImageButton2.xaml
/// </summary>
public partial class ImageButton2 : UserControl
{
public ImageButton2()
{
InitializeComponent();
}
public ImageSource Image
{
get { return (ImageSource)GetValue(ImageProperty); }
set { SetValue(ImageProperty, value); }
}
// Using a DependencyProperty as the backing store for Image. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ImageProperty =
DependencyProperty.Register("Image", typeof(ImageSource), typeof(ImageButton2), new UIPropertyMetadata(null));
public double ImageWidth
{
get { return (double)GetValue(ImageWidthProperty); }
set { SetValue(ImageWidthProperty, value); }
}
// Using a DependencyProperty as the backing store for ImageWidth. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ImageWidthProperty =
DependencyProperty.Register("ImageWidth", typeof(double), typeof(ImageButton2), new UIPropertyMetadata(16d));
public double ImageHeight
{
get { return (double)GetValue(ImageHeightProperty); }
set { SetValue(ImageHeightProperty, value); }
}
// Using a DependencyProperty as the backing store for ImageHeight. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ImageHeightProperty =
DependencyProperty.Register("ImageHeight", typeof(double), typeof(ImageButton2), new UIPropertyMetadata(16d));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
// Using a DependencyProperty as the backing store for Text. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(ImageButton2), new UIPropertyMetadata(""));
//This is for MVVM Command
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
// Using a DependencyProperty as the backing store for Text. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand), typeof(ImageButton2), new PropertyMetadata(null));
}
}
i can use it like this :
<Window x:Class="Knom.WPF.ImageButton.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300"
xmlns:my="clr-namespace:Knom.WPF.ImageButton">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="BureauBlue.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<my:ImageButton2 Image="calendar.png" Text="UserControl" HorizontalAlignment="Left" VerticalAlignment="Top"
ImageWidth="16" ImageHeight="16" Margin="10" Command="{Binding MyCommand}"/>
</Window>
And it is reusable and works fine
Thank you

Related

Why the Dependency Property can no be use in usercontrol?

I want to make a brand new button, so that I create a usercontrol inherits Button to do this.
Here is the XAML:
<Button x:Class="Uploader.PropertyButtonControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Uploader"
mc:Ignorable="d"
xmlns:s="clr-namespace:Svg2Xaml;assembly=Svg2Xaml"
d:DesignHeight="450" d:DesignWidth="800">
<Button.Template>
<ControlTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="0.6*"></RowDefinition>
<RowDefinition Height="0.4*"></RowDefinition>
</Grid.RowDefinitions>
<Border Width="{Binding Path=ActualHeight,RelativeSource={RelativeSource Self}}" Grid.RowSpan="2" Background="{Binding IconBackground,Mode=TwoWay}">
<s:SvgShape Source="{Binding IconSource,Mode=TwoWay}"></s:SvgShape>
</Border>
<TextBlock Grid.Column="1" Text="{Binding ButtonTitle,Mode=TwoWay}"></TextBlock>
<TextBlock Grid.Column="1" Grid.Row="1" Foreground="#575757" Text="{Binding ButtonContent,Mode=TwoWay}"></TextBlock>
<Border Grid.ColumnSpan="2" Grid.RowSpan="2" BorderBrush="#cecece" BorderThickness="1" Visibility="Collapsed" Name="Bo"></Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="Bo" Property="Visibility" Value="Visible"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
And here is code-behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Uploader
{
/// <summary>
/// Interaction logic for PropertyButtonControl.xaml
/// </summary>
public partial class PropertyButtonControl : Button
{
public PropertyButtonControl()
{
InitializeComponent();
}
public SolidColorBrush IconBackground
{
get { return (SolidColorBrush)GetValue(IconBackgroundProperty); }
set { SetValue(IconBackgroundProperty, value); }
}
// Using a DependencyProperty as the backing store for IconBackground. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IconBackgroundProperty =
DependencyProperty.Register("IconBackground", typeof(SolidColorBrush), typeof(PropertyButtonControl),null);
public ImageSource IconSource
{
get { return (ImageSource)GetValue(IconSourceProperty); }
set { SetValue(IconSourceProperty, value); }
}
// Using a DependencyProperty as the backing store for IconSource. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IconSourceProperty =
DependencyProperty.Register("IconSource", typeof(ImageSource), typeof(PropertyButtonControl), null);
public string ButtonTitle
{
get { return (string)GetValue(ButtonTitleProperty); }
set { SetValue(ButtonTitleProperty, value); }
}
// Using a DependencyProperty as the backing store for ButtonTitle. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ButtonTitleProperty =
DependencyProperty.Register("ButtonTitle", typeof(string), typeof(PropertyButtonControl), null);
public string ButtonContent
{
get { return (string)GetValue(ButtonContentProperty); }
set { SetValue(ButtonContentProperty, value); }
}
// Using a DependencyProperty as the backing store for ButtonContent. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ButtonContentProperty =
DependencyProperty.Register("ButtonContent", typeof(string), typeof(PropertyButtonControl), null);
}
}
If I used it in a page as:
<local:PropertyButtonControl IconBackground="Red" ButtonTitle="123" ButtonContent="456"></local:PropertyButtonControl>
After the program ran ,the content do not showed and color not changed? It seems is the binding problem.
But what's wrong with this? Thank you.
Your problem is the UserControl which is not adapted for a brand new button.
You should rather use a CustomControl. Read this if you want more details.
Here is how we do:
Create a new class inheriting from the Button control in a separate file PropertyButtonControl.cs:
public class PropertyButtonControl : Button
{
//No need for Constructor and InitializeComponent
public SolidColorBrush IconBackground[...]
public static readonly DependencyProperty IconBackgroundProperty =
DependencyProperty.Register("IconBackground", typeof(SolidColorBrush), typeof(PropertyButtonControl), null);
public ImageSource IconSource[...]
public static readonly DependencyProperty IconSourceProperty =
DependencyProperty.Register("IconSource", typeof(ImageSource), typeof(PropertyButtonControl), null);
public string ButtonTitle[...]
public static readonly DependencyProperty ButtonTitleProperty =
DependencyProperty.Register("ButtonTitle", typeof(string), typeof(PropertyButtonControl), null);
public string ButtonContent[...]
public static readonly DependencyProperty ButtonContentProperty =
DependencyProperty.Register("ButtonContent", typeof(string), typeof(PropertyButtonControl), null);
}
Create a ResourceDictionary containing the XAML template in a separate file PropertyButtonControl.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:view="clr-namespace:StackTest.View">
<Style TargetType="{x:Type view:PropertyButtonControl }">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="0.6*"/>
<RowDefinition Height="0.4*"/>
</Grid.RowDefinitions>
<Border Width="{Binding Path=ActualHeight, RelativeSource={RelativeSource Self}}"
Grid.RowSpan="2"
Background="{Binding Path=IconBackground, RelativeSource={RelativeSource AncestorType=view:PropertyButtonControl}}">
</Border>
<TextBlock Grid.Column="1"
Text="{Binding Path=ButtonTitle, RelativeSource={RelativeSource AncestorType=view:PropertyButtonControl}}"/>
<TextBlock Grid.Column="1"
Grid.Row="1"
Foreground="#575757"
Text="{Binding Path=ButtonContent, RelativeSource={RelativeSource AncestorType=view:PropertyButtonControl}}"/>
<Border Grid.ColumnSpan="2"
Grid.RowSpan="2"
BorderBrush="#cecece"
BorderThickness="1"
Visibility="Collapsed"
Name="Bo"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="Bo" Property="Visibility" Value="Visible"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Add this resource dictionary to your App.XAML resources:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="View/PropertyButtonControl.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Enjoy your control:
<local:PropertyButtonControl IconBackground="Blue" ButtonTitle="123" ButtonContent="456"/>
I put all my files in a folder named View. You must adapt it to your own structure.

Combobox usercontrol SelectedItem data binding in WPF container

This is my combobox usercontrol:
<UserControl x:Class="Hexa.Screens.UsrColorPicker"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Hexa.Screens"
xmlns:sys="clr-namespace:System;assembly=mscorlib" Height="40" Width="200" Name="uccolorpicker"
mc:Ignorable="d">
<UserControl.Resources>
<ResourceDictionary>
<ObjectDataProvider MethodName="GetType" ObjectType="{x:Type sys:Type}" x:Key="colorsTypeOdp">
<ObjectDataProvider.MethodParameters>
<sys:String>System.Windows.Media.Colors, PresentationCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35</sys:String>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<ObjectDataProvider ObjectInstance="{StaticResource colorsTypeOdp}" MethodName="GetProperties" x:Key="colorPropertiesOdp"/>
</ResourceDictionary>
</UserControl.Resources>
<ComboBox Name="superCombo" ItemsSource="{Binding Source={StaticResource colorPropertiesOdp}}" SelectedValuePath="Name" SelectedValue="{Binding ElementName=uccolorpicker, Path=SelectedColor}" Text="{Binding ElementName=uccolorpicker,Path=Text}" SelectedItem="{Binding ElementName=uccolorpicker, Path=SelectedItem}" SelectedIndex="{Binding ElementName=uccolorpicker, Path=SelectedIndex}" SelectionChanged="superCombo_SelectionChanged" HorizontalContentAlignment="Stretch" >
<ComboBox.ItemTemplate>
<DataTemplate >
<WrapPanel Orientation="Horizontal" Background="Transparent">
<TextBlock Width="20" Height="20" Margin="5" Background="{Binding Name}" />
<TextBlock Text="{Binding Name}" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="14" FontStyle="Italic" FontWeight="Bold" FontFamily="Palatino Linotype" />
</WrapPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
This is the code behind:
namespace Hexa.Screens
{
/// <summary>
/// Interaction logic for UsrColorPicker.xaml
/// </summary>
public partial class UsrColorPicker : UserControl
{
public UsrColorPicker()
{
InitializeComponent();
}
public Brush SelectedColor
{
get { return (Brush)GetValue(SelectedColorProperty); }
set { SetValue(SelectedColorProperty, value); }
}
public int SelectedIndex
{
get { return (int)GetValue(SelectedIndexProperty); }
set { SetValue(SelectedIndexProperty, value); }
}
public int SelectedItem
{
get { return (int)GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
public string Text
{
get { return (string)GetValue(SelectedTextProperty); }
set { SetValue(SelectedTextProperty, value); }
}
// Using a DependencyProperty as the backing store for SelectedColor. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectedColorProperty =
DependencyProperty.Register("SelectedColor", typeof(Brush), typeof(UsrColorPicker), new UIPropertyMetadata(null));
public static readonly DependencyProperty SelectedIndexProperty =
DependencyProperty.Register("SelectedIndex", typeof(int), typeof(UsrColorPicker), new UIPropertyMetadata(null));
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register("SelectedItem", typeof(int), typeof(UsrColorPicker), new UIPropertyMetadata(null));
public static readonly DependencyProperty SelectedTextProperty =
DependencyProperty.Register("Text", typeof(int), typeof(UsrColorPicker), new UIPropertyMetadata(null));
public static readonly RoutedEvent SettingConfirmedEvent =
EventManager.RegisterRoutedEvent("SettingConfirmedEvent", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(UsrColorPicker));
public event RoutedEventHandler SettingConfirmed
{
add { AddHandler(SettingConfirmedEvent, value); }
remove { RemoveHandler(SettingConfirmedEvent, value); }
}
private void superCombo_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
RaiseEvent(new RoutedEventArgs(UsrColorPicker.SettingConfirmedEvent));
}
}
}
I am trying to set its SelectedItem,SelectedIndex thru xaml binding in my container's XAML as under:-
<local:UsrColorPicker x:Name="cmbItem_Group_back_color" HorizontalAlignment="Center" Width="205" Height="22" SettingConfirmed="cmbItem_Group_back_color_SettingConfirmed" SelectedColor ="{Binding Path=CurrentRec.Primary_Tone,Mode=TwoWay}" Canvas.Left="97" Canvas.Top="92" />
The code behind is as under:-
form_load()
{
this.DataContext = DataContract_ButtonSettings;
}
But the selecteditem's text is not showing on the combobox as it should.
I found the solution.It was a careless mistake..
The bug was nowhere in the code posted..Actually,i was using the usercontrol's selectedcolor property to bind to the backcolor property of an element in the view.The viewmodel updates the SelectedColor property of the ColorCombobox.The viewmodel was being updated from many places in the code behind.And at one place,the viewmodel's SelectedColor property was being set with the HEX equivalent of the known Color of System.Windows.Media.Colors Known color,and when the view model wud try to bind to that Hex element to the ComboBox,it did not find any matching entry for that Hex value in the dropdown list,and hence,though the background color of the control was being effected,the combobox text showed blank.:-).
anyways,solved it...thanks for your time Ed. :-).

Custom Control in WPF

I have just started to learn WPF.
I have a button with image. like Image+Text
<Button Height="67" Name="Button1" Width="228" HorizontalContentAlignment="Left">
<StackPanel Orientation="Horizontal" >
<Image Source="Images/add.png" Stretch="Uniform"></Image>
<TextBlock Text=" Create Company" VerticalAlignment="Center" FontSize="20"></TextBlock>
</StackPanel>
</Button>
Now I want to add many more buttons in the above format.
So I have to write the same code again and again.
So I decided to have a customButton to do my job easily.
I tried to create the custom control.
I added a property named Image there.
Now how should I give value to that property?
Am I going on the wrong way?
Here you have tutorial how to create a custom control.
[1.] Add new item "Custom Control (WPF)" with name "ButtonImg".
After this step, VS create for you two files: "ButtonImg.cs" and "/Themes/Generic.xaml".
[2.] Add few dependency properties to "ButtonImg.cs" file:
I created properties to: image source, text, image width and height.
public class ButtonImg : Control
{
static ButtonImg()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ButtonImg), new FrameworkPropertyMetadata(typeof(ButtonImg)));
}
public ImageSource ImageSource
{
get { return (ImageSource)GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}
public static readonly DependencyProperty ImageSourceProperty =
DependencyProperty.Register("ImageSource", typeof(ImageSource), typeof(ButtonImg), new PropertyMetadata(null));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(ButtonImg), new PropertyMetadata(string.Empty));
public double ImageWidth
{
get { return (double)GetValue(ImageWidthProperty); }
set { SetValue(ImageWidthProperty, value); }
}
public static readonly DependencyProperty ImageWidthProperty =
DependencyProperty.Register("ImageWidth", typeof(double), typeof(ButtonImg), new PropertyMetadata((double)30));
public double ImageHeight
{
get { return (double)GetValue(ImageHeightProperty); }
set { SetValue(ImageHeightProperty, value); }
}
public static readonly DependencyProperty ImageHeightProperty =
DependencyProperty.Register("ImageHeight", typeof(double), typeof(ButtonImg), new PropertyMetadata((double)30));
}
[3.] In this step you must create Template for your new custom control. So you must edit following file "/Themes/Generic.xaml":
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfButtonImg">
<Style TargetType="{x:Type local:ButtonImg}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ButtonImg}">
<Button>
<Button.Content>
<StackPanel Orientation="Horizontal">
<Image Source="{TemplateBinding ImageSource}"
Height="{TemplateBinding ImageHeight}" Width="{TemplateBinding ImageWidth}"
Stretch="Uniform" />
<TextBlock Text="{TemplateBinding Text}" Margin="10,0,0,0" VerticalAlignment="Center" FontSize="20" />
</StackPanel>
</Button.Content>
</Button>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
[4.] Example of using this new custom control is following:
First you must add appropriate namespace:
xmlns:MyNamespace="clr-namespace:WpfButtonImg"
Now you can use it like this:
<MyNamespace:ButtonImg ImageSource="/Images/plug.png" Text="Click me!" />

UserControl Property Binding not Working

Given the following code why would "My Stupid Text" never be bound to the UserControls text box?
MainPage.xaml
<Grid x:Name="LayoutRoot">
<Local:Stupid StupidText="My Stupid Text" />
</Grid>
Stupid.xaml
<UserControl x:Class="SilverlightApplication5.Stupid"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid x:Name="LayoutRoot" Background="White">
<TextBlock Text="{Binding StupidText}" />
</Grid>
</UserControl>
Stupid.xaml.cs
public partial class Stupid : UserControl
{
public string StupidText
{
get { return (string)GetValue(StupidTextProperty); }
set { SetValue(StupidTextProperty, value); }
}
// Using a DependencyProperty as the backing store for StupidText. This enables animation, styling, binding, etc...
public static readonly DependencyProperty StupidTextProperty =
DependencyProperty.Register("StupidText", typeof(string), typeof(Stupid), new PropertyMetadata(string.Empty));
public Stupid()
{
InitializeComponent();
}
}
Do the following in the constructor of your user control (after InitializeComponent) and your textblock should be aware of its datacontext:
this.DataContext = this;
Give your Stupid control a name:-
<Local:Stupid x:Name="MyStupid" StupidText="My Stupid Text" />
Then you can use element binding like this:-
<TextBlock Text="{Binding StupidText, ElementName=MyStupid}" />

WPF - Best Practice for the run-of-the-mill [Label:Input] Control

I am wondering, which is the best and quickest way to get the well known Label Input [or output, doesn't matter] combination in WPF. Its a simple Task, just think of a quick output of the "object" ME:
Name - Christian
Age - 28
Mood - Good
I know, I can use a Grid with TextBlocks. But to be honest, the "short" XAML for this is nearly half a page long (RowDefinitions, ColDefs, Grid.Col on each Label)
The alternative way, using three StackPanels (horizontal) with one vertical seems also a little bit stupid. In this case, I have to give each Label a fixed width, to get the indent correct. And it just does not "feel" right.
So, given the Situation above, you got a custom object with 3-6 Properties you just want to dump as readonly to your GUI, how would you do it (in WPF, Silverlight too, if you are really in the mood :).
I can, of course, write a usercontrol for this. But why reinvent the wheel, if it might be already there...
And finally, to illustrate even further, the example I just created in real life and was the reason for this post:
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Log Count" Width="100"/>
<TextBlock Text="{Binding LastLogRun.LogMessageCount}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Start Time" Width="100"/>
<TextBlock Text="{Binding LastLogRun.StartTime}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="End Time" Width="100"/>
<TextBlock Text="{Binding LastLogRun.EndTime}"/>
</StackPanel>
</StackPanel>
You could use shared size groups to get the auto-sizing Grid behavior of two nicely-lined-up columns, while still being able to pull out the complexity into a UserControl.
Here's an example of using a LabeledEdit control that would do what you're looking for. The complexity has all been factored away into the UserControl, and all you need to do is remember to set Grid.IsSharedSizeScope on the StackPanel:
<Window x:Class="WpfApplication5.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication5"
Name="Self" Title="Window1" Height="300" Width="300">
<StackPanel Grid.IsSharedSizeScope="True">
<local:LabeledEdit Label="Name"/>
<local:LabeledEdit Label="Age" Text="28"/>
<!-- and with databinding... -->
<local:LabeledEdit Label="Width"
Text="{Binding Width, ElementName=Self}"/>
<local:LabeledEdit Label="Height"
Text="{Binding Height, ElementName=Self}"/>
</StackPanel>
</Window>
And here's the source code for the UserControl. LabeledEdit.xaml:
<UserControl x:Class="WpfApplication5.LabeledEdit"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="Self">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="LabeledEdit_Labels"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="{Binding Label, ElementName=Self}"/>
<TextBox Grid.Column="1" Text="{Binding Text, ElementName=Self}"/>
</Grid>
</UserControl>
LabeledEdit.xaml.cs:
using System.Windows;
namespace WpfApplication5
{
public partial class LabeledEdit
{
public static readonly DependencyProperty LabelProperty =
DependencyProperty.Register("Label", typeof(object), typeof(LabeledEdit));
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(LabeledEdit),
new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public LabeledEdit()
{
InitializeComponent();
}
public object Label
{
get { return GetValue(LabelProperty); }
set { SetValue(LabelProperty, value); }
}
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
}
}
If you're using 3.5sp1 you can use StringFormat in the binding. Something like this should work...
<TextBlock Text="{Binding LastLogRun.LogMessageCount, StringFormat={}Log Count - {0}}" />
Perhaps you should rethink your UI. Why would you want Label - Textbox on the same line? That's a horrendous waste of space.
Why not Label over texbox? Then you've got a simple UI and simple XAML:
<StackPanel Orientation="Vertical">
<TextBlock>Name</TextBlock>
<TextBox />
<TextBlock>Age</TextBlock>
<TextBox />
<TextBlock>Mood</TextBlock>
<TextBox />
</StackPanel>
Add some styling for your TextBlocks and you've got a nice, clean UI, with very little repetition.
The silverlight toolkit has a DataForm control that works pretty cool!
I know this is 13! years later, but, if anyone else is curious, you can use BulletDecorator (docs). There's special handling for vertical alignment based on the first line of text, if the content is text-based content.
OP's example, written with BulletDecorators:
<StackPanel>
<BulletDecorator>
<BulletDecorator.Bullet>
<TextBlock Text="Log Count" Width="100"/>
</BulletDecorator.Bullet>
<TextBlock Text="{Binding LastLogRun.LogMessageCount}"/>
</BulletDecorator>
<BulletDecorator>
<BulletDecorator.Bullet>
<TextBlock Text="Start Time" Width="100"/>
</BulletDecorator.Bullet>
<TextBlock Text="{Binding LastLogRun.StartTime}"/>
</BulletDecorator>
<BulletDecorator>
<BulletDecorator.Bullet>
<TextBlock Text="End Time" Width="100"/>
</BulletDecorator.Bullet>
<TextBlock Text="{Binding LastLogRun.EndTime}"/>
</BulletDecorator>
</StackPanel>
If you're not a fan of that 👆 syntax (it's a bit verbose for my sake), you can use some attached properties to clean it up some.
BulletHelper.cs
public static class BulletHelper
{
#region Bullet
public static readonly DependencyProperty BulletProperty = DependencyProperty.RegisterAttached(
"Bullet",
typeof(object),
typeof(BulletHelper),
new FrameworkPropertyMetadata(OnBulletChanged)
);
public static object? GetBullet(DependencyObject target)
=> target.GetValue(BulletHelper.BulletProperty);
public static void SetBullet(DependencyObject target, object? value)
=> target.SetValue(BulletHelper.BulletProperty, value);
private static void OnBulletChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is BulletDecorator bulletDecorator)
{
bulletDecorator.Bullet = CreateUiElement(e.NewValue);
}
}
#endregion Bullet
#region Child
public static readonly DependencyProperty ChildProperty = DependencyProperty.RegisterAttached(
"Child",
typeof(object),
typeof(BulletHelper),
new FrameworkPropertyMetadata(OnChildChanged)
);
public static object? GetChild(DependencyObject target)
=> target.GetValue(BulletHelper.ChildProperty);
public static void SetChild(DependencyObject target, object? value)
=> target.SetValue(BulletHelper.ChildProperty, value);
private static void OnChildChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is BulletDecorator bulletDecorator)
{
bulletDecorator.Child = CreateUiElement(e.NewValue);
}
}
#endregion Child
[return: NotNullIfNotNull("value")]
private static UIElement? CreateUiElement(this object? value)
{
return value switch
{
null => null,
// Uncomment if using MaterialDesignThemes (https://github.com/MaterialDesignInXAML/MaterialDesignInXamlToolkit)
// PackIconKind kind => new PackIcon { Kind = kind },
// Uncomment if using FontAwesome6.Svg (https://github.com/MartinTopfstedt/FontAwesome6)
// EFontAwesomeIcon.None => null,
// EFontAwesomeIcon fontAwesomeIcon => new SvgAwesome { Icon = fontAwesomeIcon },
UIElement uiElement => uiElement,
_ => new ContentPresenter { Content = value },
};
}
}
The above attached properties reduces it to:
<StackPanel>
<BulletDecorator BulletHelper.Bullet="Log Count"
BulletHelper.Child="{Binding LastLogRun.LogMessageCount}" />
<BulletDecorator BulletHelper.Bullet="Start Time"
BulletHelper.Child="{Binding LastLogRun.StartTime}" />
<BulletDecorator BulletHelper.Bullet="End Time"
BulletHelper.Child="{Binding LastLogRun.EndTime}" />
</StackPanel>
Unfortunately, you lose out on the Width property that was previously set on the TextBlock.
I wish BulletDecorator was easier to work with. Unfortunately, its two main properties, Child and Bullet, are not dependency properties, and they both must be UIElement.
However. You can make a Control (not a UserControl!) that makes BulletDecorator easier to use.
BulletControl.cs
public class BulletControl : ContentControl
{
static BulletControl()
{
FocusableProperty.OverrideMetadata(typeof(Control), new FrameworkPropertyMetadata(false));
DefaultStyleKeyProperty.OverrideMetadata(typeof(BulletControl), new FrameworkPropertyMetadata(typeof(BulletControl)));
}
#region Bullet
public static readonly DependencyProperty BulletProperty = DependencyProperty.Register(
nameof(Bullet),
typeof(object),
typeof(BulletControl),
new FrameworkPropertyMetadata(BulletChangedCallback)
);
public object? Bullet
{
get => this.GetValue(BulletProperty);
set => this.SetValue(BulletProperty, value);
}
private static void BulletChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is Bullet ctrl)
{
ctrl.SetValue(HasBulletPropertyKey, e.NewValue is not null);
}
}
#endregion Bullet
#region BulletStringFormat
public static readonly DependencyProperty BulletStringFormatProperty = DependencyProperty.Register(
nameof(BulletStringFormat),
typeof(string),
typeof(BulletControl),
new FrameworkPropertyMetadata()
);
public string? BulletStringFormat
{
get => (string?)this.GetValue(BulletStringFormatProperty);
set => this.SetValue(BulletStringFormatProperty, value);
}
#endregion BulletStringFormat
#region BulletTemplate
public static readonly DependencyProperty BulletTemplateProperty = DependencyProperty.Register(
nameof(BulletTemplate),
typeof(DataTemplate),
typeof(BulletControl),
new FrameworkPropertyMetadata()
);
public DataTemplate? BulletTemplate
{
get => (DataTemplate?)this.GetValue(BulletTemplateProperty);
set => this.SetValue(BulletTemplateProperty, value);
}
#endregion BulletTemplate
#region BulletTemplateSelector
public static readonly DependencyProperty BulletTemplateSelectorProperty = DependencyProperty.Register(
nameof(BulletTemplateSelector),
typeof(DataTemplateSelector),
typeof(BulletControl),
new FrameworkPropertyMetadata()
);
public DataTemplateSelector? BulletTemplateSelector
{
get => (DataTemplateSelector?)this.GetValue(BulletTemplateSelectorProperty);
set => this.SetValue(BulletTemplateSelectorProperty, value);
}
#endregion BulletTemplateSelector
#region HasBullet
private static readonly DependencyPropertyKey HasBulletPropertyKey = DependencyProperty.RegisterReadOnly(
name: nameof(HasBullet),
propertyType: typeof(bool),
ownerType: typeof(BulletControl),
typeMetadata: new FrameworkPropertyMetadata()
);
public bool HasBullet
{
get => (bool)this.GetValue(HasBulletPropertyKey.DependencyProperty);
private set => this.SetValue(HasBulletPropertyKey, value);
}
#endregion HasBullet
}
Generic.xaml
<Style TargetType="{x:Type controls:BulletControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:BulletControl}">
<BulletDecorator>
<BulletDecorator.Bullet>
<ContentPresenter ContentSource="Bullet" />
</BulletDecorator.Bullet>
<ContentPresenter ContentSource="Content" />
</BulletDecorator>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
With 👆, the OP's sample code becomes:
<StackPanel>
<StackPanel.Resources>
<DataTemplate x:Key="BulletTemplate">
<TextBlock Text="{Binding}"
Width="100" />
</DataTemplate>
</StackPanel.Resources>
<BulletControl Bullet="Log Count"
Content="{Binding LastLogRun.LogMessageCount}"
BulletTemplate="{StaticResource BulletTemplate}"
/>
<BulletControl Bullet="Start Time"
Content="{Binding LastLogRun.StartTime}"
BulletTemplate="{StaticResource BulletTemplate}"
/>
<BulletControl Bullet="End Time"
Content="{Binding LastLogRun.EndTime}"
BulletTemplate="{StaticResource BulletTemplate}"
/>
</StackPanel>

Resources