Switch data template based on value of bound data in Silverlight / WPF - wpf

say I am using WPF or Silverlight and binding a ContentPresenter to an integer property:
<ContentPresenter Content={Binding Score} />
and if the score is 10 I want to display a gold star, and otherwise just display the number. So essentially I have two possible data templates:
<Path Fill="Gold" Data="..." />
<TextBlock Text="{Binding Score}" />
What is the best way to set this up? Is it to use a Binding Converter? Or bind to a different object that dynamically loads the appropriate data template xaml and makes the correct FrameworkElement depending on the value of Score? Or is there another trick I am missing - perhaps ContentPresenter isn't the right control to be using?
I wondered if you could do something like this, but it doesn't like the nested binding within the ContentTemplate value:
<StackPanel>
<StackPanel.Resources>
<DataTemplate x:Key="LowScore">
<TextBlock Text="{Binding Path=Score}" Foreground="Red" />
</DataTemplate>
<DataTemplate x:Key="HighScore">
<Path Fill="Gold" Data="M 0,0 l 10,0 l 5,-10 l 5,10 l 10,0 l -7,10 l 2,10 l -10,-5 l -10,5 l 2,-10 Z" />
</DataTemplate>
</StackPanel.Resources>
<ContentPresenter Content="{Binding Score}" ContentTemplate="{StaticResource ResourceKey={Binding ScoreTemplate}}">
</ContentPresenter>
</StackPanel>

you could use a template selector. Here is a nice tutorial on Switch On The Code. Basically, a template selector allows you to select the template for an item based on whatever conditions you want.

Possible solutions:
Create a DataTemplate with a StackPanel containing both control types and bind their Visibility (or use a DataTrigger) so that only one is visible at a time. This is fairly simple and can be good if there are not many states or the differences are small.
Use a DataTemplateSelector and look up the DataTemplate by resource.

Related

WPF TextBox in DataTemplate of ToggleButton does not show text if in toolbar flyout

If I put the Column where the toolbar is hosted to be very big (800) then all the text is visible:
but if I put a smaller column this happens:
But I cannot understand why:
<DataTemplate x:Key="IconFilterButton">
<StackPanel Orientation="Horizontal">
<TextBlock
VerticalAlignment="Center"
Style="{StaticResource LargeIconStyle}"
Text="{Binding}" />
<TextBlock
Margin="6,0,0,0"
VerticalAlignment="Center"
DataContext="{Binding}"
Style="{StaticResource BodyTextStyle}"
Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ToggleButton}, Path=Tag}" />
</StackPanel>
</DataTemplate>
and here the definition
<ToggleButton
x:Name="DFilter"
Click="Filtering_Click"
Content=""
ContentTemplate="{StaticResource IconFilterButton}"
Tag="1d"
/>
<ToggleButton
x:Name="WFilter"
Click="Filtering_Click"
Content=""
ContentTemplate="{StaticResource IconFilterButton}"
Tag="1w"
/>
Even worst if I click on the button once they are out:
and then the text is visible but is wrong as the TextBlock is not considered in the object size:
The WPF ToolBar control uses a custom panel for the overflow Popup. In many styles, the ToolBarOverFlowPanel has a property WrapWidth set to a static value like 200. This determines how many items can be displayed before it wraps to another row in the popup.
I've created custom styling for this control and have found that the ToolBarOverFlowPanel used internally is buggy. That's probably the source of your problem.
You can re-template the ToolBar and wire-up a different value for WrapWidth to try to fix the issue, but my guess is that you'll still run into layout problems.
Otherwise, you might consider making your own replacement control.

How to change from binding ObervableCollection to a TabControl to a ContentControl

