How can I apply multiple XAML styles to one element? - wpf

I have a toolkit:DataGrid (from Codeplex).
It is starting to have many different styles:
one for switching it on and off based on a variable in the ViewModel
one for visual styling
one for some other triggers, etc.
Do all of these need to be in one big style, is that the only way to do it? Or can I have multiple styles and attach them as I need them? Is there anyway to do this so that you can e.g. swap styles in code?
Styles:
<Style x:Key="CodePlexDataGridSwapper" TargetType="toolkit:DataGrid">
<Style.Triggers>
<DataTrigger Binding="{Binding WhichGrid}" Value="Infragistics">
<Setter Property="toolkit:DataGrid.Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="ToolkitDataGridLayout" TargetType="toolkit:DataGrid">
<Setter Property="Background" Value="Yellow"/>
</Style>
PSEUDO-CODE:
<toolkit:DataGrid
Style="{StaticResource CodePlexDataGridSwapper, ToolkitDataGridLayout}"
ItemsSource="{Binding Customers}"/>

You may want to look at the BasedOn property of the Style class. Basically, it allows you to inherit one style from another. The 'child' style will have all of the setters and triggers from the parent one (unless it has setters overriding the parent's ones) plus it will have obviously its own setter and triggers.
Maybe this info will be helpful for you.

Related

Specify a binding in an application-level style resource?

I'm fairly new to WPF and have created a style to alter the appearance of a button control. The style contains a data trigger to change the button background (amongst other things) based on a boolean property in the data context, e.g.:-
<Style x:Key="IndicatorButton" TargetType="Button">
<DataTrigger Binding="{Binding Path=ValveIsOpen}" Value="True">
<Setter Property="Background" Value="#00FF00"/>
..etc..
Currently the style is only used by a single button, so the data trigger binding is hard-coded with a property called "ValveIsOpen".
I now want to re-use this style throughout my app, with different buttons being bound to different properties. How would I change the data trigger binding on each button that the style is applied to?
Many thanks
You need to define a base style and derived styles, such as
<Style x:Key="IndicatorButton" TargetType="Button">
<Setter Property="Foreground" .../>
...
<Style x:Key="ValveIndicatorButton" TargetType="Button" BasedOn={StaticResource IndicatorButton}>
<DataTrigger Binding="{Binding Path=ValveIsOpen}" Value="True">
<Setter Property="Background" Value="#00FF00"/>
..etc..

How to change the control template effectively in WPF?

The subject is that I've defined a customized control bound to a DataContext. And I wished to change the control template dynamically according to the DataContext's specific property values.
There're 2 ways I've thought of,but I have no idea about which way is better.
1.Do not use the control template in a ResourceDictionary and all details of the control are defined in C# code.Use the DependencyProperty CallBack method to render the control when DataContext's property values change.
2.Define control template in the ResourceDictionary and use DataTrigger to change the 'Control.Template' property.
In my application,thousands of instances in this type would be created,so it's really unacceptable if the ControlTemplate changging is not effective.
Could you please give me some advices or better solutions?
Using any standard WPF technique might not be effective if it would involve a thousands of instances of complex controls. See http://msdn.microsoft.com/en-us/magazine/dd483292.aspx.
I would go with MultiBinding + IMultiValueConverter to Control.Template dependency property, since Template would depend on multiple DataContext properties and would, perhaps, involve complex logic.
Perhaps you could used a ContentPresenter in your ControlTemplate to customize parts of your control. You could provide DataTemplates for those customizable parts which are automatically applied.
I would use a style with the data triggers to control which template is displayed. Like this example:
<Style x:Key="Die1Face" TargetType="{x:Type Button}">
<Setter Property="Template" Value="{StaticResource dieNone}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=ThrowDie1[0]}" Value="1" >
<Setter Property="Template" Value="{StaticResource dieOneA}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=ThrowDie1[0]}" Value="2" >
<Setter Property="Template" Value="{StaticResource dieTwoA}" />
</DataTrigger>
</Style.Triggers>
</Style>
This would give the flexibility you need.

Style Trigger to Apply another Style

I am sure this has been asked before, but I haven't had an easy time figuring out how to phrase the query.
I have this style;
<SolidColorBrush x:Key="SemiTransparentRedBrushKey">#F0FF0000</SolidColorBrush>
<Style x:Key="TextBoxEmptyError" TargetType="{x:Type TextBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Text.Length}" Value="0">
<Setter Property="BorderBrush" Value="{StaticResource ResourceKey=SemiTransparentRedBrushKey}"/>
</DataTrigger>
</Style.Triggers>
</Style>
That I can apply to Textboxes to have a red border when they are empty. Its great, I can just add Style="{StaticResource TextBoxEmptyError}" to the Control Tag. But what if I want to apply this style with a trigger, so that the control only used it under certain conditions (like a binding being true)? Something like:
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=ApprovedRequired}" Value="True">
<Setter Property="Style" Value="{StaticResource TextBoxEmptyError}"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
This code throws an exception though {"Style object is not allowed to affect the Style property of the object to which it applies."}
Can something like this be done?
Edit: If this cannot be done with a Style trigger because it would overwrite itself, is there another way to Conditionally apply a resource style?
Edit: I can change the question title if there is a more proper term for this action.
Styles cannot be set from a Setter within the Style, because then essentially the first Style would never exist at all.
Since you're looking for a Validation style, I would recommend looking into Validation.ErrorTemplate, although if that doesn't work you can change your trigger so it modifies specific properties such as BorderBrush instead of the Style property
i would think of using a Template with a TemplateTrigger and there you can change the style to what ever you like based on what ever condition

