Setting background of content control - wpf

<UserControl .....>
<DataTemplate DataType="{x:Type vm:AViewModel}">
<vw:AView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:BViewModel}">
<vw:BView />
</DataTemplate>
<ContentControl x:Name="chartScreen" Content="{Binding Screen}" Background="Yellow" />
</UserControl>
As you can see from above code, ContentControl is setting its content through binding to ViewModel's Screen property. Screen property will return an instance of AViewModel or BViewModel, depending on some condition. The problem is, when UserControl is loaded on screen, Screen property is null, so there is no content set yet. At this point, I would like to set some background for the ContentControl, but I cannot find a way how to do this? Background="Yellow" does nothing...
Any ideas how to set the background of ContentControl? This backgound should be applied always, even when content is displaying AView or Biew, or null.

Just wrap your ContentControl in a Border
<Border Background="Yellow">
<ContentControl x:Name="chartScreen"
Content="{Binding Screen}" />
</Border>
If all you have in your UserControl is your ContentControl, just set the Background on the UserControl itself. That would remove the extra Border as well.

try something like this :
<ContentControl x:Name="chartScreen" Content="{Binding Screen}" Background="Yellow">
<ContentControl.Triggers>
<Trigger Property="Content" Value="{x:Null}">
<Trigger.Value>
<Border Background="Yellow"/>
</Trigger.Value>
</Trigger>
</ContentControl.Triggers>
</ContentControl>

try something like this in WPF:
<ContentControl>
<ContentControl.Style>
<Style TargetType="ContentControl">
<Style.Triggers>
<DataTrigger Binding="{Binding Content}" Value="{x:Null}">
<Setter Property="Content">
<Setter.Value>
<Rectangle Width="100" Height="100" Fill="Blue" />
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>

Related

Handling different item templates that share the same content

I have two custom ItemTemplates for the ListBox, one for the regular items, and one for the selected item. An example of how would I handle this is:
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="ContentTemplate" Value="{StaticResource Template1}" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource Template2}" />
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
Template1 and Template2 are very similar:
<DataTemplate x:Key="Template1">
<SameContent />
<DifferentContent1 />
</DataTemplate>
<DataTemplate x:Key="Template2">
<SameContent />
<DifferentContent2 />
</DataTemplate>
So, is it a proper way to duplicate the code for the SameContent (which is like a bunch of TextBlocks, Panels, etc) in both templates, or it is a better approach to have only one template, but switch the DifferentContent based on the IsSelected property, or...?
if second approach, how would it be properly done?
Obviously duplicating the code is not a very good solution. A better approach is to define another DataTemplate as your common content and then use ContentPresenter to present it:
<Window.Resources>
<DataTemplate x:Key="CommonTemplate">
<TextBlock Text="{Binding CommonProperty1}" />
<TextBlock Text="{Binding CommonProperty2}" />
</DataTemplate>
<DataTemplate x:Key="Template1" >
<StackPanel>
<ContentPresenter ContentTemplate="{StaticResource CommonTemplate}"/>
<TextBlock Text="{Binding Template1Property1}"/>
<TextBlock Text="{Binding Template1Property2}"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="Template2" >
<StackPanel>
<ContentPresenter ContentTemplate="{StaticResource CommonTemplate}"/>
<TextBlock Text="{Binding Template2Property1}"/>
<TextBlock Text="{Binding Template2Property2}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>

Need multiple styles dependent on Listboxitem

