Why the Dependency Property can no be use in usercontrol? - wpf

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.

Related

WPF TemplateBinding to an ObservableCollection

I'm creating a content control that contains another usercontrol. We'll call them InnerControl and OuterControl. The InnerControl has a dependency property of type ObservableCollection called "Items." I'm trying to bind that to an identical Dependency Property in the OuterControl. Here is a stub of the InnerControl code:
public class InnerControl : UserControl {
public InnerControl() {
InnerItems = new ObservableCollection<string>();
}
public ObservableCollection<string> InnerItems
{
get { return (ObservableCollection<string>)GetValue(InnerItemsProperty); }
set { SetValue(InnerItemsProperty, value); }
}
public static DependencyProperty InnerItemsProperty =
DependencyProperty.Register("InnerItems",
typeof(ObservableCollection<string>),
typeof(InnerControl),
new PropertyMetadata());
}
The outer control contains an identical Items property:
public class OuterControl : ContentControl {
public OuterControl() {
OuterItems = new ObservableCollection<string>();
}
static OuterControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(OuterControl),
new FrameworkPropertyMetadata(typeof(OuterControl)));
}
public ObservableCollection<string> OuterItems
{
get { return (ObservableCollection<string>)GetValue(OuterItemsProperty); }
set { SetValue(OuterItemsProperty, value); }
}
public static DependencyProperty OuterItemsProperty =
DependencyProperty.Register("OuterItems",
typeof(ObservableCollection<string>),
typeof(OuterControl),
new PropertyMetadata());
}
Then I'm defining the OuterControl's appearance in the generic.xaml file:
<Style TargetType="{x:Type userControls:OuterControl}">
<Setter Property="Padding" Value="10" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type userControls:OuterControl}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<local:InnerControl Grid.Row="0" Grid.Column="0"
InnerItems="{TemplateBinding OuterItems}"/>
<ContentPresenter Grid.Row="1" Grid.Column="0"
Content="{TemplateBinding Content}"
Margin="{TemplateBinding Padding}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The really important part of the above code I want to call your attention to is:
<local:InnerControl Grid.Row="0" Grid.Column="0"
InnerItems="{TemplateBinding OuterItems}"/>
What I expect to happen is that when items are added to the OuterItems collection of the OuterControl, those same items will be added to the InnerControl.InnerItems collection. However, that doesn't happen, and I can't figure out why.
I've also tried a relative binding so that I could experiment with using TwoWay mode and so on. Something like this:
InnerItems="{Binding OuterItems, Mode=TwoWay,
RelativeSource={RelativeSource TemplatedParent}}"
But so far that hasn't worked either.
UPDATE
Everything that I thought solved this problem so far has only exposed new problems, so I've removed my previous updates. What I'm stuck with at this point is:
If I initialize InnerItems in the constructor, then the TemplateBinding doesn't seem to work (the items never get updated)
If I don't initialize InnerItems at all, the TemplateBinding works. However, if InnerControl is just used by itself in the Designer, it breaks, because InnerItems is null when the designer tries to add items to it.
When you have a collection type dependency property, you must not use an instance of the collection class as default value of the property. Doing so will make all instances of the control that owns the property use the same collection instance.
So your property metadata
new PropertyMetadata(new ObservableCollection<string>())
should be replaced by
new PropertyMetadata(null)
or you do not specify any metadata at all
public static DependencyProperty InnerItemsProperty =
DependencyProperty.Register(
"InnerItems", typeof(ObservableCollection<string>), typeof(InnerControl));
Now you would somehow have to initialize the property value. As usual, you'll do it in the control's constructor, like
public InnerControl()
{
InnerItems = new ObservableCollection<string>();
}
When you now bind the property of the control like
<local:InnerControl InnerItems="{Binding ...}" />
the value set in the constructor is replaced by the value produced by the Binding.
However, this does not happen when you create the Binding in a Style Setter, because values from Style Setters have lower precedence than so-called local values (see Dependency Property Value Precedence).
A workaround is to set the default value by the DependencyObject.SetCurrentValue() method, which does not set a local value:
public InnerControl()
{
SetCurrentValue(InnerItemsProperty, new ObservableCollection<string>());
}
I find it quite likely that #Clemens comment has the right answer. Anyhow, I tested your solution using the code below and it worked fine for me.
Check how you are binding and adding items. You did not post that code in your question.
OuterControl
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
namespace TemplateBindingTest.Controls
{
public class OuterControl : UserControl
{
static OuterControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(OuterControl), new FrameworkPropertyMetadata(typeof(OuterControl)));
}
public ObservableCollection<string> OuterItems
{
get { return (ObservableCollection<string>)GetValue(OuterItemsProperty); }
set { SetValue(OuterItemsProperty, value); }
}
public static DependencyProperty OuterItemsProperty =
DependencyProperty.Register("OuterItems",
typeof(ObservableCollection<string>),
typeof(OuterControl),
new PropertyMetadata(new ObservableCollection<string>()));
}
}
InnerControl
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
namespace TemplateBindingTest.Controls
{
public class InnerControl : UserControl
{
static InnerControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(InnerControl), new FrameworkPropertyMetadata(typeof(InnerControl)));
}
public ObservableCollection<string> InnerItems
{
get { return (ObservableCollection<string>)GetValue(InnerItemsProperty); }
set { SetValue(InnerItemsProperty, value); }
}
public static DependencyProperty InnerItemsProperty =
DependencyProperty.Register("InnerItems",
typeof(ObservableCollection<string>),
typeof(InnerControl),
new PropertyMetadata(new ObservableCollection<string>()));
}
}
Generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:TemplateBindingTest.Controls">
<Style TargetType="{x:Type controls:OuterControl}">
<Setter Property="Padding" Value="10" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:OuterControl}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ContentPresenter Grid.Row="0" Grid.Column="0"
Content="{TemplateBinding Content}"
Margin="{TemplateBinding Padding}"/>
<ItemsControl Grid.Row="1" ItemsSource="{TemplateBinding OuterItems}" />
<Border Grid.Row="2" BorderThickness="1" BorderBrush="Red">
<controls:InnerControl Grid.Column="0"
InnerItems="{TemplateBinding OuterItems}">Inner Control</controls:InnerControl>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type controls:InnerControl}">
<Setter Property="Padding" Value="10" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:InnerControl}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ContentPresenter Grid.Row="0" Grid.Column="0"
Content="{TemplateBinding Content}"
Margin="{TemplateBinding Padding}"/>
<ItemsControl Grid.Row="1" ItemsSource="{TemplateBinding InnerItems}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
MainWindow.xaml
<Window x:Class="TemplateBindingTest.MainWindow"
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:controls="clr-namespace:TemplateBindingTest.Controls"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<controls:OuterControl OuterItems="{Binding OuterItems}">Outer Control</controls:OuterControl>
<Button Grid.Row="1" Content="Add" Click="Button_Click" HorizontalAlignment="Left" />
</Grid>
</Window>
MainWindow.cs
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows;
namespace TemplateBindingTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private ObservableCollection<string> _OuterItems;
public MainWindow()
{
InitializeComponent();
DataContext = this;
_OuterItems = new ObservableCollection<string>(new List<string>()
{
"Test 1",
"Test 2",
"Test 3",
});
}
public ObservableCollection<string> OuterItems
{
get
{
return _OuterItems;
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
_OuterItems.Add(System.IO.Path.GetRandomFileName());
}
}
}

