I know how to create a custom user control in WPF but how can I make it so that someone can provide an ItemTemplate?
I have a user control that is a mixture of several other WPF controls, one of them being a ListBox. I'd like to let the user of the control specify the content of the list box but I'm not sure how to pass that information through.
EDIT: The accepted answer works with the following correction:
<UserControl x:Class="WpfApplication6.MyControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:WpfApplication6">
<ListBox ItemTemplate="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type src:MyControl}}, Path=ItemsSource}" />
</UserControl>
You will want to add a DependencyProperty to your control. The xaml will look slightly different if you are deriving from UserControl or Control.
public partial class MyControl : UserControl
{
public MyControl()
{
InitializeComponent();
}
public static readonly DependencyProperty ItemTemplateProperty =
DependencyProperty.Register("ItemTemplate", typeof(DataTemplate), typeof(MyControl), new UIPropertyMetadata(null));
public DataTemplate ItemTemplate
{
get { return (DataTemplate) GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
}
Here is xaml for a UserControl.
<UserControl x:Class="WpfApplication6.MyControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:WpfApplication6">
<ListBox ItemTemplate="{Binding ItemTemplate, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type src:MyControl}}}" />
</UserControl>
Here is xaml for a Control:
<Style TargetType="{x:Type src:MyControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type src:MyControl}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ListBox ItemTemplate="{TemplateBinding ItemTemplate}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Related
Edit:
How is the proper way to create a Control to avoid the following problem with the ElementName Binding:
<TextBox x:Name="MyTextBox" Text="some Text"></TextBox>
<Label>
<!--Binding works-->
<TextBlock Text="{Binding Path=Text, ElementName=MyTextBox, FallbackValue='Binding Failed'}"></TextBlock>
</Label>
<Button>
<!--Binding works-->
<TextBlock Text="{Binding Path=Text, ElementName=MyTextBox, FallbackValue='Binding Failed'}"></TextBlock>
</Button>
<local:MyUserControl>
<!-- THIS BINDING FAILS !!!-->
<TextBlock Text="{Binding Path=Text, ElementName=MyTextBox, FallbackValue='Binding Failed'}"></TextBlock>
</local:MyUserControl>
MyUserControl.xaml:
<UserControl x:Class="Problem.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" />
MyUserControl.xaml.cs
public partial class MyUserControl : UserControl
{
public MyUserControl()
{
InitializeComponent();
}
}
Original:
Im still new to WPF and could not find out how to do this properly.
I basically want a UserControl's child to retain the behavior of being able to bind to the root element's x:Name in a XAML.
This is an example that shows the problem caused by my UserControl Descriptor compared to the WPF Controls:
<Parent x:Name="_thisParent">
...
<Label>
<!-- Binding to _thisParent works -->
<TextBlock Text="{Binding Path=MyText, ElementName=_thisParent}" />
</Label>
<uc:Descriptor Text="description: ">
<!-- Binding to _thisParent FAILS !! -->
<TextBlock Text="{Binding Path=MyText, ElementName=_thisParent}" />
</uc:Descriptor>
<Button>
<!-- Binding to _thisParent works -->
<TextBlock Text="{Binding Path=MyText, ElementName=_thisParent}" />
</Button>
Here is the Code for my UserControl:
Descriptor.xaml
<UserControl
x:Class="EmbedContent.UserControls.Descriptor"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="_thisDescriptor">
<UserControl.Template>
<ControlTemplate>
<DockPanel>
<TextBlock DockPanel.Dock="Left" Text="{Binding Path=Text, ElementName=_thisDescriptor, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, FallbackValue='Binding Failed'}" />
<ContentPresenter Content="{Binding Path=Content, ElementName=_thisDescriptor, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, FallbackValue='Binding Failed'}" />
</DockPanel>
</ControlTemplate>
</UserControl.Template>
Descriptor.xaml.cs
public partial class Descriptor : UserControl
{
#region Ctor
public Descriptor()
{
InitializeComponent();
}
#endregion
#region Dependency-Properties
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(Descriptor), new PropertyMetadata("Descriptor's default Text"));
#endregion
How would i need to implement a custom UserControl / ContentControl (if needed without xaml) to retain the behavior of the WPF Controls?
How is this done according to best practice anyway? I assume i only run into this problem because i'm doing something wrong.
Answer, including clarifying your decision: I finally found a solution.
Think about the problem a little deeper.
Binding a property of an element is part of the functionality of the element itself, not of its container.
Therefore, if you change the DataContext of an element, the default bindings are interpreted relative to it, not the DataContext of its container.
For this reason, you should not assign the DataContext inside a UserControl, as the default bindings behavior will change in a way that is unexpected for the user (programmer).
Now think about how ElementName bindings work.
For example, in the XAML that creates a new class from UserControl, you have defined named elements.
And then create multiple instances of UserControl.
In this case, if all elements with the same names were in the same visual tree, this would create a conflict, since it is not known which of them is being accessed.
(In this case, the "visual tree" is an oversimplification. It actually has to do with the system for registering the names of the UI elements).
To avoid such conflicts, when using XAML for ANY element (not only UserControl), its own local visual tree is created that is not associated with the main visual tree.
BUT!
I already wrote above, Bindings are part of the functionality of the element itself.
And therefore, the binding of a property of type ElementName will search for an element by name not higher in the container, but in the UserControl itself.
The same goes for Templates.
Therefore, the names of elements inside Templates are not visible outside of it.
Default elements are not implemented as UserControl, but as Custom Control.
In this case, there is a separate Sharp class with element logic.
And a separate default Template for this class.
In this implementation, since no XAML is used to create the element, the element does not have its own internal local visual tree.
And all its bindings (of the ElementName type) are interpreted in relation to the general visual tree.
To some extent, you partially implemented this in the Descriptor: ContentControl class.
Only instead of regregistering the default Template for the type, you set it up in the XAML App.
This implementation will work for an example, but in many cases it can create other problems.
Therefore, it is better to use the "standard" Custom Control for which the theme is created and the default Template is registered.
In the case of DescriptorTwo, you specify x: Class = "EmbedContent.UserControls.DescriptorTwo" and this automatically creates its own local visual tree, relative to which the ElementName bindings will work.
Slightly modified example for the solution you found.
using System.Windows;
using System.Windows.Controls;
namespace EmbedContent.CustomControls
{
public class Descriptor : ContentControl
{
#region Registering a default template
static Descriptor()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Descriptor), new FrameworkPropertyMetadata(typeof(Descriptor)));
}
#endregion
#region Ctor
public Descriptor() : base() { }
#endregion
#region Dependency-Properties
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register(nameof(Text), typeof(string), typeof(Descriptor), new PropertyMetadata("Descriptor's default Text"));
#endregion
}
}
Theme with default templates - file Themes/Generic.xaml:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:customcontrols="clr-namespace:EmbedContent.CustomControls">
<Style TargetType="{x:Type customcontrols:Descriptor}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type customcontrols:Descriptor}">
<StackPanel>
<TextBlock x:Name="tblock" Text="Example Text in UserControl"/>
<TextBlock Text="{TemplateBinding Text}"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
DescriptorTwo.xml:
<UserControl x:Class="EmbedContent.UserControls.DescriptorTwo"
x:Name="PART_Main"
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:EmbedContent.UserControls"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<StackPanel>
<TextBlock x:Name="tblock" Text="Example Text in UserControl"/>
<TextBlock Text="{Binding Text, ElementName=PART_Main}"/>
</StackPanel>
</UserControl>
Examle Window:
<Window x:Class="EmbedContent.ExampleWindow"
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:EmbedContent"
xmlns:customcontrols="clr-namespace:EmbedContent.CustomControls"
xmlns:usercontrols="clr-namespace:EmbedContent.UserControls"
mc:Ignorable="d"
Title="ExampleWindow" Height="450" Width="800">
<StackPanel>
<TextBlock x:Name="tblock" Text="Example Text in Parent UIElement"/>
<Border Background="LightBlue" Margin="20" Padding="10"
BorderBrush="Blue" BorderThickness="2">
<customcontrols:Descriptor Text="{Binding Text, ElementName=tblock}"/>
</Border>
<Border Background="LightGreen" Margin="20" Padding="10"
BorderBrush="Green" BorderThickness="2">
<usercontrols:DescriptorTwo Text="{Binding Text, ElementName=tblock}"/>
</Border>
</StackPanel>
</Window>
Result:
As you can see in the XAML Designer and when launched at runtime, such bindings (in the UserControl) also work differently, which causes additional problems.
Therefore, the main purpose of the UserControl is to represent the Data coming through the DataContext.
Also consider the moment with the assignment of Content.
In UserControl in XAML, you set a value for this property.
And assigning it when used does not complement the presentation, but replace it.
In CustonControl, you can explicitly specify in the Template where to insert additional content.
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:customcontrols="clr-namespace:EmbedContent.CustomControls">
<Style TargetType="{x:Type customcontrols:Descriptor}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type customcontrols:Descriptor}">
<StackPanel>
<TextBlock x:Name="tblock" Text="Example Text in UserControl"/>
<TextBlock Text="{TemplateBinding Text}"/>
<ContentPresenter Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
<Window x:Class="EmbedContent.ExampleWindow"
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:EmbedContent"
xmlns:customcontrols="clr-namespace:EmbedContent.CustomControls"
xmlns:usercontrols="clr-namespace:EmbedContent.UserControls"
mc:Ignorable="d"
Title="ExampleWindow" Height="250" Width="400">
<StackPanel>
<TextBlock x:Name="tblock" Text="Example Text in Parent UIElement"/>
<Border Background="LightBlue" Margin="20" Padding="10"
BorderBrush="Blue" BorderThickness="2">
<customcontrols:Descriptor Text="{Binding Text, ElementName=tblock}">
<TextBlock Text="{Binding Text, ElementName=tblock}"/>
</customcontrols:Descriptor>
</Border>
<Border Background="LightGreen" Margin="20" Padding="10"
BorderBrush="Green" BorderThickness="2">
<usercontrols:DescriptorTwo Text="{Binding Text, ElementName=tblock}">
<TextBlock Text="{Binding Text, ElementName=tblock}"/>
</usercontrols:DescriptorTwo>
</Border>
</StackPanel>
</Window>
Result:
I finally found a solution but it still seems somewhat ugly so i expect someone to post a better solution.
I post the code here including an example highlighting the problem again.
Solution:
split UserControl / ContentControl up into a .cs class inheriting from e.g. ContentControl and set the Template describing the extra elements in the App.xaml Resources. Basically move the .xaml part as style into Resources.
Descriptor.cs
namespace EmbedContent.UserControls
{
class Descriptor : ContentControl{
#region Ctor
public Descriptor() : base() {}
#endregion
#region Dependency-Properties
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(Descriptor), new PropertyMetadata("Descriptor's default Text"));
#endregion
}
}
App.xaml
<Application
x:Class="EmbedContent.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:EmbedContent"
xmlns:uc="clr-namespace:EmbedContent.UserControls"
StartupUri="MainWindow.xaml">
<Application.Resources>
<Style TargetType="uc:Descriptor">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="uc:Descriptor">
<DockPanel>
<TextBlock DockPanel.Dock="Left" Text="{Binding Path=Text, RelativeSource={RelativeSource AncestorType=uc:Descriptor}}" />
<ContentPresenter />
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
The above can call and consume the Descriptor as i expect it from a WPF Control.
Here the Code for the UserControl, that breaks the binding when used:
DescriptorTwo.xaml
<UserControl
x:Class="EmbedContent.UserControls.DescriptorTwo"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="_thisDescriptorTwo">
<UserControl.Template>
<ControlTemplate>
<DockPanel>
<TextBlock DockPanel.Dock="Left" Text="{Binding Path=Text, ElementName=_thisDescriptorTwo}" />
<ContentPresenter Content="{Binding Path=Content, ElementName=_thisDescriptorTwo}" />
</DockPanel>
</ControlTemplate>
</UserControl.Template>
DescriptorTwo.xaml.cs
namespace EmbedContent.UserControls
{
public partial class DescriptorTwo : UserControl
{
#region Ctor
public DescriptorTwo()
{
InitializeComponent();
}
#endregion
#region Dependency-Properties
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(DescriptorTwo), new PropertyMetadata("Descriptor's default Text"));
#endregion
}
}
And here an example of how both are called from another UserControl
Example.xaml
<UserControl
x:Class="EmbedContent.UserControls.EmbedContentExample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:uc="clr-namespace:EmbedContent.UserControls"
x:Name="parent">
<StackPanel>
<Label>
<!-- Binding to parent works -->
<TextBlock Text="{Binding Path=MyText, ElementName=parent}" />
</Label>
<uc:Descriptor Text="description: ">
<!-- Binding to parent works -->
<TextBlock Text="{Binding Path=MyText, ElementName=parent}" />
</uc:Descriptor>
<uc:DescriptorTwo Text="description: ">
<!-- Binding to parent fails -->
<TextBlock Text="{Binding Path=MyText, ElementName=parent}" />
</uc:DescriptorTwo>
</StackPanel>
and the codebehind
Example.xaml.cs
namespace EmbedContent.UserControls
{
public partial class EmbedContentExample : UserControl
{
public EmbedContentExample()
{
InitializeComponent();
}
public string MyText
{
get { return (string)GetValue(MyTextProperty); }
set { SetValue(MyTextProperty, value); }
}
public static readonly DependencyProperty MyTextProperty =
DependencyProperty.Register("MyText", typeof(string), typeof(EmbedContentExample), new PropertyMetadata("EmbedContentExample's MyText"));
}
}
It seems very hard to achieve something rather trivial in WPF...
I need to design a toggle button with a specific look (and feel). I made a small project to demonstrate the problem.
"ToggleButton user control" :
<UserControl x:Class="WpfApp4.UserControl1"
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"
mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"
Name="Bla">
<UserControl.Resources>
<Style TargetType="ToggleButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Ellipse Width="300" Height="300" Fill="Yellow"/>
<ContentPresenter />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<ToggleButton
Width="300" Height="300">
<ContentControl Content="{Binding ElementName=Bla, Path=MainContent}"/>
</ToggleButton>
</UserControl>
Dependency property:
public static DependencyProperty MainContentProperty = DependencyProperty.Register(
"MainContent",
typeof(object),
typeof(UserControl1),
null);
public object MainContent
{
get => GetValue(MainContentProperty);
set => SetValue(MainContentProperty, value);
}
The way I want to use the control:
<local:UserControl1>
<TextBlock>Whatever</TextBlock>
</local:UserControl1>
When I run the program, the textbox appears "Whatever", but the style is not applied, the ellipse won't show.
What's the correct way of doing this?
=== Update ===
OK, getting somewhere... finally...
Now I got this as user control:
<UserControl x:Class="WpfApp4.UserControl1"
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:wpfApp4="clr-namespace:WpfApp4"
mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"
Name="Bla">
<UserControl.Resources>
<Style TargetType="wpfApp4:UserControl1">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<ToggleButton>
<Grid>
<Ellipse Width="300" Height="300" Fill="Yellow"/>
<ContentPresenter Content="{Binding ElementName=Bla, Path=MainContent}" />
</Grid>
</ToggleButton>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<ContentPresenter/>
</UserControl>
And this is how I use it:
<local:UserControl1>
<local:UserControl1.MainContent>
<TextBlock>Whatever</TextBlock>
</local:UserControl1.MainContent>
</local:UserControl1>
That finally gives me a toggle button with the style applied (the ellipse shows up) and the textbox is shown as well.
So, this works. Is this the way you mean it should work? Or can it be simplified?
It should be more like
<local:UserControl1>
<local:UserControl1.MainContent>
<TextBlock>Whatever</TextBlock>
</local:UserControl1.MainContent>
</local:UserControl1>
But you should look forward overriding ContentControl which would be more adequate rather then using UserControl.
By the way why did you put a ContentControl inside ToggleButton? ToggleButton by itself is a ContentControl it has it's own Content property.
Update:
All depends on what you whant to do. If it is only change the visual of the toggle button, then just create a toggle button style like this:
<ToggleButton>
<TextBlock>Whatever</TextBlock>
<ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Grid>
<Ellipse Width="300" Height="300" Fill="Yellow"/>
<ContentPresenter Content="{TemplateBinding Content}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ToggleButton.Style>
</ToggleButton>
Ofcorse if you want to use your style across the application it is better to define the style in a resource dictionnary (for exemple in App.xaml), give it a key and call it on each toggle button using {StaticResource key}.
If on the other hand, you want to add some logic, you have to create a control class inheriting from ToggleButton and add the logic inside.
I have a control PaneBase, which derives from UserControl. There is no XAML, it's just a control. The UserControl type in question is the one from Catel, but I still observe this issue when using System.Windows.Controls.UserControl.
public class PaneBase : UserControl
{
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title",
typeof(string),
typeof(PaneBase),
new PropertyMetadata(default(string)));
public string Title
{
get => (string) GetValue(TitleProperty);
set => SetValue(TitleProperty, value);
}
}
I have another control EquationPane which derives from PaneBase and does have XAML:
EquationPane.xaml
<local:PaneBase x:Class="EngineersToolkit.Windows.Views.Panes.EquationPane"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:catel="http://schemas.catelproject.com" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:EngineersToolkit.Windows.Views.Panes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="Equation"
d:DataContext="{d:DesignData EquationPaneViewModel}" d:DesignHeight="450" d:DesignWidth="800"
mc:Ignorable="d">
<Grid>
<TextBox Width="150" Height="30"
Text="{Binding Equation, Mode=TwoWay}" />
</Grid>
</local:PaneBase>
EquationPane.xaml.cs
public partial class EquationPane
{
public EquationPane()
{
InitializeComponent();
DataContext = new EquationPaneViewModel();
}
}
All of this works so far, when I include an EquationPane in a window, then it shows up normally:
<Grid>
<panes:EquationPane Width="100" Height="100"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Background="Aqua" />
</Grid>
It continues to work properly if I attempt to style the control:
<Style TargetType="{x:Type panes:EquationPane}">
<Setter Property="Background" Value="Red" />
</Style>
However, things begin to fall apart if I target the style to PaneBase instead. If I change TargetType to PaneBase, then the style is simply not applied. However, if I override the dependency property metadata in PaneBase like this:
static PaneBase()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(PaneBase),
new FrameworkPropertyMetadata(
typeof(PaneBase)));
}
Then the control ceases to render at all, leaving me with a blank window. Why is this happening?
Why is this happening?
WPF controls are "lookless" which means that they have no appearance unless you define a template for them.
The following indicates that there is a default style defined for PaneBase in themes/generic.xaml:
static PaneBase()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(PaneBase),
new FrameworkPropertyMetadata(
typeof(PaneBase)));
}
You should then define such a style:
<Style TargetType="local:PaneBase">
<Setter Property="Background" Value="Red" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type UserControl}">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
If you remove the static constructor in PanelBase it will inherit the template from the style of the UserControl base class.
If you then define a Style for PaneBase like this:
<Style TargetType="{x:Type local:PaneBase}">
<Setter Property="Background" Value="Red" />
</Style>
...it no longer has a template and that's why you don't see the control in your window.
Implicit Styles are not automatically applied to the derived types. To apply the style of PaneBase to EquationPane, you can do one of the following:
1.Write this on your EquationPane.xaml.cs
public EquationPane()
{
this.SetResourceReference(StyleProperty, typeof(PaneBase));
}
2.Explicitly provide the style where EquationPane is used
<panes:EquationPane Style="{DynamicResource {x:Type PaneBase}}"/>
3.Define a style that is basedon the parent style
<Style x:Key="{x:Type PaneBase}" TargetType="{x:Type PaneBase}">
<Setter Property="Background" Value="Red" />
</Style>
<Style TargetType="{x:Type panes:EquationPane}" BasedOn="{StaticResource {x:Type PaneBase}}"/>
So, I have created a UserControl which really is an "advanced button".
I have implemented Dependency Properties, one of which is ICommand, that is supposed to be bindable further when control is used in an actual Window.
However, for some reason the Command doesn't work.
When I tried an exact same approach on a regular button, everything worked fine (thus it's not the fault of my DelegateCommand implementation or my ViewModel).
I tried to followup on why the bound command doesn't fire, but I couldn't find a reliable reason for it not to.
Here is my UserControl XAML:
<Window x:Class="NoContact.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:local="clr-namespace:NoContact"
mc:Ignorable="d"
xmlns:userControls="clr-namespace:NoContact.UserControls"
Title="MainWindow" Height="800" Width="960" Background="#22282a">
<Border BorderThickness="1" BorderBrush="#ffcd22" Margin="10,10,10,10">
<Grid>
<Button HorizontalContentAlignment="Stretch" Foreground="{Binding ElementName=ImageButtonUC, Path=Foreground}"
Background="{Binding ElementName=ImageButtonUC, Path=Background}">
<DockPanel Width="{Binding ElementName=ImageButtonUC, Path=ActualWidth}">
<Image Source="{Binding ElementName=ImageButtonUC, Path=Image}" DockPanel.Dock="Left"
Height="{Binding ElementName=ImageButtonUC, Path=ActualHeight, Converter={StaticResource HeightConverter}}"
Width="{Binding ElementName=ImageButtonUC, Path=ActualWidth, Converter={StaticResource HeightConverter}}" VerticalAlignment="Center"/>
<TextBlock Text="{Binding ElementName=ImageButtonUC, Path=Text}" FontSize="17" VerticalAlignment="Center" />
</DockPanel>
</Button>
</Grid>
<UserControl.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter Content="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
And here is my UserControl code-behind:
public partial class ImageButton : UserControl
{
// OTHER IRRELEVANT CLASS PARAMETERS ARE HERE
public ICommand ClickCommand
{
get { return (ICommand)GetValue(ClickCommandProperty); }
set { SetValue(ClickCommandProperty, value); }
}
// OTHER IRRELEVANT DEPENDENCY PROPERTIES ARE HERE
public static DependencyProperty ClickCommandProperty =
DependencyProperty.Register("ClickCommand", typeof(ICommand), typeof(ImageButton));
public ImageButton()
{
InitializeComponent();
}
}
Finally, this is how I use my control:
<userControls:ImageButton x:Name="phoneButton" ClickCommand="{Binding Path=MyButtonClickCommand}" Style="{StaticResource phoneImageButtonUCStyle}" Text="Telefon" Width="200" Height="100" VerticalAlignment="Top" />
The DataContext of the control is set on a stackpanel, that is wrapped around my control. I have also tried setting it directly on the control, no effect.
Again, doing the same on a regular button works just fine.
I have finally solved the problem - and it was pretty trivial.
I had ommited the most important binding - UserControl's button Command to UserControl's Dependency property.
Changing it like this made it work:
<Button HorizontalContentAlignment="Stretch" Foreground="{Binding ElementName=ImageButtonUC, Path=Foreground}"
Background="{Binding ElementName=ImageButtonUC, Path=Background}"
Command="{Binding ElementName=ImageButtonUC, Path=ClickCommand}">
I just created a DependencyProperty for my custom button.
The property is called IsChecked.
If IsChecked == true my button shall change its background color to something else and keep this color until IsChecked == false.
Here is my DependencyProperty:
public static readonly DependencyProperty IsCheckedProperty = DependencyProperty.Register("IsChecked", typeof(bool), typeof(MainMenuButton), new PropertyMetadata(false));
public bool IsChecked
{
get { return (bool)GetValue(IsCheckedProperty); }
set { SetValue(IsCheckedProperty, value); }
}
Next I have a ControlTemplate for this button:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ControlTemplate x:Key="MainMenuButtonStyle" TargetType="{x:Type UserControl}">
<ControlTemplate.Resources>
/* Some storyboards */
</ControlTemplate.Resources>
<Grid x:Name="grid">
<Grid.Background>
<SolidColorBrush Color="{DynamicResource MainUI_MainMenuButtonBackground}"/>
</Grid.Background>
</Grid>
<ControlTemplate.Triggers>
/* Some triggers */
</ControlTemplate.Triggers>
</ControlTemplate>
My problem is now how I can access IsChecked and based on the current value of it change the background of grid? Haven't done that before only tried it some time ago and totally failed.
Thanks in advance :)
Edit:
Here is the UserControl aswell:
<UserControl
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"
mc:Ignorable="d"
x:Class="IFCSMainInterface.MainMenuButton"
x:Name="UserControl"
d:DesignWidth="640" d:DesignHeight="480" Width="272" Height="110" Template="{DynamicResource MainMenuButtonStyle}">
<Grid x:Name="LayoutRoot"/>
I think the problem is here:
<ControlTemplate x:Key="MainMenuButtonStyle" TargetType="{x:Type UserControl}">
You must set the correct type in order to use its dependency property.
Write in your ResourceDictionary the namespace of the class, and put your type correctly in control template, eg:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:ProjectName.NameSpace">
<ControlTemplate TargetType="my:MainMenuButton" x:Key="MainMenuButtonStyle">
<!-- ... -->
</ControlTemplate>
</ResourceDictionary>
Edit (now that was explained):
You're trying to define a template in xaml own control
That does not seem quite right to do, I suggest looking for other approaches, such as creating a control that uses a Generic.xaml
( http://utahdnug.org/blogs/xamlcoder/archive/2007/12/13/building-custom-template-able-wpf-controls.aspx )
but if you want to use the dependency properties (the way you're doing) you can try the following code (for example):
<UserControl x:Class="SamplesStack.Controls.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:SamplesStack.Controls">
<UserControl.Template>
<ControlTemplate TargetType="UserControl">
<Grid x:Name="LayoutRoot">
<ContentPresenter />
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=Self}, Path=IsChecked}" Value="True">
<Setter TargetName="LayoutRoot" Property="Background" Value="Red"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</UserControl.Template>
This will make your control work as expected. Though not think very cool use DataTrigger.