How to change the control template effectively in WPF? - wpf

The subject is that I've defined a customized control bound to a DataContext. And I wished to change the control template dynamically according to the DataContext's specific property values.
There're 2 ways I've thought of,but I have no idea about which way is better.
1.Do not use the control template in a ResourceDictionary and all details of the control are defined in C# code.Use the DependencyProperty CallBack method to render the control when DataContext's property values change.
2.Define control template in the ResourceDictionary and use DataTrigger to change the 'Control.Template' property.
In my application,thousands of instances in this type would be created,so it's really unacceptable if the ControlTemplate changging is not effective.
Could you please give me some advices or better solutions?

Using any standard WPF technique might not be effective if it would involve a thousands of instances of complex controls. See http://msdn.microsoft.com/en-us/magazine/dd483292.aspx.
I would go with MultiBinding + IMultiValueConverter to Control.Template dependency property, since Template would depend on multiple DataContext properties and would, perhaps, involve complex logic.

Perhaps you could used a ContentPresenter in your ControlTemplate to customize parts of your control. You could provide DataTemplates for those customizable parts which are automatically applied.

I would use a style with the data triggers to control which template is displayed. Like this example:
<Style x:Key="Die1Face" TargetType="{x:Type Button}">
<Setter Property="Template" Value="{StaticResource dieNone}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=ThrowDie1[0]}" Value="1" >
<Setter Property="Template" Value="{StaticResource dieOneA}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=ThrowDie1[0]}" Value="2" >
<Setter Property="Template" Value="{StaticResource dieTwoA}" />
</DataTrigger>
</Style.Triggers>
</Style>
This would give the flexibility you need.

Related

Change a Style based on Visual State

"Blend makes it simple to setup Visual States based on say minWindowWidth. But it would be very nice to have state-based Styles defined for TextBlock elements, say with tag="header", tag="body", and have the Setter change the Style automatically."
Maybe I wasn't explicit enough in my initial question, let me rephrase this.
I am resizing my application window from say Landscape to Portrait. My VisualStateManager has setters which adjust the page properties based on the new minimum width.
I have multiple TextBlocks (header,body, etc) and other controls with .Text (ToggleSwitch), that I want to automatically adjust FontSize based on the new width.
Aside from manually setting every single control by name in all the states, is there a way to have the VisualStateManager do it automatically for each 'type' of Text(body/header/etc)? My work around at the moment is to DataBind a Style for every .Text control and have the ViewModel do all the scaling. That works, but this is really something Blend is made for, right? So I must be missing the obvious way everyone is using.
Thanks for all the good comments so far.
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="VisualStateGroup">
<VisualState x:Name="PhonePortrait">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="{StaticResource NarrowMinWidth}"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="SwitchStackPanel.(StackPanel.Orientation)" Value="Vertical"/>
<!-- Setter to change all Body textblocks to FontSize=8-->
<!-- Setter to change all Header textblocks to FontSize=10-->
<!-- Setter to change all ToggleSwitch.Text to FontSize=8-->
<!-- etc..-->
You could use a global style. That utilizes triggers based on the Tag.
<Style TargetType="{x:Type TextBlock}">
<!--Default Setters Here-->
<Setter Property="Background" Value="White"/>
<Style.Triggers>
<Trigger Property="Tag" Value="body">
<Setter Property="Background" Value="Gray"/>
<!--Insert desired state setters here-->
</Trigger>
<Trigger Property="Tag" Value="header">
<Setter Property="Background" Value="DarkGray"/>
<!--Insert desired state setters here-->
</Trigger>
</Style.Triggers>
</Style>
You can then have a default state, and Triggered setters than only affect a Textblock when an applicable tag is attached.
If you already have other styles or want to have a selection process, you may want to add a key and then use BasedOn within the style.

Different template on different ListBoxItem value without DataTemplateSelector?