UserControl multiple datatemplate + templateselector

I need to show data inside a usercontrol in different ways depending on a flag.
To achieve this i tried the following, but using this control in the main view shows nothing.
<UserControl DataContext="**self**">
<UserControl.Resources>
<DataTemplate x:Key="mouseInputTemplate">
<TextBlock HorizontalAlignment="Center"><Run Text="{Binding Text}" /></TextBlock>
</DataTemplate>
<DataTemplate x:Key="touchInputTemplate">
<StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Image Source="{Binding ImageUri}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Width="{Binding ImageWidth}" Height="{Binding ImageHeight}" />
<TextBlock HorizontalAlignment="Center"><Run Text="{Binding Text}" /></TextBlock>
</StackPanel>
</DataTemplate>
<local:InputModeDataTemplateSelector x:Key="inputModeTemplateSelector"
MouseInputModeTemplate="{StaticResource mouseInputTemplate}"
TouchInputModeTemplate="{StaticResource touchInputTemplate}" />
</UserControl.Resources>
<ContentControl HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" VerticalAlignment="Stretch"
VerticalContentAlignment="Stretch" ContentTemplateSelector="{StaticResource inputModeTemplateSelector}" />
</UserControl>
What am i doing wrong?
Is there a better way to achieve that?
Thank to EdPlunkett and more research i found out it is better to
use a ContentPresenter here and instead of binding on DataContext=this bind like this (as alsways suggested when writing a UserControl)
DataContext="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type yourType}}}"
Code:
<UserControl.Resources>
<DataTemplate x:Key="touchInputTemplate">
<StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Image Source="{Binding ImageUri}" Width="64" Height="64" />
<TextBlock HorizontalAlignment="Center" Text="{Binding Text}" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="mouseInputTemplate">
<TextBlock HorizontalAlignment="Center" Text="{Binding Text}" />
</DataTemplate>
<local:InputModeDataTemplateSelector x:Key="inputModeTemplateSelector"
MouseInputModeTemplate="{StaticResource mouseInputTemplate}"
TouchInputModeTemplate="{StaticResource touchInputTemplate}" />
</UserControl.Resources>
<Grid DataContext="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type yourType}}}">
<ContentPresenter Content="{Binding}" ContentTemplateSelector="{StaticResource inputModeTemplateSelector}">
</Grid>
Your ContentPresenter idea is the correct way to do it with a DataTemplateSelector, and I should have thought of it myself.
But here's yet another way to do it, which unlike my first answer, actually solves all the problems you're having:
XAML (in practice the Style would probably be defined in a separate ResourceDictionary):
<Window
x:Class="TestApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestApplication"
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
Title="MainWindow" Height="350" Width="525"
>
<Window.Resources>
<Style TargetType="local:TestControl">
<Setter Property="Background" Value="Gainsboro" />
<Style.Triggers>
<!-- The 0 value for the InputMode enum is Mouse, so this will be the default. -->
<Trigger Property="InputMode" Value="Mouse">
<Setter Property="Background" Value="Wheat" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:TestControl}">
<Grid Background="{TemplateBinding Background}">
<TextBlock HorizontalAlignment="Center"><Run Text="{TemplateBinding Text}" /></TextBlock>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="InputMode" Value="Touch">
<Setter Property="Background" Value="LightSkyBlue" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:TestControl}">
<Grid Background="{TemplateBinding Background}">
<StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Image
Source="{TemplateBinding ImageUri}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Width="{TemplateBinding ImageWidth}"
Height="{TemplateBinding ImageHeight}"
/>
<TextBlock HorizontalAlignment="Center"><Run Text="{TemplateBinding Text}" /></TextBlock>
</StackPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<local:TestControl
ImageHeight="100"
ImageWidth="100"
Text="This is the test control"
ImageUri="http://www.optimizeagency.com/wp-content/uploads/2015/09/GoogleLogo.jpg"
/>
</Grid>
</Window>
C#:
using System;
using System.Windows;
using System.Windows.Controls;
namespace TestApplication
{
class TestControl : Control
{
public TestControl()
{
// If input mode may change at runtime, you'll need an event that fires when that
// happens and updates this property.
// UIUtilities.GetInputMode() is just a stub in this example.
InputMode = UIUtilities.GetInputMode();
}
#region InputMode Property
public InputMode InputMode
{
get { return (InputMode)GetValue(InputModeProperty); }
set { SetValue(InputModeProperty, value); }
}
public static readonly DependencyProperty InputModeProperty =
DependencyProperty.Register("InputMode", typeof(InputMode), typeof(TestControl),
new PropertyMetadata(InputMode.Mouse));
#endregion InputMode Property
#region Text Property
public String Text
{
get { return (String)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(String), typeof(TestControl),
new PropertyMetadata(null));
#endregion Text Property
#region ImageUri Property
// The TemplateBinding in the template can't coerce a string to an
// ImageSource, so we have to make that happen elsewhere.
public ImageSource ImageUri
{
get { return (ImageSource)GetValue(ImageUriProperty); }
set { SetValue(ImageUriProperty, value); }
}
public static readonly DependencyProperty ImageUriProperty =
DependencyProperty.Register("ImageUri", typeof(ImageSource), typeof(TestControl),
new PropertyMetadata(null));
#endregion ImageUri Property
#region ImageHeight Property
public float ImageHeight
{
get { return (float)GetValue(ImageHeightProperty); }
set { SetValue(ImageHeightProperty, value); }
}
public static readonly DependencyProperty ImageHeightProperty =
DependencyProperty.Register("ImageHeight", typeof(float), typeof(TestControl),
new PropertyMetadata(float.NaN));
#endregion ImageHeight Property
#region ImageWidth Property
public float ImageWidth
{
get { return (float)GetValue(ImageWidthProperty); }
set { SetValue(ImageWidthProperty, value); }
}
public static readonly DependencyProperty ImageWidthProperty =
DependencyProperty.Register("ImageWidth", typeof(float), typeof(TestControl),
new PropertyMetadata(float.NaN));
#endregion ImageWidth Property
}
#region This stuff belongs in a different file
public static class UIUtilities
{
public static InputMode GetInputMode()
{
// Here you'd do whatever you're already doing to detect the input mode
return InputMode.Touch;
}
}
public enum InputMode
{
Mouse,
Touch
}
#endregion This stuff belongs in a different file
}

Control inheritance and blendability

I created a custom control which inherits from Window. The goal is to make a reusable window for all the small apps I'll program in my company, and not having to redo the header/footer/base styles... each time.
So here is what I did.
The class:
public class MyWindow : Window
{
public bool ShowHeader
{
get { return (bool)GetValue(ShowHeaderProperty); }
set { SetValue(ShowHeaderProperty, value); }
}
// Using a DependencyProperty as the backing store for ShowHeader. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ShowHeaderProperty =
DependencyProperty.Register("ShowHeader", typeof(bool), typeof(MyWindow), new PropertyMetadata(true));
public string HeaderText
{
get { return (string)GetValue(HeaderTextProperty); }
set { SetValue(HeaderTextProperty, value); }
}
// Using a DependencyProperty as the backing store for HeaderText. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HeaderTextProperty =
DependencyProperty.Register("HeaderText", typeof(string), typeof(MyWindow), new PropertyMetadata(""));
public MyWindow()
{
this.Template = FindResource("MyWindowTemplate") as ControlTemplate;
}
}
The template:
<ControlTemplate x:Key="MyWindowTemplate" TargetType="{x:Type me:MyWindow}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<DockPanel>
<Grid DockPanel.Dock="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="300" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="Images/logo.gif" />
<TextBlock Grid.Column="1" Text="{TemplateBinding HeaderText}" />
</Grid>
<ContentPresenter />
</DockPanel>
</Border>
</ControlTemplate>
Those 2 things are in a class library.
In my app, I have set this.
app.xaml:
<Application x:Class="TestInheritance.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/ClassLibrary1;component/MyStyles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
and my MainWindow.xaml is:
<uc:MyWindow x:Class="TestInheritance.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:uc="clr-namespace:ClassLibrary1;assembly=ClassLibrary1"
Title="MainWindow"
Width="525"
Height="350"
HeaderText="Test1234"
ShowHeader="True">
<Grid>
<TextBlock Text="test text" />
</Grid>
</uc:MyWindow>
All of this works as intended in execution, but in the designer, I don't see the header (no matter the value I set for ShowHeader), neither the HeaderText.
What should I do to make all this "blendable", and see the header in the designer ?
EDIT
I tried moving the ControlTemplate in a style
<Style TargetType="{x:Type me:MyWindow}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type me:MyWindow}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<DockPanel>
<Grid DockPanel.Dock="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="300" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="Images/logo.gif" />
<TextBlock Grid.Column="1" Text="{TemplateBinding HeaderText}" />
</Grid>
<ContentPresenter />
</DockPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and use this
StyleProperty.OverrideMetadata(typeof(MyWindow), new FrameworkPropertyMetadata(FindResource(typeof(MyWindow))));
in Mywindow's constructor.
So now, I have the opposite, I see the header in design mode, but not in the application anymore...
EDIT2
I moved the style in Themes\Generic.xaml and added [assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)] in the Assemblyinfo.cs of my class library.
I also added a static constructor to my class like this:
static MyWindow()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyWindow), new FrameworkPropertyMetadata(typeof(MyWindow)));
}
and it still doesn't work.
So now my solution structure is the following: (non-relevant parts omitted)
Solution
ClassLibrary1
Properties
AssemblyInfo.cs
Themes
Generic.xaml
MyWindow.cs
TestInheritance (the wpf app)
App.xaml
MainWindow.xaml
Edit3
Here is the link to my code: https://github.com/MerlinDuChaos/TestInheritance/
Set the default style in static constructor like this
static MyWindow()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyWindow), new FrameworkPropertyMetadata(typeof(MyWindow)));
}
Also dont forget to set this stupid little property:
<Style TargetType="{x:Type me:MyWindow}">
<Setter Property="Background" Value="White"/>
Try it out now
EDIT:
public partial class App : Application
{
//public App()
//{
// InitializeComponent();
// FrameworkElement.StyleProperty.OverrideMetadata(typeof(Window), new FrameworkPropertyMetadata
// {
// DefaultValue = FindResource(typeof(Window))
// });
//}
}
EDIT 2:
To force designer apply your custom window style you shall copy paste this inside app xaml.
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:/ClassLibrary1;component/Themes/Generic.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>