i have a Listbox, which stores two different object types, based on the same baseclass. (e.g. BaseObject = baseclass and the children of it: CustomPath and CustomImage)
The Datasource:
ObservableCollection<BattlegroundBaseObject> _baseObjectCollection;
public ObservableCollection<BattlegroundBaseObject> BaseObjectCollection
{
get { return _baseObjectCollection?? (_baseObjectCollection= new ObservableCollection<BaseObject>()); }
}
The Listbox databinding: <ListBox ItemsSource="{Binding BaseObjectCollection}"
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem" x:Name="ListBoxPathLineStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem" x:Name="BattlegroundObjectControlTemplate">
<Path Stroke="{Binding ObjectColor}" StrokeThickness="{Binding StrokeThickness}" Data="{Binding PathGeometryData}" x:Name="PathLine" Opacity="{Binding Opacity}">
</Path>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Effect" TargetName="PathLine">
<Setter.Value>
<DropShadowEffect Color="CornflowerBlue" ShadowDepth="3" BlurRadius="10" />
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
I want to add to the ControlTemplate where the Path is, also a Image and to differ it by type or a property. doesnt matter.
anyone any ideas?
You can add to ListBox resources DataTemplate for each type.
In my example classes Car and Motorbike derived from Vehicle class.
<ListBox x:Name="listBox">
<ListBox.Resources>
<DataTemplate DataType="{x:Type local:Car}">
<StackPanel Background="Red">
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Motorbike}">
<StackPanel Background="Orange">
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ListBox.Resources>
</ListBox>
EDIT:
You can add style for ListBoxItem to resources:
<ListBox x:Name="listBox">
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<Style.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect Color="CornflowerBlue" ShadowDepth="3" BlurRadius="10" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
<DataTemplate DataType="{x:Type local:Car}">
<StackPanel Background="Red">
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Motorbike}">
<StackPanel Background="Orange">
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ListBox.Resources>
</ListBox>
You could define some DataTemplates for your different classes. They determine how the classes are displayed. I've been using them to display derived classes differently when working with a collection of the base class.
<DataTemplate DataType="{x:Type CustomPath}">
<TextBlock Text="This is a CustomPath"/>
</DataTemplate>
<DataTemplate DataType="{x:Type CustomImage}">
<TextBlock Text="This is a CustomImage"/>
</DataTemplate>
At the moment you are changing the style of the controls that WPF is using to render your bound data. A better way to do this is to provide WPF with a way of generating the correct controls. Ignore the ListBoxItem and use DataTemplates for your actual objects.
First you need to tell the Window or control how to find your types.
<Window or UserControl
...
xmlns:model="clr-namespace:yourNamespace"
>
Then you can provide WPF with a way to show your objects e.g.
<DataTemplate TargetType="{x:Type model:CustomPath}">
<Path Stroke="{Binding ObjectColor}" StrokeThickness="{Binding StrokeThickness}"
Data="{Binding PathGeometryData}" x:Name="PathLine" Opacity="{Binding Opacity}"/>
<!-- maybe use a binding from the Path.Effect back to the IsSelected and ValueConverters
to re-apply the selection effect-->
</DataTemplate>
<DataTemplate TargetType="{x:Type model:CustomImage}">
<Image Src="{Binding SomeProperty}" />
</DataTemplate>
Now all you need to do is to make these available to the ListBox in some way. Almost every element in WPF can have .Resources added to it, so you could choose to do these across the entire window
<Window ...>
<Window.Resources>
<DataTemplate .../>
<DataTemplate .../>
</Window.Resources>
...
<ListBox .../>
</Window>
or you can apply it more locally
<Window ...>
...
<ListBox>
<ListBox.Resources>
<DataTemplate .../>
<DataTemplate .../>
</ListBox.Resources>
</ListBox>
</Window>
And this way your listbox definition can become much neater too, e.g. if you are using Window.Resources
<ListBox ItemsSource="{Binding BaseObjectCollection}"/>

Change listbox template on lost focus event in WPF