As title, is it possible?
I have seen in TreeView you can defines different HierarchicalDataTemplate for different datatype using DataType attribute, it doens't even need DataTemplateSelector.
So I wonder if is possible to choose a template according to a binded value without using DataTemplateSelector?
In my condition, is very simple, if the data object's Property = 1, then display template1, 2 then template2.
Is it possible to do it without DataTemplateSelector?
Yes, you can use a DataTrigger
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template" Value="{StaticResource DefaultTemplate}" />
<Style.Triggers>
<DataTrigger Binding="{Binding SomeProperty}" Value="2">
<Setter Property="Template" Value="{StaticResource Template2}" />
</DataTrigger>
</Style.Triggers>
</Style>
I actually prefer DataTriggers to a DataTemplateSelector because they respond to PropertyChange notifications, and I prefer to see my UI logic in my UI code.

WPF ListView image depending on status column

I'd like to show an icon instead of a value in a listview. Basically, the ListView is bind to ModelView-Class (Observable-Collection as a property in that) and has a column called "status". Depending on status value, I'd like to show a different image. What would be the best way to do it. I read about DataTemplate, but I don't know where to hook in the code to switch the image.
MV-Class constructor looks like:
public TaskViewModel()
{
this.TaskCollection = ac.GetAllTasks();
}
Many thanks in advance,
Adam
I'd use a DataTrigger. Here's an example:
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Template" Value="{StaticResource DisabledImageTemplate}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Status}" Value="Active">
<Setter Property="Template" Value="{StaticResource ActiveImageTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
If your images path/name dictated by code behind (this is bad but sometimes happens), you could implement IValueConverter and name it like StatusToIconConverter. See here on MSDN with a simple example.
Regarding Data Templates See at the DataTemplateSelector class. Here is also a very simple example.
The key point is to define simple class which just analizes a passed in value and returns an appropriate data template, obviously you have to declare one data template per image.

How can I apply multiple XAML styles to one element?

I have a toolkit:DataGrid (from Codeplex).
It is starting to have many different styles:
one for switching it on and off based on a variable in the ViewModel
one for visual styling
one for some other triggers, etc.
Do all of these need to be in one big style, is that the only way to do it? Or can I have multiple styles and attach them as I need them? Is there anyway to do this so that you can e.g. swap styles in code?
Styles:
<Style x:Key="CodePlexDataGridSwapper" TargetType="toolkit:DataGrid">
<Style.Triggers>
<DataTrigger Binding="{Binding WhichGrid}" Value="Infragistics">
<Setter Property="toolkit:DataGrid.Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="ToolkitDataGridLayout" TargetType="toolkit:DataGrid">
<Setter Property="Background" Value="Yellow"/>
</Style>
PSEUDO-CODE:
<toolkit:DataGrid
Style="{StaticResource CodePlexDataGridSwapper, ToolkitDataGridLayout}"
ItemsSource="{Binding Customers}"/>
You may want to look at the BasedOn property of the Style class. Basically, it allows you to inherit one style from another. The 'child' style will have all of the setters and triggers from the parent one (unless it has setters overriding the parent's ones) plus it will have obviously its own setter and triggers.
Maybe this info will be helpful for you.

WPF Trigger for IsSelected in a DataTemplate for ListBox items with Blend

I wanted to change the Foreground color when I selected a listboxItem and I did it using this bit of code:
<DataTrigger Binding="{Binding
RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ListBoxItem}},Path=IsSelected}" Value="True">
<Setter TargetName="descriptionTB" Property="Foreground" Value="#000"/>
</DataTrigger>
You can find the answer here.
But if I want a designer to do this in Blend, how would he do it without drilling into xaml?
Thank you
Artur,
The Triggers designer in Expression Blend only allows adding and modifying EventTriggers and Property triggers. I haven't found a way to add DataTriggers with Blend. I'm also not sure how to set RelativeSource Binding using Blend either. I've always handed code the XAML for test and use Blend for everything else.
Maybe I'm misunderstanding the question but can't you just create a style resource for descriptionTB and let the designer only deal with that style definition and not the binding?
<DataTrigger Binding="..">
<Setter TargetName="descriptionTB" Property="Style" Value="{StaticResource DescriptionTextBoxStyle}" />
</DataTrigger>
In the resources section of your control or window you add the style definition:
<Style TargetType="{x:Type TextBox}" x:Key="DescriptionTextBoxStyle">
<Setter Property="Foreground" Value="#000" />
</Style>
If you want to further isolate the designer from the mechanics of the UI you can create a resource dictionary in a separate xaml file in which you can collect all styles meant for the designer. Then you can merge that resource dictionary with your control's or application's main resources.

Resources