How To Extend A DataTemplate to Include an Additional Control - c#

So I've got a style, which does some fancy things to a wpf TabControl, among which it sets a HeaderTemplate that allows users to rename the tab. I've got a style which is based on this template that colors the text based on some parameter, and I would like to add a polygon to the header of each tab depending on some conditions.
So the base style looks like:
<Style TargetType="TabItem" x:Key="EditableTabItemStyle"
BasedOn="{StaticResource BaseTabItemStyle}">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<controls:EditableTabContent Template="{StaticResource EditableTabItemTemplate}"
Content="{Binding Path=., Mode=TwoWay}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
and my derived looks like:
<Style x:Key="DerivedTabItem"
BasedOn="{StaticResource EditableTabItemStyle}"
TargetType="TabItem">
<Setter Property="Foreground">
<Setter.Value>
<MultiBinding Converter="{StaticResource StateToColorConverter}">
<Binding Path="States.State1" />
<Binding Path="States.State2" />
</MultiBinding>
</Setter.Value>
</Setter>
I'm looking to add a polygon to the derived style, preferably without changing the base style at all, so my initial thought was to add a header template:
<Setter Property="HeaderTemplate">
<Setter.Value>
<!-- Old header template -->
<!-- Polygon -->
</Setter.Value>
</Setter>
But that overrides the editable styling. I'm not sure if what I want is even possible, so I'll take that for an answer if need be, but if it can be done, how can I do it?

You can't amend an existing template. You can either:
Copy it and add anything you like.
Modify the original template to include another control that's only displayed under certain conditions (e.g. using a DataTrigger or a binding to its Visibility property).
This control could be something very flexible, such as a ContentPresenter that could display all sorts of content by binding it to some placeholder property.
In this case, where the template is so simple, I'd go with the former.

Related

Dynamic content of a Grid

My data model has a property of the enumeration type. I wonder if there is way to place dynamically a user control based on the value of the enumeration type?
I am currently investigating in the following direction:
<Grid Name ="AdjustmentsArea" DockPanel.Dock ="Right" MinWidth ="100" Visibility ="Collapsed" >
<ContentControl DataContext ="{Binding AjustmentView}">
<Style TargetType ="model:AjustmentViews">
<Style.Triggers>
<DataTrigger Binding ="{Binding}" Value ="Settings">
/// is it possible in principle to point a user control using a Setter ???
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl>
</Grid>
May be also I am on a wrong path. But I would like to know (learn) if it is possible to implement this requirement for dynamic content in user control, but not using hide/show exised element approach.
What would you recommend?
you can set different template depending on trigger binding value
<ContentControl DataContext ="{Binding AjustmentView}">
<ContentControl.Style>
<Style TargetType ="ContentControl">
<Style.Triggers>
<DataTrigger Binding="{Binding}" Value ="Settings">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate> <!--template with UserControl here--> </ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
e.g. WPF Slider uses this approach when Orientation changes (Horizontal or Vertical)

WPF using style that contains another style

I'm trying to make a progress bar style that is re-usable.
So the idea here is very simple. I have a style already with target type ProgressBar, and it's just a spinny circle that fills as it goes from 0-100%. However, in order to make it re-usable and modular, I do not want to hard-code the text that goes along with it - it should be optional.
So I want to create another style that DOES include text "Downloading... X/Y MB". For this I take Value for X, Maximum for Y, and Tag for the unit. I want to include the same spinny circle thingy for the graphical part on the left. How can I do this? With BasedOn property, I think you can only set something already there to be different. What if I want to add additional elements (like textblocks in this case)?
If only text value differentiates then within style you can bind propertie's value to parent's property like Tag where at every single either style or element you adjust it to specific requirement.
<Window.Resources>
<Style x:Key="FirstButtonStyle" TargetType="Button">
<Setter Property="Content" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<ContentPresenter/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="SecondButtonStyle" TargetType="Button" BasedOn="{StaticResource FirstButtonStyle}">
<Setter Property="Content" Value="2"/>
</Style>
</Window.Resources>
<StackPanel>
<Button Style="{StaticResource FirstButtonStyle}"/>
<Button Style="{StaticResource SecondButtonStyle}"/>
</StackPanel>
The outcome is 1 and 2. If your intent is to inject some UI element within style then there is no such an option, alas. Style needs to be rewritten once again.

How to switch between views using DataTemplate + Triggers

