How can I swap controls based on a dependency property? - c#

I am creating a custom control.
I want the template for this control to use different controls for the root control based on the value of a dependency property called CanExpand. CanExpand is defined in the custom control class.
If CanExpand is true, I want to display using an Expander:
<ControlTemplate ...>
<Expander ...>
<!--...-->
<ContentPresenter/>
</Expander>
</ControlTemplate>
If CanExpand is false, I want to display using a HeaderedContentControl instead:
<ControlTemplate ...>
<HeaderedContentControl ...>
<!--...-->
<ContentPresenter/>
</HeaderedContentControl>
</ControlTemplate>
I thought of using a DataTemplateSelector, but this is a ControlTemplate not a DataTemplate and there is no selector property for the Template of a control.
I can't set the different controls to visible/hidden with a Trigger because the child content can only live under one control. Also, I don't think you can change the Content property using a Trigger.
Any suggestions?
Thanks.

Inside your Style, set the ControlTemplate property for the default state and then have a Trigger which sets the ControlTemplate property to a different template. For example:
<Style ...>
<Setter Property="ControlTemplate">
<ControlTemplate ...>
</ControlTemplate>
</Setter>
<Style.Triggers>
<Trigger Property="YourProperty" Value="WhateverValue">
<Setter Property="ControlTemplate">
<ControlTemplate ...>
</ControlTemplate>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
Keep in mind you can have a trigger on the same property for multiple values, each value getting a completely different template.

Related

Creating style resource for custom control removes template

I've got a custom control in WPF, which has a variety of dependency properties that allow visual customization. For the sake of brevity I won't post the entire control, but it basically is setup like this:
<UserControl.Resources>
<Style TargetType="{x:Type MyControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type MyControl}">
<Border BorderBrush="{TemplateBinding BorderColor}">
// more stuff here
<ContentPresenter/>
</Border>
</ControlTemplate>
</Setter>
</Setter>
</Style>
</UserControl.Resources>
The BorderColor property works fine if I set it directly, like this:
<ctl:MyControl BorderColor="Brushes.Red">....</ctl:MyControl>
But I want to set it application-wide. The problem I have is if I simply set the style with no key, it does not apply. Like this:
<Window.Resources>
<Style TargetType="{x:Type ctl:MyControl}">
<Setter Property="BorderColor" Value="Brushes.Red"/>
</Style>
</Window.Resources>
This does not do anything to the control. So I thought I'd just set a key and apply that style, like this:
<Style TargetType="{x:type ctl:MyControl}" x:Key="myStyle">....</Style>
<ctl:MyControl Style="{StaticResource myStyle}">.....</ctl:MyControl>
But this causes the control to vanish, I'm assuming because it's removing the Template. What am I doing wrong? With other framework controls you can just set the properties you want without losing the control template.
You need to inherit from the default style you have created.
inherit style from default style

WPF ControlTemplate AND DataTemplate

I have ListView where I would like to apply a custom ControlTemplate to it's items. It is defined like this:
<ListView ItemsSource="{Binding MyAwesomeItems}" ...
MyAwesomeItems holds different classes. So I thought to myself: "Well, hello DataTemplates."
To make the contained items look the way I want them to, I have defined a ControlTemplate like this:
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<Border><ContentControl Content="{TemplateBinding Content}"/></Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
I have used ContentControl with Binding to TemplateBinding Content. I expected that WPF would then insert my items inside that ContentControl, using whatever DataTemplate I have defined for it.
But instead, it looks like WPF just uses the items .ToString() and does not apply any DataTemplates. Is this intended behaviour?
What I want to achieve is: Have a list of items, where the container of each item looks exactly the way I want and the content of that container comes from the DataTemplate.
In a ControlTemplate for a ContentControl you usually use an empty ContentPresenter tag. In your case:
<ControlTemplate TargetType="ListViewItem">
<Border>
<ContentPresenter/>
</Border>
</ControlTemplate>
The ContentPresenter has a ContentSource property which defaults to "Content" and sets all the necessary properties (Content, ContentTemplate, etc.).
See here for details.

