XAML separate binding of ContainerStyle and ContentTemplate in ListBox - wpf

I tried this for a while now and searched in the web without success... Now I dare to ask on stackoverflow myself.
So, the aim is to separate the definitions of the ItemContainerStyle and the ContentTemplate of a ListBoxItem. I have defined the ListBoxItemStyle in a ResourceDictionary and two different DataTemplates in the Window.Resources.
I now like to set the style of the ListBoxItem according to the one defined in the ResourceDictionary and change the DataTemplate with a trigger (IsMouseOver).
My (not working) Code so fare looks like this:
<ListBox HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="20,60,10,10"
Grid.Row="1" Grid.Column="0"
PreviewMouseMove="DragMove_PreviewMouseMove"
PreviewMouseLeftButtonDown="Drag_PreviewMouseLeftButtonDown"
ItemsSource="{Binding Persons}" VerticalContentAlignment="Center"
Style="{StaticResource DefaultListBoxStyle}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Style" Value="{StaticResource DefaultListBoxItemStyle}"/>
<Setter Property="ContentTemplate" Value="{StaticResource PersonsListBoxItemTemplate_default}"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource PersonsListBoxItemTemplate_infoButtons}"/>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
where DefaultListBoxStyle is the style defined in the ResourceDictionary and PersonsListBoxItemTemplate_default & PersonsListBoxItemTemplate_infoButtons are the DataTemplates defined in the Window.Resources.
Now I get an error, that the style-object must not have an impact on the style-property of the object it belongs to.
I tried different ways, but without success... I also tried to define the style first and then change the template, but it does not work either.
Thank you all for your help!

So you can't set Style Property in Style with Setter. For this you need to use BasedOn:
<Style TargetType="ListBoxItem" BasedOn="{StaticResource DefaultListBoxItemStyle}">
<Setter ... />
</Style>

Related

Conditional BasedOn in XAML styles

Given:
<Style x:Key="ThirdLevelGroupBoxStyle" TargetType="GroupBox" BasedOn="{StaticResource MetroGroupBox}">
<Setter Property="Background" Value="{DynamicResource AccentColorBrush3}" />
</Style>
<Style x:Key="SecondLevelGroupBoxStyle" TargetType="GroupBox" BasedOn="{StaticResource MetroGroupBox}">
<Setter Property="Background" Value="{DynamicResource AccentColorBrush2}" />
</Style>
<Style TargetType="GroupBox" x:Key="WidgetControlTemplateStyle" BasedOn="{StaticResource ThirdLevelGroupBoxStyle}">
<Style.Triggers>
<DataTrigger Binding="{Binding CanExecuteClickCommand}" Value="True">
<!-- TODO: SecondLevelGroupBoxStyle -->
<Setter Property="Background" Value="{DynamicResource AccentColorBrush2}" />
</DataTrigger>
</Style.Triggers>
</Style>
<ControlTemplate TargetType="ContentControl" x:Key="WidgetControlTemplate">
<GroupBox ... Style="{StaticResource WidgetControlTemplateStyle}">
<ContentPresenter />
</GroupBox>
</ControlTemplate>
ControlTemplate has style WidgetControlTemplate. I want to base (BasedOn) the WidgetControlTemplate style conditionally on ThirdLevelGroupBoxStyle or SecondLevelGroupBoxStyle, in order to avoid XAML duplication. Is there way to achieve this?
If I can't do that, I have to duplicate the definition of SecondLevelGroupBoxStyle.
Is there way to achieve this?
No, not in XAML. The base style to base the style on must be known at design-time.
If I can't do that, I have to duplicate the definition of SecondLevelGroupBoxStyle.
Since you have two different styles that are based on MetroGroupBox, each style that is based on either of this will always be a separate style because you cannot base a single style on more than one style.
What you could do is to use a trigger that sets the Background property of the GroupBox to either AccentColorBrush3 or AccentColorBrush2 instead of trying to inherit this property from another style. It seems like you are already doing this in your WidgetControlTemplateStyle style. And yes, if the ThirdLevelGroupBoxStyle/SecondLevelGroupBoxStyle sets more than one property you will have to set all these in the WidgetControlTemplateStyle as well.
I am afraid there is no way around this unless you merge your two style into one or define them programatically somehow.

