WPF XAML - Adding multiple styles to resource dictionary which target rowdefinition - wpf

Thanks for your thoughts in helping a relative beginner understand WPF.
I am trying to use the following style template in an XAML file for a WPF application:
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<Style TargetType="{x:Type RowDefinition}"
x:Key="hideIfNotDischarged">
<Style.Triggers>
<DataTrigger Binding="{Binding DischargedBy28Days,
Mode=OneWay}" Value="false">
<Setter Property="Height" Value="0" />
</DataTrigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type RowDefinition}"
x:Key="hideIfOutcomeKnownAndAlive">
<Style.Triggers>
<DataTrigger Binding="{Binding IsKnownDead,
Mode=OneWay}" Value="false">
<Setter Property="Height" Value="0" />
</DataTrigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
Which will be later used in a grid like so:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="30" />
<RowDefinition Style="{StaticResource hideIfNotDischarged}" />
...
However, if there is more than 1 style element targeting Type RowDefinition AND ALSO the ResourceDictionary is nested within a MergedDictionary (even with only a single child ResourceDictionary to merge) the application fails with
System.Windows.ResourceDictionary.DeferrableContent: Item has already been added.
That is, despite the fact the 2 styles have different keys, the resource dictionary is trying to add a dictionary item with name based purely on the target type (and ignoring the key).
Any help with how I might overcome this would be most appreciated.

Xaml is parsed left to right so I think its checking the ResourceDictionary or the other MergedDictionaries if the TargetType (default identifier if no x:Key is used) exists before it realizes it has an x:Key identifier
Try setting the x:Key before the TargetType.
this could be a bug in the Xaml parser or it could be by design, It may be worth a look on Microsoft Connect

Related

Why style targettype has to be rebinded back to the original properties?

I am referring to the code block here, on Data triggers
<Window x:Class="WpfTutorialSamples.Styles.StyleDataTriggerSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="StyleDataTriggerSample" Height="200" Width="200">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<CheckBox Name="cbSample" Content="Hello, world?" />
<TextBlock HorizontalAlignment="Center" Margin="0,20,0,0" FontSize="48">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="No" />
<Setter Property="Foreground" Value="Red" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=cbSample, Path=IsChecked}" Value="True">
<Setter Property="Text" Value="Yes!" />
<Setter Property="Foreground" Value="Green" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
</Window>
To me, from programming language design point of view, the line <Style TargetType="TextBlock"> is completely unnecessary, because it is already nested inside the <TextBlock>, so of course whatever setter property should be applied to the TextBlock type. So why need the line <Style TargetType="TextBlock">?
Can <Style TargetType> be of other type except TextBlock?
So why need the line ?
A Style may be defined as a resource, i.e. not inline, and if doesn't have a TargetType specified, how is the XAML parser supposed to be able to parse it and set the properties specified by the setters? It can't. Or at least it doesn't.
Just because you can define a Style inline you are still creating an instance of the exact same class that may be used as a (global) resource and then setting a TargetType is indeed required.
Can be of other type except TextBlock?
No, apart from a type that is derived from TextBlock. If you specify another type you will get an exception at runtime when the BAML (the compiled XAML) is parsed.
You could use any class TextBlock derives from (for example FrameworkElement).
If you implement your own CustomizedTextBlock for example you are able to use styles defined for TextBlock in your project.
You find an example for this here.

MenuItem icon returns collection

When I have a following code, it all works fine:
<Image x:Key="Icons" x:Shared="false"
Source="{Binding Path=Icon}" Height="16px" Width="16px"/>
<Style x:Key="MenuItemStyle" TargetType="MenuItem" >
<Setter Property="MenuItem.ItemsSource" Value="{Binding Children}"/>
<Setter Property="MenuItem.Header" Value="{Binding Text}"/>
<Setter Property="MenuItem.IsEnabled" Value="{Binding IsEnabled}"/>
<Setter Property="MenuItem.Icon" Value="{StaticResource Icons}"/>
</Style>
But I have to load xaml from external source and WPF has a bug that does not allow x:Shared= in that case. So I made Resource dictionary with x:Name=Icons that is compiled internally (Action is database table that has string property Icon and there is path to the Icon):
<Image x:Key="Icons" x:Shared="False"
Source="{Binding Path=Action.Icon}" Height="16px" Width="16px"/>
and the code now looks
<ResourceDictionary x:Key="IkoniceDict" Source="/MVVM_App;component/View/iconimage.xaml"/>
<Style x:Key="MenuItemStyle" TargetType="MenuItem">
<Setter Property="MenuItem.Icon" Value="{Binding Source={StaticResource Icons}}"/>
Problem is that now I get (collection) instead of image:
Need help, please!
You must not specify a key when loading a resource dictionary into the control.
<ResourceDictionary Source="/MVVM_App;component/View/iconimage.xaml"/>
Edit
I guess I'm not clear enough. The above only works if you have nothing but this in that single <SomeControl.Resources> tag.
<SomeControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionary>
<ResourceDictionary Source="/MVVM_App;component/View/iconimage.xaml"/>
</ResourceDictionary.MergedDictionary>
</ResourceDictionary>
<Style x:Key="MyStyleKey" .............>
</Style>
</SomeControl.Resources>
The Resources is a ResourceDictionary type. All entries must have an x:Key. If you assign an x:Key to an ResourceDictionary inside Resources tag, you are putting a dictionary inside dictionary. That is why you need to use ResourceDictionary.MergedDictionary to tell the XAML parser to merge the contents of iconimage.xaml in.
I think that you should set the Path property for your Binding (set it to "Source"):
<Style x:Key="MenuItemStyle" TargetType="MenuItem">
<Setter Property="MenuItem.Icon" Value="{Binding Path=Source, Source={StaticResource Icons}}"/>
When the path is not specified, the default is to bind to the entire object (in your case the collection of icons).
Msdn Binding