WPF XAML display content in a ContentControl

I need to display a number in a square, centered horizontally and vertically.
When I tried to use a label for that purpose, it seemed like it ignored the centering completely. So I decided to use a grid and display a label on the grid as that centers perfectly.
I need to use a template as there's several themes available. From what I've found on the internet, I thought this ( ignoring the centering for now )
<ControlTemplate x:Key="ClockTemplate">
<Grid>
<Grid.Style>
<Style TargetType="Grid">
<Setter Property="Background" Value="White"/>
</Style>
</Grid.Style>
<Label>
<Label.Style>
<Style TargetType="Label">
<Setter Property="Foreground" Value="#376092"/>
</Style>
</Label.Style>
<ContentPresenter/>
</Label>
</Grid>
</ControlTemplate>
would be correct. Using it as follows:
<ContentControl Content="20" Height="64" Width="64" Template="{DynamicResource ClockTemplate}"/>
the content is not displayed tho, what am I doing wrong? Also, is there a better way to achieve my goal?
As per my understanding this is not the correct approach. Instead of creating ControlTemplate you have to write a Style for your control like below, also use StaticResource binding if possible. It is faster than Dynamic binding. Please not that, I have not mentioned the Label size inside the ControlTemplate. Please do it based on your needs
<Style x:Key="ContentControlStyle"
TargetType="ContentControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ContentControl">
<Grid Background="White">
<Label Foreground="#376092"
Width="200"
Height="100" Content="{TemplateBinding Content}">
</Label>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
bind your ContentControl with the newly created Style like below
<ContentControl Style="{StaticResource ContentControlStyle} ">
If your requirement is only to set some value in ContentControl, use Label instead and change the Style of the Label. Because ContentControl is heavy

Wpf, style is not being applied