WPF Change a property in a customcontrol after a button click

I posted a question in this link. maybe I'm not well expressed.
It's very simple, I want to change a property in a usercontrol or CustomControl after a click on a Boutton outside...
The code of the customcontrol is as follows :
<Style TargetType="{x:Type local:CustomControl1}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomControl1}">
<Border x:Name="container" Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
</Border>
<ControlTemplate.Triggers>
<Trigger Property="Hidden" Value="true">
<Setter Property="BorderBrush" Value="Blue"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
public class CustomControl1 : Control
{
static CustomControl1()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl1), new FrameworkPropertyMetadata(typeof(CustomControl1)));
}
public bool Hidden
{
get { return (bool)GetValue(HiddenProperty); }
set { SetValue(HiddenProperty, value); }
}
// Using a DependencyProperty as the backing store for Hidder. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HiddenProperty =
DependencyProperty.Register("Hidden", typeof(bool), typeof(CustomControl1), new PropertyMetadata(false));
}
And a simple window for test
<Window x:Class="WpfTestCustomControl.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfCustomBorder;assembly=WpfCustomBorder"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="400"/>
<ColumnDefinition Width="70"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<local:CustomControl1 x:Name="cc" BorderBrush="Red" BorderThickness="3" Margin="10" Grid.RowSpan="2"/>
<Button Grid.Column="1" Content="Ok" Margin="5" Click="Button_Click"/>
</Grid>
namespace WpfTestCustomControl
{
/// <summary>
/// Logique d'interaction pour MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
cc.Hidden = true;
}
}
}
The property "Hidden" is a dependency property inside the custom control.
When i click on the button in mainwindow i want to change the hidden property to true. this must fire the trigger inside the custom control to change borderbrush to "blue" color. While nothing happen.
Is there something missing or is not the right way to do it ?
Thanks in advance..
Don't hard-set BorderBrush="Red" in your Control's declaration, it's prioritary over any trigger's setter.
You might want to check msdn's Dependency Property Value Precedence

WPF how to preserve themes in a custom control

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

Resources