I want to present a listbox with TextBlocks as items. When the user clicks/selects an item it changes into a TextBox for editing. As soon as the controls loses focus the item would turn back to a TextBlock.
The following XAML is almost working in that the TextBlock does turn into a TextBox when it's selected. It also turns back to a TextBlock if I select another item on the list. The problem is that if I move out of the listbox (in this case to the Add New text box) the list item stays as a TextBox.
The question ( WPF ListViewItem lost focus event - How to get at the event? ) looked promising but I can't make it work. I tried using the IsFocused property but then I wasn't able to edit the textbox because when I got into the textbox the listboxitem would go out of focus and thus turning back to a TextBlock before I had a chance to edit the text.
How can I make the TextBox turn back to TextBlock if the listbox/item loses the focus?
(Any other implementation that accomplishes the goal are also welcomed)
<Window x:Class="MyView.MainWindow1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
Title="MainWindow1" Height="300" Width="200">
<Window.Resources>
<DataTemplate x:Key="ItemTemplate">
<TextBlock Text="{Binding Name}" />
</DataTemplate>
<DataTemplate x:Key="SelectedTemplate">
<TextBox Text="{Binding Name, Mode=TwoWay}" />
</DataTemplate>
<Style TargetType="{x:Type ListBoxItem}" x:Key="ContainerStyle">
<Setter Property="ContentTemplate" Value="{StaticResource ItemTemplate}" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource SelectedTemplate}" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel >
<ListBox ItemsSource="{Binding Departments}" HorizontalContentAlignment="Stretch"
ItemContainerStyle="{StaticResource ContainerStyle}" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox Text="{Binding NewDepartmentName, Mode=TwoWay}" />
<Button Grid.Column="1" Width="50" Content="Add" Command="{Binding Path=AddNewDepartmentCommand}" />
</Grid>
</StackPanel>
</Window>
This doesn't answer your question, but gives an alternative solution by making a textbox look like a textblock when the listboxitem isn't selected:
<Page.Resources>
<ResourceDictionary>
<Style x:Key="ListBoxSelectableTextBox" TargetType="{x:Type TextBox}">
<Setter Property="IsHitTestVisible" Value="False" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}, AncestorLevel=1}}" Value="True">
<Setter Property="IsHitTestVisible" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</Page.Resources>
<Grid>
<ListBox ItemsSource="{Binding Departments}" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Margin="5" Style="{StaticResource ListBoxSelectableTextBox}" Text="{Binding Name}" BorderBrush="{x:Null}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
There are many ways you can do this:
Above answer
Create a TextBox style based on TextBoxBase such that when disabled, it makes the TextBox look like a TextBlock by setting the BorderThickness="0", Background="transparent"
Have both TextBlock and TextBox on each ListViewItem and using the above answer technique hide and show the TextBlock/TextBox accrodingly

HeaderTemplate with multiple items

I'm trying to write a HeaderTemplate for an extender. So far, I've noticed all the examples use the {Binding} keyword to get the data from the header. However, what happens if there are multiple controls within the Header? How do I specify that those controls should be inserted at a specific location?
<Window.Resources>
<Style x:Key="ExpanderStyle" TargetType="{x:Type Expander}">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<!-- what do I put in here? -->
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Expander Style="{StaticResource ExpanderStyle}">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock>Some Text</TextBlock>
<TextBlock Text="{Binding SomeBinding}" />
<Button />
</StackPanel>
</Expander.Header>
<Image Source="https://www.google.com/logos/2012/steno12-hp.jpg" />
</Expander>
Should I be moving my binding into the HeaderTemplate in the style and just overwriting whatever the Header in the Expander is?
You can use ContentPresenter to insert whatever the usual content would be into your Template
For example:
<Style x:Key="ExpanderStyle" TargetType="{x:Type Expander}">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<Border BorderBrush="Blue" BorderThickness="2">
<ContentPresenter />
</Border>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
Header's property Content could contain only one object.
If you merge these object in one panel:
<StackPanel>
<TextBlock>Some Text</TextBlock>
<TextBlock Text="{Binding SomeBinding}" />
<Button />
</StackPanel>
then in template you could use {binding}

getting validation into a style

I use IDataErrorInfo on my viewmodels and I have a style (below) for a TextBox with an error template that works ok. I know that "ValidatesOnDataErrors=True" used like:
<TextBox Text="{Binding Path=LastName, ValidatesOnDataErrors=True}"
Style="{StaticResource TextBoxStyle}" />
will force WPF to use IDataErrorInfo but am wondering how to get that baked into my style.
Cheers,
Berryl
<Style x:Key="TextBoxStyle" TargetType="{x:Type TextBox}">
...
<!--
Error handling
-->
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Right" Text=" *"
Foreground="Red"
FontWeight="Bold" FontSize="16"
ToolTip="{Binding ElementName=placeholder, Path=AdornedElement.(Validation.Errors).CurrentItem.ErrorContent}"/>
<Border BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder Name="placeholder"></AdornedElementPlaceholder>
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="Background" Value="LightYellow"/>
</Trigger>
</Style.Triggers>
</Style>
If I understand what you're asking, you want to be able to be able to use the ValidatesOnDataError=True in your style, that so you don't have to repeat it every time.
If that's the case you can't, because that is a property of the data binding and not the style; and you can't template the data binding.
I just wonder if you use a Label instead of a TextBox, then in the style of the Label you can probably do something like this,
<ControlTemplate TargetType="sdk:Label">
<TextBlock x:Name="textBlock" Text="{Binding Content, RelativeSource={RelativeSource TemplatedParent}, ValidatesOnDataErrors=True}">
You can't because that is part of the definition of the binding to your property, not how the error is visualized.

Resources