Using BasedOn Style Property on DynamicResources

i wonder if there is a way to use the basedOn property of wpf styles with dynamicresources. e.g.
<Style BasedOn="{DynamicResource somestyle}">
<Setter Property="SomeProp" Value="SomeValue"/>
</Style>
this e.g. throws an error indicating that the usage of dynamicresources in combination with BasedOn styles is not possible.
i wonder how someone could do that?
thanks
I think the main reason is sealed objects. If you have a Style hierarchy:
Style A
/ \
Style A1 Style A2
this might not be a difficult scenario. You refer to StyleA using a dynamic resource, so whenever that resource changes, Style A1 and Style A2 should change their BasedOn property. However, once a Style is being used in your application, it becomes a sealed object. Style A becomes immutable.
One workaround you can use is:
Style A needs to change.
Create a new Style object that is going to be the new Style A resource.
Create a new version of Style A1 and Style A2. You'd need to write a copy procedure that makes copies of all the Setters, Resources, etc. Set the BasedOn to the new version of Style A.
Update the resources collection so that the three new styles are in there.
{DynamicResource StyleA1} and {DynamicResource StyleA2} should now pick up the fact that those resources changes (from step 4) and update any references automatically.
Note that this is a very simple scenario. Real world style hierarchies can be more complex, especially if they are spread across multiple files and come from merged dictionaries.
Hope I understood your problem and helped out.
I've found that since you can't use BasedOn on a DynamicResource, you can "convert" the DynamicResource to StaticResource by merging the ResourceDictionary holding your "parent" resources to your current Window/UserControl/whatever. This way you are now able to refer to the resource object (eg. Style) using StaticResource. This way you can use Datatriggers on DynamicResource (through conversion).
Example:
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/MyProject.Styles;component/ButtonStyles.xaml"/>
</ResourceDictionary.MergedDictionaries>
[*Your other resources can be put here*]
</ResourceDictionary>
</Window.Resources>
...
<Button Command="{Binding MyCommandInViewModel, RelativeSource={RelativeSource AncestorType=Window}}">
<Button.Style>
<Style BasedOn="{StaticResource StyleFromButtonStyles}" TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding SomeBool}" Value="True">
<Setter Property="Button.Content" Value="{StaticResource SomeImage}"/>
</DataTrigger>
<DataTrigger Binding="{Binding SomeBool}" Value="False">
<Setter Property="Button.Content" Value="{StaticResource SomeOtherImage}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Hence Datatriggers are applied to a button styled in an imported ResourceDictionary.
Hope this helps!

WPF Trigger for IsSelected in a DataTemplate for ListBox items with Blend

I wanted to change the Foreground color when I selected a listboxItem and I did it using this bit of code:
<DataTrigger Binding="{Binding
RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ListBoxItem}},Path=IsSelected}" Value="True">
<Setter TargetName="descriptionTB" Property="Foreground" Value="#000"/>
</DataTrigger>
You can find the answer here.
But if I want a designer to do this in Blend, how would he do it without drilling into xaml?
Thank you
Artur,
The Triggers designer in Expression Blend only allows adding and modifying EventTriggers and Property triggers. I haven't found a way to add DataTriggers with Blend. I'm also not sure how to set RelativeSource Binding using Blend either. I've always handed code the XAML for test and use Blend for everything else.
Maybe I'm misunderstanding the question but can't you just create a style resource for descriptionTB and let the designer only deal with that style definition and not the binding?
<DataTrigger Binding="..">
<Setter TargetName="descriptionTB" Property="Style" Value="{StaticResource DescriptionTextBoxStyle}" />
</DataTrigger>
In the resources section of your control or window you add the style definition:
<Style TargetType="{x:Type TextBox}" x:Key="DescriptionTextBoxStyle">
<Setter Property="Foreground" Value="#000" />
</Style>
If you want to further isolate the designer from the mechanics of the UI you can create a resource dictionary in a separate xaml file in which you can collect all styles meant for the designer. Then you can merge that resource dictionary with your control's or application's main resources.

Resources