Datatrigger on contentpresenter.content not working

I am trying to switch the content of a contentpresenter based on a datatrigger.I want to display a usercontrol in the contentpresenter.content, if i have a value set or else i need to display an error message.But the binding on my datatrigger fails stating that the property is not found.I cant get the datacontext to inherit for the datatrigger checking.I can make it work by using the commented out code.But i am confused why it doesn't work the normal way.
<ContentPresenter.Style>
<Style TargetType="{x:Type ContentPresenter}">
<Setter Property="Content" Value="{Binding UC}"/>
<Style.Triggers>
<!--<DataTrigger Binding="{Binding DataContext.HasValue,RelativeSource={RelativeSource AncestorType={x:Type ContentPresenter}}}" Value="false">
<Setter Property="Content" Value="No preview"/>
</DataTrigger>-->
<DataTrigger Binding="{Binding HasValue}" Value="false">
<Setter Property="Content" Value="No value"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentPresenter.Style>
</ContentPresenter>
If you want to use triggers to display UserControl, you should use ContentControl not ContentPresenter.
I prefer to use ContentPresenter for CustomControls and When I am using the UserControl for views of Custom Data Types in my system and Allow to give dynamic behavior.
Example: To switch templates for ContentPresenter you need to set ContentTemplateSelector like this
<ContentPresenter Content="{Binding MyContent}"
ContentTemplate="{Binding MyContentTemplate}"
ContentTemplateSelector="{Binding MyContentTemplateSelector}"/>
MyContent, MyContentTemplate & MyContentTemplateSelector are Dependency Properties and can be binded wherever you are using its instance.
READ :
Usage of ContentPresenter
What is the difference between ContentControl and ContentPresenter
The binding mentioned in the question won't work as
ContentPresenter’s DataContext is automatically set to the value of
its Content property, while ContentControl’s DataContext is not.
Bindings are resolved relatively to the value of the DataContext property. If you declare a binding on the ContentPresenter, the moment its content is set, the binding would be re-evaluated.
ContentControl.Content Property can be changed on any trigger based on your requirement. If you want to use it to change on PropertyChanged Event of a property of ViewModel, DataTrigger can be used by binding it with a DataTemplate with UserControl instance in it or using static resource of that UserControl.
<ContentControl>
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Setter Value="{StaticResource UnSelectedDataTemplate}" Property="ContentTemplate" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsSelected}" Value="True">
<Setter Value="{StaticResource SelectedDataTemplate}" Property="ContentTemplate" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentContro.Style>
</ContentControl>
READ How to use triggers for content template, more details here
Difference in DataTemplate and StaticResource scope is DataTemplate creates a new instance of the template every time its applied.
Whereas, StaticResource is using the same instance of UserControl again (Static Instance).
You can also use EventTriggers to change content base don Control Events like MouseOver etc.
Alternate approach
Very similar to the above with slight difference. Defining as a data template in resources. Triggering for the content change is essentially identical.
...in <x.Resources /> tag:
<DataTemplate x:Key="DesignerTemplate" DataType="{x:Type vm:SolutionViewModel}">
<vw:SolutionDesignerView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:SolutionViewModel}">
<ContentControl Content="{Binding }">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsLoaded}" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource DesignerTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
...then:
<ContentControl Content="{Binding Solution}" />
I usually use trigger Like this...
<UserControl>
<UserControl.Resources>
<DataTemplate x:Key="normalTemplate" >
<!-Fav UserControl->
</DataTemplate >
<DataTemplate x:Key="overWriteTempalte">
<!-Fav UserControl-> </DataTemplate>
</UserControl.Resources>
<ContentPresenter x:Name="ContentField"
Content="{Binding}"
ContentTemplate="{StaticResource ResourceKey=normalTemplate}" />
<UserControl.Triggers>
<DataTrigger Binding="{Binding Path=MyProperty}" Value="True">
<Setter TargetName="ContentField" Property="ContentTemplate" Value="{StaticResource ResourceKey=overWriteTempalte}" />
</DataTrigger>
</UserControl.Triggers>
</UserControl>
If Bindings are a problem Use Snoop to Detect binding errors