I have a requirement where a where user can switch to view hierarchical data either as tree or as a text in datagrid or as FlowChart.
The user can do this by clicking a Toggle Button which say: Switch Mode. I want to do all this in such a way that it can be handled within the View only as ViewModel in all the three cases is the same.
How do I apply View to my ViewModel based on Trigger.
If the state of which view to show is saved in some enum property you could use a ContentControl and DataTriggers for example:
<ContentControl>
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding ViewMode}" Value="TreeMode">
<Setter Property="Content">
<Setter.Value>
<uc:TreeModeView />
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding ViewMode}" Value="GridMode">
<Setter Property="Content">
<Setter.Value>
<uc:GridModeView />
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
(As the style is only used in one place, by setting it directly as ContentControl.Style this will work, if you want to use it in more than one place you should set the ContentTemplate instead, because otherwise there will only be one view instance shared by all controls with the style which is not allowed by WPF (of course Content needs to be set to something for the template to be applied))
You could also bind directly to IsChecked of the ToggleButton using ElementName of course. The relevant values would then be True, False and {x:Null}.
H.B.'s answer is good, but there are scenarios where it's not quite so good.
If constructing the views (and their underlying view models) is expensive, then toggling the Content property will pay this expense every time the user changes the view.
In some scenarios, it makes sense to create both views in the same container (e.g. a Grid), and toggle their Visibility instead. If you use lazy evaluation in your view models, the expensive operations won't be conducted until the view becomes visible, and - importantly - they'll only be conducted the first time the view becomes visible. Once both views have been displayed, the user can toggle back and forth between views without reconstructing the underlying view models.
Edit:
I stand corrected, sort of: H.B.'s answer is not as good as it looked .
You can't use a style to set the Content property of a ContentControl to a UIElement. See this blog post for full details, but the long and short of it is that if you use H.B.'s approach, you'll get a runtime error.
You can set the ContentTemplate property, instead, e.g.:
<Style TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding ViewMode}"
Value="TreeMode">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<uc:TreeModeView/>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding ViewMode}"
Value="GridMode">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<uc:GridModeView/>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>

WPF: TextTrimming on a ContentPresenter

Is there a simple way to just get TextTrimming to work with a ContentPresenter?
I have implict styles for TextBlock and AccessText that have TextTrimming set to CharacterEllipsis, but it's not picked up by the ContentPresenter. I can change the ContentPresenter to an AccessText or TextBlock and set it there, but then the template only handles text content.
Any suggestions?
Thanks!
Implicit Styles for elements that derive from UIElement, but not Control, are not applied if the element is defined in a control's Template unless the implict Style is defined in the application Resources. The same holds true for TextBlocks used by ContentPresenter.
For example, in the following XAML the TextBlock that is ultimately used to present the button's content will not get the implicit Style:
<Window.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Red" />
</Style>
</Window.Resources>
<StackPanel>
<Button Content="Will not be red" />
<TextBlock Text="Will be red" />
</StackPanel>
If you take that exact same Style and move it to the application's Resources, then both will be red:
<Application.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Red" />
</Style>
</Application.Resources>
So you can either move your implicit Style to application resources, which is generally not a good idea. Or you can customize the display for the specific scenario you have. This can include adding an implicit DataTemplate, or customizing a control's Template.
If you can provide more information, then it would be easier to know which is the best approach.
Thanks to this Gist by James Nugent: "WPF style which puts character ellipsis on button contents without replacing the ContentPresenter with a TextBlock and thus losing the ability to support access keys."
This worked for me:
<ContentPresenter.Resources>
<Style TargetType="TextBlock">
<Setter Property="TextTrimming" Value="CharacterEllipsis"></Setter>
</Style>
</ContentPresenter.Resources>

Setting a style on Control type not working

I'm writing a very basic WPF dialog and want to apply a simple style to all objects that inherit from the Control class. The code I'm using:
<Window.Resources>
<Style TargetType="{x:Type Control}">
<Setter Property="Margin" Value="20"/>
</Style>
</Window.Resources>
<StackPanel>
<TextBlock Text="some text"/>
<TextBox x:Name="x_NameTextBox"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button x:Name="x_CancelButton" Click="x_CancelButton_Click" Content="Cancel"/>
<Button x:Name="x_OkButton" Click="x_OkButton_Click" Content="OK"/>
</StackPanel>
</StackPanel>
</Window>
The Style defined above doesn't change the layout of the window at all unless I specify a key and set the style on each individual object, which is exactly what I'm trying to avoid. It also works for more specific types (setting the TargetType to Button, for example.)
Any ideas why this isn't working?
Every control when it gets instantiated it gets its Style from the explicitly defined resource or look for the immediate parent where it can get a default style. In your case the Button control will get its default Style from the platform because your App haven't defined one. Now that platform Button Style has no way to know about your custom defined Control base style. Because styles will look for a base style only when you explicitly define BasedOn
So you got only two ways
1. Define Style for every control - which you don't want I think.
2. Define Styles for the controls you are interested and set the BasedOn
<Style TargetType="{x:Type Control}">
<Setter Property="Margin" Value="20"/>
</Style>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Control}}">
</Style>

Categories