How to set a ResourceDictionary to a ContentControl.Content?

In my application, I have a ContentControl and this shows the Content property using DataTemplates.
Right now, I need to pass this DictionaryResource to the content property. So I did this (I'm not sure)
<ContentControl Content="{Binding CurrentViewModel">
<ContentControl.Resources>
<ResourceDictionary Source="/MathematicsBusiness.Infrastructure;component/Resources/ThemeResources.xaml" />
</ContentControl.Resources>
</ContentControl>
And this contain my dictionary:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="40" />
<Setter Property="FontFamily" Value="Georgia" />
</Style>
<!--<Style x:Key="TextBlockStyle" TargetType="TextBlock">
<Setter Property="FontSize" Value="40" />
<Setter Property="FontFamily" Value="Georgia" />
</Style>-->
</ResourceDictionary>
And it works, all the data templates show the textblocks with that style. But if I use the commented style, it does not work. It throws me an error:
Cannot find a Resource with the Name/Key TextBlockStyle
Why is happening that? If the style does not have a Key, it works. But if I set a key, it doesn't work.
If you specify a key without using it, it should not be a problem. My guess is that you are calling TextBlockStyle somewhere else (maybe your visual state or your code).
The error you are having usually happens if you are trying to use the key, but you haven't specified it in your xaml.
Other possible cause is that you are trying to use the key outisde the ContentControl.

Setting an Elements.Resource Style using BasedOn within a Resource Dictionary

I have a Resource Dictionary that I am using to define the look-and-feel (style) for my application.
I have just created another Resource Dictionary that contains DataTemplates that I am using on several different screens (and even multiple times within the same screen) to display my business objects.
I would like to change some of the default styles within my DataTemplates so that the controls fit better; however I would like the controls to inherit the same style as the rest of the screen. So, naturally I want to use the BasedOn property for this task.
The problem that I am having is that I'm not sure what to set the BasedOn property to.
For example, in the resource dictionary that contains my styles (called "myStyle.xaml") I have:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:primatives="clr-namespace:System.Windows.Controls.Primitives;assembly=PresentationFramework"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="{x:Type Label}">
<Setter Property="Foreground" Value="#F5F5F5" />
<Setter Property="FontSize" Value="12"></Setter>
<Setter Property="Width" Value="120"></Setter>
<Setter Property="FontFamily" Value="Arial"></Setter>
</Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="FontSize" Value="12"></Setter>
<Setter Property="Width" Value="120"></Setter>
<Setter Property="Height" Value="25"></Setter>
<Setter Property="Background" Value="Black"></Setter>
</Style>
<!-- .... and so on .... -->
</ResourceDictionary>
I am using this resource in the following window:
<Window x:Class="SiteSetupWindow4"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:primatives="clr-namespace:System.Windows.Controls.Primitives;assembly=PresentationFramework"
Title="A Screen">
<Window.Resources>
<ResourceDictionary x:Key="defaultStyleX">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary x:Name="DefaultStyles" Source="Resources/myStyle.xaml" />
<ResourceDictionary x:Name="Templates" Source="Resources/myTemplates.xaml"></ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
Now, I have another Resource Dictionary that contains DataTemplates that I am using within my window. It is called "myTemplates". The style is applied to the DataTemplate as expected; however, I would like to overwrite some aspects of the style within the DataTemplate (Like width for example).
This is what I have tired, however I cannot get the BasedOn property to work...
(myTemplate.xaml)
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DataTemplate x:Key="PanelInfo">
<StackPanel>
<StackPanel.Resources>
<Style TargetType="TextBox" BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="Width" Value="120" />
</Style>
<Style TargetType="Label">
<Setter Property="Width" Value="180" />
</Style>
<Style TargetType="ComboBox">
<Setter Property="Width" Value="120" />
</Style>
<StackPanel.Resources>
<StackPanel Orientation="Horizontal">
<Label Content="Type:"></Label>
<ComboBox>
<ComboBoxItem Content="{Binding Path=Type}" IsSelected="True"></ComboBoxItem>
</ComboBox>
<!--...and so on -->
</StackPanel>
</StackPanel>
</ResourceDictionary>
This fails....I have also tried using DynamicResource, but this also fails.
I'm not sure how to get around this.
Any advise would be greatly appreciated!
Thanks,
-Frinny
I was having the same problem with an extended Button Style.
The ResourceKey= is what solved it for me.
This worked:
<Style x:Name="ButtonVisibility"
TargetType="{x:Type Button}"
BasedOn="{StaticResource ResourceKey={x:Type Button}}">
The way you have BasedOn for a type is correct. This will work in theory as long as, at run time, the style that you are basing it on is merged into the tree correctly. Make sure you have the "myStyles.xaml" merged in correctly. You can check this by removing your style you tried to modify and make sure it displays correctly from your style in "myStyles.xaml."
If it isn't there are a lot of places you can go wrong, but it always helps to try merging the styles in the file you are working on, then work up the tree to see where it's missing.
This utility will help you look at what is happing in the tree at run time.
http://blois.us/Snoop/

WPF datagrid styling

I want to style a WPF datagrid and it seems to be really easy . As far as I understand I have to have code such as the following:
<Style x:Key="DataGridColumnHeaderStyle" TargetType="{x:Type Custom:DataGridColumnHeader}" >
<Setter Property="Background" Value="#88800080" />
<Setter Property="Foreground" Value="White" />
</Style>
But my question is ..where do I place this code and how do I let the datagrid know to use the style above ?
Regards,
S
Put it in the resource of the xaml (local or global). The easiest is to put it in the local resource of the current xaml file:
<Page Name="SomeName"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Resources>
<Style x:Key="DataGridColumnHeaderStyle" TargetType="{x:Type Custom:DataGridColumnHeader}" >
<Setter Property="Background" Value="#88800080" />
<Setter Property="Foreground" Value="White" />
</Style>
</Page.Resources>
<!-- The rest of the xaml -->
</Page>
The best place to put styles is in a resource dictionary, referenced in App.xaml.
Resource dictionary ("StyleResources.xaml" in this example):
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="TextBlockRightAlign" TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Right" />
</Style>
<Style x:Key="TextBlockTitle" TargetType="TextBlock">
<Setter Property="FontSize" Value="20" />
<Setter Property="FontWeight" Value="Bold" />
</Style>
</ResourceDictionary>
Referencing the style dictionary in App.xaml:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="StyleResources.xaml"/>
</ResourceDictionary.MergedDictionaries>
<ValueConverters:PriceConverter x:Key="PriceConverter"/>
</ResourceDictionary>
</Application.Resources>
Using the definition in a datagrid (column formatting here, but should work for headers as well):
<data:DataGridTextColumn Header="Charge" Width="100"
Binding="{Binding Charge, Mode=TwoWay, Converter={StaticResource PriceConverter}}"
ElementStyle="{StaticResource TextBlockRightAlign}" />
Note that the element inside the cell is a TextBlock, so you can use a style with a target type of TextBlock.
As for the "Type DataGridColumnHeader was not found": you need a second xml namespace entry since the DataGridColumnHeader is in the System.Windows.Controls.Primitives namespace. You need something like
xmlns:dg="clr-namespace:Microsoft.Windows.Controls.Primitives;assembly=WPFToolkit"
and then reference the new namespace in your style, e.g.
<Style x:Key="DataGridColumnHeaderStyle" TargetType="{x:Type dg:DataGridColumnHeader}" >
Styles usually go:
<UserControl.Resources>
<Style x:Key="DataGridColumnHeaderStyle" TargetType="{x:Type Custom:DataGridColumnHeader}" >
<Setter Property="Background" Value="#88800080" />
<Setter Property="Foreground" Value="White" />
</Style>
</UserControl.Resources>
Use the appropriate container if this isn't within a UserControl you may use "Window" or whatever container you're in.
Also you need to reference it in your datagrid with:
<Custom:DataGrid ColumnHeaderStyle="{StaticResource DataGridColumnHeaderStyle}"/>

Resources