How to define all textblock elements the same color

We are using global styles definitions for most of the types. We define then in the app.xaml file. When using TextBlock it is a problem to define a foreground color because it changes all the controls using TextBlock (Button's content color for example).
How can we define a global style which will act only on specific TextBlock usages?
current problematic usage:
<Style TargetType={x:Type TextBlock}>
<Setter Property="Foreground" Value="Red"/>
</Style>
Since I don't think there is a way to differentiate “your” TextBlocks and those that are part of other controls, your options are quite limited.
You could create named Style and add Style="{StaticResource coloredTextBlock}" or Foreground="{StaticResource textBlockColor}" to all TextBlocks. This would be quite tedious and non-DRY.
You could create your own type that inherits from TextBlock and style that. This has some of the disadvantages of the above solution (you have to remember doing that). But it has much less repetition.
This is because ContentPresenter creates a TextBlock for a string content, and since that TextBlock isn't in the visual tree, it will lookup to Application level resource. And if you define a style for TextBlock at Application level, then it will be applied to these TextBlock within ControlControls.
A workaround is to define a DataTemplate for System.String, where we can explicitly use a default TextBlock to display the content. You can place that DataTemplate in the same dictionary you define the TextBlock style so that this DataTemplate will be applied to whatever ContentPresenter effected by your style.
Add this to your Application resources and it should work for you -
<DataTemplate DataType="{x:Type system:String}">
<TextBlock Text="{Binding}">
<TextBlock.Resources>
<Style TargetType="{x:Type TextBlock}"/>
</TextBlock.Resources>
</TextBlock>
</DataTemplate>
Declare a namespace in your xaml, if not referred already -
xmlns:system="clr-namespace:System;assembly=mscorlib"
EDIT : Check this sample where its working..
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="Red"/>
</Style>
<DataTemplate DataType="{x:Type system:String}">
<TextBlock Text="{Binding}">
<TextBlock.Resources>
<Style TargetType="{x:Type TextBlock}"/>
</TextBlock.Resources>
</TextBlock>
</DataTemplate>
<Style TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="Yellow"/>
</Style>
<Style TargetType="{x:Type Label}">
<Setter Property="Foreground" Value="Blue"/>
</Style>
Just provide a x:key in the style, like:
<Style x:Key="stRedTextBlock" TargetType={x:Type TextBlock}>
<Setter Property="Foreground" Value="Red"/>
</Style>
and mention the key in the TextBlock control style, where ever you require this particular TextBlock style, like:
<TextBlock Name="textBlock1" Style="{StaticResource stRedTextBlock}" />

How do I change the CellErrorStyle for an Xceed Datagrid?

So in the Xceed documentation there is a code example that does not work for me. It may be because my grid is bound to a DataGridCollectionView. The objects in the collection used by the datagridcollection are what implement IDataErrorInfo.
The errors are showing up just fine. The problem is that they are using the default orange background for errors...I need a red border. Below is the XAML instantiation of my grid. I set the DataCell background property to red just so I could be sure I had access to the grid's properties... I do. I just can't find the way to identify the cell's w/ errors so I can style them. Thanks!
<XceedDG:DataGridControl Grid.Row="1" Grid.ColumnSpan="5" ItemsSource="{Binding Path = ABGDataGridCollectionView, UpdateSourceTrigger=PropertyChanged}"
Background="{x:Static Views:DataGridControlBackgroundBrushes.ElementalBlue}" IsDeleteCommandEnabled="True"
FontSize="16" AutoCreateColumns="False" x:Name="EncounterDataGrid" AllowDrop="True">
<XceedDG:DataGridControl.View>
<Views:TableView ColumnStretchMode="All" ShowRowSelectorPane="True"
ColumnStretchMinWidth="100">
<Views:TableView.FixedHeaders>
<DataTemplate>
<XceedDG:InsertionRow Height="40"/>
</DataTemplate>
</Views:TableView.FixedHeaders>
</Views:TableView>
</XceedDG:DataGridControl.View>
<!--Group Header formatting-->
<XceedDG:DataGridControl.Resources>
<Style TargetType="{x:Type XceedDG:GroupByControl}">
<Setter Property="Visibility" Value="Collapsed"/>
</Style>
<Style TargetType="{x:Type XceedDG:DataCell}">
<Setter Property="Background" Value="Red"/>
</Style>
</XceedDG:DataGridControl.Resources>
...
The knowledge base entry:
http://xceed.com/KB/questions.php?questionid=256
Does seem to be potentially missing a critical piece.
Did you try the CellErrorStyle Property on DataGridView?
<Grid xmlns:xcdg="http://schemas.xceed.com/wpf/xaml/datagrid">
<Grid.Resources>
<Style x:Key="errorStyle" TargetType="{x:Type xcdg:DataCell}">
<Setter Property="Foreground" Value="Red"/>
</Style>
</Grid.Resources>
<xcdg:DataGridControl CellErrorStyle="{StaticResource errorStyle}" >
<!--STUFF OMITTED-->
</xcdg:DataGridControl>
</xcdg:DataGridControl>

changing background color of container when textbox is in focus

I have a simple user control with a TextBox. I want to change the color of user control when the TextBox gets the focus. This is what I have:
<UserControl x:Class="OutLookContactList.ContactSearchControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="root" MinHeight="30" Loaded="UserControl_Loaded">
<UserControl.Resources>
<Style x:Key="searchTextBoxStyle" TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="IsFocused" Value="true">
<Setter TargetName="root" Property="Background" Value="{StaticResource OnMouseOverColor}" />
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
But I get the errot "TargetName property cannot be set on a style Setter". How can I Set the back ground color of user control when text box gets the focus?
Thanks a bunch
Will it work to wrap the contents of your UserControl inside a Border object? If so, you can simply style the Border like so:
<UserControl x:Class="Sample2.ContactSearchControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="75" Width="300">
<Border>
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="White" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsFocused, ElementName=txtSearch}" Value="true">
<Setter Property="Background" Value="Black" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<StackPanel>
<TextBox x:Name="txtSearch" Text="Search" />
<TextBox Text="Other" />
</StackPanel>
</Border>
</UserControl>
Update: (Answering Sheraz' Questions)
I'm not sure why ElementName doesn't work for accessing children within a UserControl. It might have something to do with the way the visual tree is constructed.
As for Trigger vs DataTrigger: Trigger is for dependency properties and DataTrigger is for databound properties (data or other controls). Since you are trying to style the Border, it makes more sense to place the DataTrigger there and have it watch the TextBox than to have the TextBox change the appearance of the Border.
As I understand it, the TargetName property of Setter is only applicable within a DataTemplate or ControlTemplate. (Info from Dr. WPF in this forum post)
If you were changing the background of the text box you need to remove the TargetName property:
<Style x:Key="searchTextBoxStyle" TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="IsFocused" Value="true">
<Setter Property="Background" Value="{StaticResource OnMouseOverColor}" />
</Trigger>
</Style.Triggers>
</Style>
and change the TextBox that wants this style to be:
<TextBox Style="{StaticResource searchTextBoxStyle}" .... />
However, as you want to change the value of the parent user control this won't give you want you want.
You could certainly do it in the code behind by adding a GotFocus event handler and putting the code to change the background colour in there.
Here's some XAML that works in Kaxaml:
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Style>
<Style TargetType="Page">
<Setter Property="Background" Value="#CCCCD0" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=txtSearch, Path=IsFocused}"
Value="true">
<Setter Property="Background" Value="Black" />
</DataTrigger>
</Style.Triggers>
</Style>
</Page.Style>
<TextBox x:Name="txtSearch" Width="100"
HorizontalAlignment="Center" VerticalAlignment="Center" />
</Page>
You would change the Page object with your UserControl. I find it much easier to test these sorts of things out in a rapid prototyping tool such as Kaxaml before coding up the UserControl in VS.
Note that you have to set the default colour (in this case #CCCCD0) via a property setter and not via an attribute on the Page itself. This is because the attribute would override the value set by the trigger (because it's a style trigger), so even though the trigger would fire, it would always be trumpted by the local attribute specification, meaning that it wouldn't change. I only point this out because it's a fairly common gotcha.

Resources