I'm very new to WPF and am writing an application using this example as a starting point
http://msdn.microsoft.com/en-us/magazine/dd419663.aspx#id0090025
I will only have one workspace visible at any one time, so I want to get rid of the TabControl and use something simple instead - probably a ContentControl, I'm really not sure but all it needs to do is have content in and be closable. So I am trying to replace this:
<DataTemplate x:Key="WorkspacesTemplate"><TabControl
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource ClosableTabItemTemplate}"
Margin="4"
/>
with:
<DataTemplate x:Key="WorkspacesTemplate">
<ContentControl Content="{Binding ??}" ContentTemplate="{StaticResource ClosableTabItemTemplate}"/>
</DataTemplate>
but I don't know what to bind to. The code in the example seems to use CollectionViewSource to set the active workspace - it's the active workspace that I am interested in but I don't understand what TabControl is doing except that it's something to do with IsSynchronizedWithCurrentItem="True"
The template is invoked from here (Workspaces is the ObservableCollection of ViewModels):
<HeaderedContentControl Content="{Binding Path=Workspaces}" ContentTemplate="{StaticResource WorkspacesTemplate}" Header="Workspaces" Style="{StaticResource MainHCCStyle}"/>
and here is the ClosableItem template:
<DataTemplate x:Key="ClosableTabItemTemplate">
<DockPanel Width="120">
<Button
Command="{Binding Path=CloseCommand}"
Content="X"
Cursor="Hand"
DockPanel.Dock="Right"
Focusable="False"
FontFamily="Courier"
FontSize="9"
FontWeight="Bold"
Margin="0,1,0,0"
Padding="0"
VerticalContentAlignment="Bottom"
Width="16" Height="16"
/>
<ContentPresenter
Content="{Binding Path=DisplayName}"
VerticalAlignment="Center"
/>
</DockPanel>
</DataTemplate>
Please can someone explain what I need to do? Thanks
The WorkspacesTemplate is telling WPF how to display the Workspaces property, which, as you say is an ObservableCollection of ViewModels.
So, the WorkspacesTemplate says, display all these ViewModels in a tab control, and for each ViewModel, use the ClosableTabItemTemplate to display the ViewModel in a tab.
Since you only want one workspace visible at a time, you don't need to expose a collection of workspaces from your ViewModel, and you don't need a tab control to display them. You simply expose the one current workspace from your ViewModel and provide some XAML to display it.
If you still want to use a template to wrap the ViewModel, then yes, you can just use a ContentControl to invoke the template:
<DataTemplate x:Key="MySingleWorkspaceTemplate">
<TextBlock Text={Binding Blah} />
<!-- etc -->
</DataTemplate>
and to invoke the template
<ContentControl Content="{Binding CurrentWorkspace}" ContentTemplate="{StaticResource MySingleWorkspaceTemplate}"/>
However, if this is the only place that the XAML is going to be used, you might as well forget the template and just declare the XAML directly. Eg, (instead of ContentControl)
<TextBlock Text={Binding CurrentWorkspace.Blah} />
<!-- etc -->
EDITED TO ADD:
I think you might be getting confused because currently the ViewModel has no concept of the "Selected Workspace", it just exposes a collection. For completeness (but don't worry about all this), the selection is introduced by the TabControl which indirectly uses the default CollectionView for the Workspaces collection, and CollectionView has the concept of a selected item. This is all in the view.
I wouldn't worry about any of this now, just expose the one workspace yourself from your ViewModel.
EDIT2:
Your close button is appearing because you are explicitly setting a ContentTemplate on your HeaderedContentControl. This template will appear regardless of Content.
To make a template only appear when there is data in Content, make the template implicit instead. If you add a DataType to your template definition (and remove the key), you tell WPF to always use this template to display an object of that data type.
<DataTemplate DataType="{x:Type vm:WorkspaceViewModel}">
<!-- Blah -->
</DataTemplate>
Then you can remove the explicit template from your HeaderedContentControl. Simply setting the Content will be enough to invoke the template, and if there is no Content, there is no template.
<HeaderedContentControl Content="{Binding Path=CurrentWorkspace}" />
(ps. If you're not using the header of HeaderedContentControl, you might as well just use a bog standard ContentControl)

Is there something in WPF similar to Style.BasedOn for DataTemplate?

At the moment, I have two very large DataTemplate objects to display two sets of items in two ListBoxes. The DataTemplates are referenced in the ContentTemplate property in two Styles that are set in the ItemContainerStyle properties of the two ListBoxes. The items are of the same type and the DataTemplates are identical except for the following control:
From DataTemplate1
<TextBlock Style="{StaticResource TextStyle}" FontSize="20" Foreground="White"
HorizontalAlignment="Left" Panel.ZIndex="2" Text="{Binding RemainingTime.TotalHours,
Converter={StaticResource DoubleToIntegerConverter}, StringFormat={}{0:#00}}" />
From DataTemplate2
<TextBlock Style="{StaticResource TextStyle}" FontSize="20" Foreground="White"
HorizontalAlignment="Left" Panel.ZIndex="2" Text="{Binding ElapsedTime.TotalHours,
Converter={StaticResource DoubleToIntegerConverter}, StringFormat={}{0:#00}}" />
Is there some way to avoid duplicating the whole Dataemplate but still have this one difference in the text binding of this TextBlock in the second template?
No, there is no inheritance for DataTemplate. If you think about, how would you override a part of a DataTemplate?
Solution: Use another Style to capture the common properties between the two templates. You can scope it in the same Resources block if it only place you need it. It is much cleaner or more WPF way of doing things.
I've already asked this question here once and unfortunately there isn't.
but in this specific situation you can move the fontsize,foreground,horizontalalignment..etc to a style (lets say textstyle2) that based on your current textstyle.
I got an answer to this from another post (by Liz). Basically, you can put all common controls into one DataTemplate and then create two more DataTemplates that each use the first one as a ContentTemplate in a ContentPresenter. Then, you can add different controls into one or both of the latter DataTemplates. Liz provided a code example.
<DataTemplate x:Key="UserTemplate">
<!-- show all the properties of the user class here -->
</DataTemplate>
<DataTemplate DataType="{x:Type local:User}">
<ContentPresenter Content="{Binding}" ContentTemplate="{StaticResource UserTemplate}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Author}">
<StackPanel>
<ContentPresenter Content="{Binding}" ContentTemplate="{StaticResource UserTemplate}"/>
<!-- show all the additional Author properties here -->
</StackPanel>
</DataTemplate>
Thanks once again Liz.
Adding to what Dennis suggested, you can always create a custom control that you just stick inside your DataTemplate and re-style that control instead of the DataTemplate.

How does inheritance of FontSize in silverlight tree work?

If I have a ChildWindow in Silverlight I can apply the FontSizeProperty and it is inherited by child items.
<controls:ChildWindow FontSize="14">
<StackPanel>
<TextBlock Content="Hello">
<TextBlock Content="World">
</StackPanel>
</controls:ChildWindow>
Now that's fine if you want the whole page to have the same font size, but I want to do something like this and have inheritance in smaller blocks:
<controls:ChildWindow>
<StackPanel FontSize="14">
<TextBlock Content="Hello">
<TextBlock Content="World">
</StackPanel>
<StackPanel FontSize="10">
<TextBlock Content="Hello">
<TextBlock Content="World">
</StackPanel>
</controls:ChildWindow>
This doesn't compile. Is there any way i can achieve this pattern in Silverlight without having to define a style for StackPanel (I think that would work).
Are there any other containers that let me set FontSize for its descendants - or can I write one that would?
I want to easily set fontsize to be larger for certain StackPanels. I don't want to resort to styles because its very specialized and I won't need to reuse the style elsewhere.
Whats the best way to do this?
You can wrap each StackPanel in a ContentControl, which does implement FontSize.

Difference between Control Template and DataTemplate in WPF

What is difference between a ControlTemplate and a DataTemplate in WPF?
Typically a control is rendered for its own sake, and doesn't reflect underlying data. For example, a Button wouldn't be bound to a business object - it's there purely so it can be clicked on. A ContentControl or ListBox, however, generally appear so that they can present data for the user.
A DataTemplate, therefore, is used to provide visual structure for underlying data, while a ControlTemplate has nothing to do with underlying data and simply provides visual layout for the control itself.
A ControlTemplate will generally only contain TemplateBinding expressions, binding back to the properties on the control itself, while a DataTemplate will contain standard Binding expressions, binding to the properties of its DataContext (the business/domain object or view model).
Very basically a ControlTemplate describes how to display a Control while a DataTemplate describes how to display Data.
For example:
A Label is a control and will include a ControlTemplate which says the Label should be displayed using a Border around some Content (a DataTemplate or another Control).
A Customer class is Data and will be displayed using a DataTemplate which could say to display the Customer type as a StackPanel containing two TextBlocks one showing the Name and the other displaying the phone number. It might be helpful to note that all classes are displayed using DataTemplates, you will just usually use the default template which is a TextBlock with the Text property set to the result of the Object's ToString method.
Troels Larsen has a good explanation on MSDN forum
<Window x:Class="WpfApplication7.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate x:Key="ButtonContentTemplate">
<StackPanel Orientation="Horizontal">
<Grid Height="8" Width="8">
<Path HorizontalAlignment="Stretch"
Margin="0,0,1.8,1.8"
VerticalAlignment="Stretch" Stretch="Fill" Stroke="#FF000000"
Data="M0.5,5.7 L0.5,0.5 L5.7,0.5"/>
<Path HorizontalAlignment="Stretch"
Margin="2,3,0,0"
VerticalAlignment="Stretch" Stretch="Fill" Stroke="#FFFFFFFF"
Data="M3.2,7.5 L7.5,7.5 L7.5,3.5"/>
<Path HorizontalAlignment="Stretch"
Margin="1.2,1.4,0.7,0.7"
VerticalAlignment="Stretch" Fill="#FFFFFFFF" Stretch="Fill" Stroke="#FF000000"
Data="M2.5,2.5 L7.5,7.5"/>
<Path HorizontalAlignment="Stretch"
Margin="1.7,2.0,1,1"
VerticalAlignment="Stretch" Stretch="Fill" Stroke="#FF000000"
Data="M3,7.5 L7.5,7.5 L7.5,3.5"/>
<Path HorizontalAlignment="Stretch"
Margin="1,1,1,1"
VerticalAlignment="Stretch" Stretch="Fill" Stroke="#FFFFFFFF"
Data="M1.5,6.5 L1.5,1 L6.5,1.5"/>
</Grid>
<ContentPresenter Content="{Binding}"/>
</StackPanel>
</DataTemplate>
<ControlTemplate TargetType="Button" x:Key="ButtonControlTemplate">
<Grid>
<Ellipse Fill="{TemplateBinding Background}"/>
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Window.Resources>
<StackPanel>
<Button Template="{StaticResource ButtonControlTemplate}" ContentTemplate="{StaticResource ButtonContentTemplate}" Content="1"/>
<Button Template="{StaticResource ButtonControlTemplate}" ContentTemplate="{StaticResource ButtonContentTemplate}" Content="2"/>
<Button Template="{StaticResource ButtonControlTemplate}" ContentTemplate="{StaticResource ButtonContentTemplate}" Content="3"/>
</StackPanel>
</Window>
(Templates blatently stolen from
http://msdn.microsoft.com/en-us/library/system.windows.controls.controltemplate.aspx
and
http://msdn.microsoft.com/en-us/library/system.windows.controls.contentcontrol.contenttemplate%28VS.95%29.aspx
respectively)
Anyway, the ControlTemplate decides how the Button itself looks, while
the ContentTemplate decides how the Content of the button looks. So
you could bind the content to one of you data classes and have it
present itself however you wanted it.
ControlTemplate: Represents control style.
DataTemplate: Represents data style(How would you like to show your data).
All controls are using default control template that you can override through template property.
For example
Button template is a control template.
Button content template is a data template
<Button VerticalAlignment="Top" >
<Button.Template>
<ControlTemplate >
<Grid>
<Rectangle Fill="Blue" RadiusX="20" RadiusY="20"/>
<Ellipse Fill="Red" />
<ContentPresenter Content="{Binding}">
<ContentPresenter.ContentTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Height="50">
<TextBlock Text="Name" Margin="5"/>
<TextBox Text="{Binding UserName, Mode=TwoWay}" Margin="5" Width="100"/>
<Button Content="Show Name" Click="OnClickShowName" />
</StackPanel>
</DataTemplate>
</ContentPresenter.ContentTemplate>
</ContentPresenter>
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
public String UserName
{
get { return userName; }
set
{
userName = value;
this.NotifyPropertyChanged("UserName");
}
}
ControlTemplate - Changing the appearance of element. For example Button can contain image and text
DataTemplate - Representing the underlying data using the elements.
ControlTemplate DEFINES the visual appearance, DataTemplate REPLACES the visual appearance of a data item.
Example: I want to show a button from rectangular to circle form => Control Template.
And if you have complex objects to the control, it just calls and shows ToString(), with DataTemplate you can get various members and display and change their values of the data object.
All of the above answers are great but there is a key difference that was missed. That helps make better decisions about when to use what. It is ItemTemplate property:
DataTemplate is used for elements that provide ItemTemplate property for you to replace its items' content using DataTemplates you define previously according to bound data through a selector that you provide.
But if your control does not provide this luxury for you then you still can use a ContentView that can display its content from predefined ControlTemplate. Interestingly, you can change the ControlTemplate property of your ContentView at runtime. One more thing to note that unlike controls with ItemTemplate property, you cannot have a TemplateSelector for this (ContentView) control. However, you still can create triggers to change the ControlTemplate at runtime.

Resources