I've written a user control with popup, who's content is being set outside the control. The ControlTemplate of that control looks like the following:
<ControlTemplate TargetType="local:InfoIcon">
<Grid>
<ToggleButton x:Name="HelpButton" Style="{StaticResource HelpButton}" />
<Popup PlacementTarget="{Binding ElementName=HelpButton}" Placement="Bottom"
IsOpen="{Binding ElementName=HelpButton, Path=IsChecked, Mode=TwoWay}" StaysOpen="False">
<Border BorderBrush="#767676" BorderThickness="1"
Background="#f1f2f7">
<Border.Resources>
<!-- Important -->
<Style TargetType="Label">
<Setter Property="Foreground" Value="#575757" />
</Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="#575757" />
</Style>
<!-- /Important -->
</Border.Resources>
<ContentPresenter Content="{TemplateBinding HelpContent}" />
</Border>
</Popup>
</Grid>
</ControlTemplate>
The Important part - I want to assign custom styles to items, which are being put inside the popup (it serves as a clickable hint)
I'm using my control in the following way:
<local:MyControl>
<local:MyControl.HelpContent>
<TextBlock>Ala ma kota</TextBlock>
</local:MyControl.HelpContent>
</local:MyControl>
But despite styles in the Border, TextBlock's text's color always inherit the value from its parent (checked using Snoop) - resulting in white text on white background.
You can downlad the small PoC application, which demonstrates the problem.
My observations:
The styling does work for Label. It only doesn't work for TextBlock.
When I add TextBlock.Foreground="Red" to the Border, TextBlock becomes red, still ignoring style (but now using color from Border).
Snoop informs, that this TextBlock actually has the Style resolved correctly. But despite it shouldn't, it uses the inherited value instead of one specified in the style.
How can I solve this problem and why does it occur?
I received answer on Microsoft forums; I'll leave it here in case someone encounters the same problem.
The difference is that a TextBlock is not a control, i.e. it doesn't have any ControlTemplate and because of this the implicit style doesn't get applied to it when it is located inside the StackPanel. Please see the following page for more information: http://blogs.msdn.com/b/wpfsdk/archive/2009/08/27/implicit-styles-templates-controls-and-frameworkelements.aspx
You could use Label elements or set the style for the TextBlock elements explicitly.
-- Magnus (MM8)
Edit2
I've set the Foreground of the UserControl to something else. This behavior is because the child TextBlock controls of the UserControl inherit the Foreground-Settings somehow. This has nothing to do with the popup or some other approaches we tried yet.
I've stumbled upon another question with a similar problems here: Cannot override controls foreground colour in wpf
I suggest to accept this strange behavior and just set a Foreground Color of the UserControl instead:
<Style TargetType="{x:Type local:InfoIcon}">
<Setter Property="Foreground" Value="Red"/>
</Style>
previous Edit
You had my curiousity with this weird behavior, but after looking at your PoC it was rather obvious :) The Popup has some attached Properties TextElement.* where you can style the text elements in the popup. This was new to me, too and I will reseach a bit more afterwards. Nevertheless: Workaround for your Problem is to not style the TextBlock but the Popup instead. your code could look something like following :
<ControlTemplate TargetType="{x:Type local:InfoIcon}">
<ControlTemplate.Resources>
<Style TargetType="Popup">
<Setter Property="TextElement.Foreground" Value="Red"/>
</Style>
<Style TargetType="Label">
<Setter Property="Foreground" Value="Red" />
</Style>
</ControlTemplate.Resources>
<Grid>
<ToggleButton x:Name="TB" Width="16" Height="16"/>
<Popup Placement="Bottom" PlacementTarget="{Binding ElementName=TB}" IsOpen="{Binding ElementName=TB, Path=IsChecked}" StaysOpen="False">
<ContentPresenter Content="{TemplateBinding InfoContent}"/>
</Popup>
</Grid>
</ControlTemplate>
I changed the styles to be outside of the controls, of course you can just use the attached properties of the popup directly. But initially you wanted to know how it works with the styles attached at the border, it does not matter now where you add the styles. You can use a ResourceDictionary for example.
As a suggestion, shouldn't this:
TargetType="local:InfoIcon"
be like this?
TargetType="{x:Type local:InfoIcon}"
Maybe you have some TextBlock style defining that it shouldd take the parent's control foreground.
Did you try to add a BasedOn property like this ?
<Style TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}">
<Setter Property="Foreground" Value="#575757" />
</Style>
I tried with your code example and this works :
<ContentPresenter Content="{TemplateBinding InfoContent}">
<ContentPresenter.Style>
<Style TargetType="{x:Type ContentPresenter}">
<Setter Property="TextBlock.Foreground" Value="Red" />
</Style>
</ContentPresenter.Style>
<ContentPresenter.Resources>
<Style TargetType="{x:Type Label}">
<Setter Property="Foreground" Value="Red" />
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
That's kind of odd because when I put the Foreground setter for the Label control inside the ContentPresenter.Style then this time it's Label wich doesn't work...I think it's because Label is a considered as a ContentControl whereas TextBlock is just a FrameworkElement.
Had a similar issue caused by another problem:
There is a strange bug in WPF that prevents styles, defined in merged dictionaries, from being applied to the first element:
https://www.engineeringsolutions.de/wpf-fix-style-is-only-applied-to-first-element/

Custom Wpf Lookless contol...Dynamically Decide Control type

How to decide the type of a custom lookless control on run time.I have to decide the controls type(ie,whether textbox or combo) on runtime(actually when some Dependency property is bound).How can i do it? Can i define where to inherit from on run time..?
You create a control that inherit from FramewrokElement (or Decorator, if you want a quick implementation and don't care about using a type for something it's not supposed to do) and create the required control as a child of your control when the dependency property is set.
You can use a Trigger that sets the ControlTemplate property of your control.
<Style TargetType={x:Type local:MyControl}>
<Style.Triggers>
<Trigger Property="MyProperty" Value="MyValue1">
<Setter Property="ControlTemplate">
<Setter.Value>
<ControlTemplate TargetType={x:Type local:MyControl}>
<!-- first template -->
</ControlTemplate
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="MyProperty" Value="MyValue2">
<Setter Property="ControlTemplate">
<Setter.Value>
<ControlTemplate TargetType={x:Type local:MyControl}>
<!-- second template -->
</ControlTemplate
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers

Categories

Resources