Can I get this binding into a style? - wpf

I would like to take the xaml I currently have for a ComboBox (below), and condense it into something like the Style also shown below. I think this should work, but I have a 'Type'ing issue and not sure how to resolve it
"Cannot resolve the Style Property 'Margin'. Verify that the owning type is the Style's TargetType, or use Class.Property syntax to specify the Property.)
As I look at the existing ComboBoxStyle (also below) that I'm looking to base this new style off of, I see that I hadn't used x:Type, but it does seem to work.
Is there any reason this new style shouldn't work? What must I change?
Cheers,
Berryl
combo box, as is, working):
<ComboBox
x:Name="cboDepartmentFilter" Style="{StaticResource ComboBoxStyle}"
Margin="{StaticResource FliterPanelItem_Margin}" Width="{StaticResource FilterPanelItem_Width}"
ItemsSource="{Binding Path=DepartmentFilterControl.Choices}"
ToolTip="{Binding DepartmentFilterControlData.ToolTipTitle}"
/>
what I want:
<ComboBox Style="{StaticResource FilterPanelComboBoxStyle}" DataContext="{Binding DepartmentFilterControl}" />
<!- in some resource file ->
<Style x:Key="FilterPanelComboBoxStyle" BasedOn="{StaticResource ComboBoxStyle}">
<Setter Property="Margin" Value="{StaticResource FliterPanelItem_Margin}" />
<Setter Property="Width" Value="{StaticResource FilterPanelItem_Width}" />
<Setter Property="ItemsSource" Value="{Binding Choices}" />
<Setter Property="ToolTip" Value="{Binding ToolTipTitle}" />
</Style>
<!--
This style defines a common margin for items in a filter panel.
-->
150
existing ComboBoxStyle:
<!-- ComboBox Style -->
<Style x:Key="ComboBoxStyle" TargetType="ComboBox">
<Setter Property="Background" Value="{StaticResource headerBrush}" />
...
<Setter Property="Template">
<Setter.Value>
...
</Setter.Value>
</Setter>
<Setter Property="ItemContainerStyle" Value="{StaticResource ComboBoxItemStyle}" />
<Setter Property="IsSynchronizedWithCurrentItem" Value="True" />
</Style>

You still need to specify the TargetType in the derived style. (Or you prefix the properties with "ComboBox.")

Related

GroupSizeDefinition is not working with style defined for ribbon group

I have a main list which is a ribbon group.
Inside each ribbon group there is another list called subfuctions for ribbon buttons.
<DataTemplate x:Key="buttonTempl">
<RibbonButton IsEnabled="{Binding IsEnabled}" Label="{Binding Path=SubFunctionName}" Command="{Binding SubFunctionCommand}" LargeImageSource="{Binding LargeButtonImage}" SmallImageSource="{Binding SmallButtonImage}"/>
</DataTemplate>
<RibbonGroupSizeDefinitionBaseCollection x:Key="groupSize">
<RibbonGroupSizeDefinition IsCollapsed="False" />
</RibbonGroupSizeDefinitionBaseCollection>
<Style TargetType="RibbonGroup" x:Key="groupStyle">
<Setter Property="GroupSizeDefinitions" Value="{StaticResource groupSize}"/>
<Setter Property="Header" Value="{Binding DisplayName}"/>
<Setter Property="ItemsSource" Value="{Binding SubFunctions}"/>
<Setter Property="ItemTemplate" Value="{StaticResource buttonTempl}"/>
<Setter Property="SmallImageSource" Value="{Binding RibbonGroupSmallImageSource}"/>
<Setter Property="Margin" Value="0"/>
</Style>
<Style TargetType="RibbonTab" x:Key="tabStyle">
<Setter Property="Header" Value="Pump"/>
<Setter Property="ItemsSource" Value="{Binding MainFunctions}"/>
<Setter Property="ItemContainerStyle" Value="{StaticResource groupStyle}"/>
</Style>
Hence groupSizedefinition is working if I use ribbongroup as separate as below:
<RibbonGroup Header="Selection" Name="SelectionMenu" GroupSizeDefinitions="{StaticResource groupSize}">
but not working with the above code that is with template.
What could be done to work groupsizedefinition with ribbongroup defined as style?
This is not really an answer, but it is too much for a comment.
I tried to reproduce this problem and observed the following effects using the "Live Visual Tree" and the "Live Property Explorer" windows in the debugger.
Specifying the GroupSizeDefinitions explicitly
<RibbonGroup Style="{StaticResource groupStyle}" GroupSizeDefinitions="{StaticResource groupSize}" />
the GroupSizeDefinitions in the style is crossed out, and the local definition is used
Specifying the GroupSizeDefinitions in the styls
<RibbonGroup Style="{StaticResource groupStyle}"/>
The Live property explorer shows a Local definition, which in my opinion is not present, is also crossed out.
The GroupSizeDefinitions which are actually being used are under the heading Coercion.
I'm afraid that I can't explain that, but maybe somebody else can.

Attached behaviors and styles

I use an attached behavior that allows a DoubleClick event to be wired to a command in a view model, as in the binding below:
<ListBox Style="{StaticResource MasterListBoxStyle}"
b:SelectionBehavior.DoubleClickCommand="{Binding EditCommand}"
>
I need multiple list boxes for a presentation, all of which will need a DoubleClick wired to an EditCommand.
Can I push this behavior into my MasterListBoxStyle? How?
Cheers,
Berryl
<Style x:Key="MasterListBoxStyle" TargetType="ListBox">
<Setter Property="ItemsSource" Value="{Binding MasterVm.AllDetailVms}" />
<Setter Property="ItemContainerStyle" Value="{StaticResource MasterListingRowStyle}" />
<Setter Property="IsSynchronizedWithCurrentItem" Value="True" />
<Setter Property="AlternationCount" Value="2" />
</Style>
You should be able to add a simple Setter like so in WPF:
<Setter Property="b:SelectionBehavior.DoubleClickCommand" Value="{Binding EditCommand}" />
Assuming the b xmlns is defined in the XAML file that contains your Style.
This won't work in Silverlight though, since Bindings are not supported in Setters. This is something Microsoft is fixing in Silverlight 5.

Underline Label in WPF, using styles

I have the following style:
<Style x:Key="ActionLabelStyle" TargetType="{x:Type Label}">
<Setter Property="Margin" Value="10,3" />
<Setter Property="Padding" Value="0" />
<Setter Property="TextBlock.TextWrapping" Value="Wrap" />
<Setter Property="FontFamily" Value="Calibri" />
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True" />
<Condition Property="IsEnabled" Value="True" />
</MultiTrigger.Conditions>
<Setter Property="Background" Value="Red" />
<Setter Property="TextBlock.TextDecorations" Value="Underline" />
</MultiTrigger>
</Style.Triggers>
</Style>
So basically, I want to have a label which is underlined when it is enabled and the mouse cursor is over it. The part of this style which is not working is the <Setter Property="TextBlock.TextDecorations" Value="Underline" />. Now, what am I doing wrong here? Thanks for all the help.
This is actually much more difficult than it appears. In WPF, a Label is not a TextBlock. It derives from ContentControl and can therefore host other, non-text controls in its Content collection.
However, you can specify a string as the content as in the example below. Internally, a TextBlock will be constructed to host the text for you.
<Label Content="Test!"/>
This internally translates to:
<Label>
<Label.Content>
<TextBlock>
Test!
</TextBlock>
</Label.Content>
</Label>
The simple solution to this would be for the TextDecorations property of a TextBlock to be an attached property. For example, FontSize is designed this way, so the following works:
<Label TextBlock.FontSize="24">
<Label.Content>
<TextBlock>
Test!
</TextBlock>
</Label.Content>
</Label>
The TextBlock.FontSize attached property can be applied anywhere in the visual tree and will override the default value for that property on any TextBlock descendant in the tree. However, the TextDecorations property is not designed this way.
This leaves you with at least a few options.
Use color, border, cursor, etc., instead of underlined text because this is 100% easier to implement.
Change the way you are doing this to apply the Style to the TextBlock instead.
Go to the trouble to create your own attached property and the control template to respect it.
Do something like the following to nest the style for TextBlocks that appear as children of your style:
FYI, this is the ugliest thing I've done in WPF so far, but it works!
<Style x:Key="ActionLabelStyle" TargetType="{x:Type Label}">
<Setter Property="Margin" Value="10,3" />
<Setter Property="Padding" Value="0" />
<Setter Property="TextBlock.TextWrapping" Value="Wrap" />
<Setter Property="FontFamily" Value="Calibri" />
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True" />
<Condition Property="IsEnabled" Value="True" />
</MultiTrigger.Conditions>
<Setter Property="Background" Value="Red" />
</MultiTrigger>
</Style.Triggers>
<Style.Resources>
<Style TargetType="TextBlock">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Label}, Path=IsMouseOver}" Value="True" />
<Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsEnabled}" Value="True" />
</MultiDataTrigger.Conditions>
<Setter Property="TextDecorations" Value="Underline"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Style.Resources>
</Style>
This works because it is overriding the default style of any TextBlock beneath a Label of this style. It then uses a MultiDataTrigger to allow relative binding back up to the Label to check if its IsMouseOver property is True. Yuck.
Edit:
Note that this only works if you explicitly create the TextBlock. I was incorrect when I posted this because I had already dirtied up my test Label. Boo. Thanks, Anvaka, for pointing this out.
<Label Style="{StaticResource ActionLabelStyle}">
<TextBlock>Test!</TextBlock>
</Label>
This works, but if you have to go to this trouble, you're just working too hard. Either someone will post something more clever, or as you said, my option 1 is looking pretty good right now.
Further to Jerry's answer, in order to avoid having to add the TextBlock into the template each time you can let the style do this too by adding the Setter property into the style:
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Label}">
<TextBlock>
<ContentPresenter />
</TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
Then your Label is back to:
<Label Content="Test!" Style="{StaticResource ActionLabelStyle}" />
Thanks Jerry!
Andrew.
I think the issue is that TextBlock.TextDecorations is not defined on Label.
You can use this approach if you're happy to use a TextBlock rather than a Label.
Just to add my workaround to the mix. I am currently using C# code and it works well enough. I just trap the MouseLeave and MouseEnter events and show the underline there.
void Control_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
{
WPFHelper.EnumerateChildren<TextBlock>(this, true).ForEach(c => c.TextDecorations = null);
}
void Control_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
{
WPFHelper.EnumerateChildren<TextBlock>(this, true).ForEach(c => c.TextDecorations = TextDecorations.Underline);
}
The WPFHelper class simply enumerates all the children of an DependencyObject and ForEach is an extension method that just does executes the action inside the lambda expression for each item.
An old question, but since I just fought with this, heres my method. Though it just uses a Trigger as opposed to a MultiTrigger
For XAML:
<Label Content="This text is for testing purposes only.">
<Style TargetType="{x:Type ContentPresenter}">
<Style.Resources>
<Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource {x:Type TextBlock}}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="TextDecorations" Value="Underline" />
</Trigger>
</Style.Triggers>
</Style>
</Style.Resources>
</Style>
</Label>
For C# Codebehind:
public override void OnApplyTemplate()
{
if( !hasInitialized )
{
var tbUnderStyle = new Style( typeof(TextBlock), (Style)FindResource(typeof(TextBlock)) );
var tbUnderSetter = new Setter( TextBlock.TextDecorationsProperty, TextDecorations.Underline );
var tbUnderTrigger = new Trigger() { Property = Label.IsMouseOverProperty, Value = true };
tbUnderTrigger.Setters.Add( tbUnderSetter );
var contentPresenter = FindVisualChild<ContentPresenter>( this );
contentPresenter.Resources.Add( typeof(TextBlock), tbUnderStyle );
hasInitialized = true;
}
}
hasInitialized being a bool that is set to false in the constructor, and FindVisualChild sourced from here.

Apply default WPF style

I have a global style that sets all my TextBox styles, but in some cases I want to revert just the Foreground color to the original non-custom-style color. I tried using {TemplateBinding Foreground} inside the specific TextBoxes that I wanted to revert. It didn't end up being valid XAML and I'm not sure that's the right way anyhow.
Any ideas? Thanks.
There's a few ways this could be done. If you look at the Precedence List on the MSDN
then you can see that the Forground set in ways 1-8 will override the Foreground from a default style. The easiest way being just to set the local value in the TextBox.
<TextBox Foreground="Red" />
Another thing that you can do is use the 'BasedOn' property of styles to override the other versions. This does require giving a key value to your default style, but that can then be used to also apply the default like in this example:
<Style TargetType="{x:Type TextBox}"
x:Key="myTextBoxStyle">
<Setter Property="Foreground"
Value="Red" />
<Setter Property="FontWeight"
Value="Bold" />
</Style>
<!-- Style applies to all TextBoxes -->
<Style TargetType="{x:Type TextBox}"
BasedOn="{StaticResource myTextBoxStyle}" />
<TextBox Text="Hello">
<TextBox.Style>
<Style BasedOn="{StaticResource myTextBoxStyle}" TargetType="{x:Type TextBox}">
<Setter Property="Foreground"
Value="Blue" />
</Style>
</TextBox.Style>
</TextBox>
Edit:
In the case that the default style is applying a value and you want to revert it to the base value there are a few ways I can think of, off hand, to get this behavior. You can't, that I know of, bind back to the default theme value in a generic manner.
We can however do some other things. If we need the style to not apply some properties, we can set the style to {x:Null}, thus stopping the default style from applying. Or we can give the element it's own style that does not inherit from the base style and then re-apply only the setters that we need:
<TextBox Text="Hello" Style="{x:Null}" />
<TextBox Text="Hello">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="FontWeight"
Value="Bold" />
</Style>
</TextBox.Style>
</TextBox>
We could modify the default style so that the Foreground will only be set on certain conditions, such as the Tag being a certain value.
<Style TargetType="{x:Type TextBox}"
x:Key="myTextBoxStyle">
<Setter Property="FontWeight"
Value="Bold" />
<Style.Triggers>
<Trigger Property="Tag"
Value="ApplyForeground">
<Setter Property="Foreground"
Value="Red" />
</Trigger>
</Style.Triggers>
</Style>
<TextBox Text="Hello" />
<TextBox Text="Hello" Tag="ApplyForeground" />

Can you define multiple TargetTypes for one XAML style?

In HTML/CSS you can define a style which can be applied to many types of elements, e.g.:
.highlight {
color:red;
}
can be applied to both P and DIV, e.g.:
<p class="highlight">this will be highlighted</p>
<div class="highlight">this will also be highlighted</div>
but in XAML you seem to have to define the TargetType for styles, otherwise you get an error:
<Style x:Key="formRowLabel" TargetType="TextBlock">
is there a way to allow a XAML style to be applied to multiple elements or even to leave it open as in CSS?
The setters in WPF styles are checked during compile time; CSS styles are applied dynamically.
You have to specify a type so that WPF can resolve the properties in the setters to the dependency properties of that type.
You can set the target type to base classes that contain the properties you want and then apply that style to derived classes. For example, you could create a style for Control objects and then apply it to multiple types of controls (Button, TextBox, CheckBox, etc)
<Style x:Key="Highlight" TargetType="{x:Type Control}">
<Setter Property="Foreground" Value="Red"/>
</Style>
...
<Button Style="{StaticResource Highlight}" Content="Test"/>
<TextBox Style="{StaticResource Highlight}" Text="Test"/>
<CheckBox Style="{StaticResource Highlight}" Content="Test"/>
<!-- Header text style -->
<Style x:Key="headerTextStyle">
<Setter Property="Label.VerticalAlignment" Value="Center"></Setter>
<Setter Property="Label.FontFamily" Value="Trebuchet MS"></Setter>
<Setter Property="Label.FontWeight" Value="Bold"></Setter>
<Setter Property="Label.FontSize" Value="18"></Setter>
<Setter Property="Label.Foreground" Value="#0066cc"></Setter>
</Style>
<!-- Label style -->
<Style x:Key="labelStyle" TargetType="{x:Type Label}">
<Setter Property="VerticalAlignment" Value="Top" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Margin" Value="0,0,0,5" />
</Style>
I think both of these methods of declaring a style might answer your question.
In the first one, there is no TargetType specified, but the property names are prefixed with 'Label'. In the second one, the style is created for Label objects.
Another method to do it is:
<UserControl.Resources>
<Style x:Key="commonStyle" TargetType="Control">
<Setter Property="FontSize" Value="24"/>
</Style>
<Style BasedOn="{StaticResource commonStyle}" TargetType="ListBox"/>
<Style BasedOn="{StaticResource commonStyle}" TargetType="ComboBox"/>
</UserControl.Resources>
I wanted to apply a style to a Textblock and a TextBox but the selected answer didn't work for me because Textblock doesn't inherit from Control, in my case I wanted to affect the Visibility property, so I used FrameworkElement
<Style x:Key="ShowIfRequiredStyle" TargetType="{x:Type FrameworkElement}">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ShowIfRequiredStyle, UpdateSourceTrigger=PropertyChanged}" Value="true">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
<TextBlock Style="{StaticResource ResourceKey=ShowIfRequiredStyle}"/>
<TextBox Style="{StaticResource ResourceKey=ShowIfRequiredStyle}"/>
This works for the Visibility property because both items inherit from Frameworkelement and the property is defined there. Of course this will not work for properties defined only in Control, you can search the hierarchy tree and try to find a base class, anyway I thought this could help someone since this is a top search result and the selected answer is a little incomplete.
There is an alternative answer to the question. You CAN leave the TargetType parameter off the style altogether which will allow it to apply to various different controls, but only if you prefix the property name with "Control."
<Style x:Key="Highlight">
<Setter Property="Control.Foreground" Value="Red"/>
</Style>
Obviously, this only works for properties of the base control class. If you tried to set ItemsSource say, it would fail because there is no Control.ItemsSource
I got this working
<Style x:Key="HeaderStyleThin" TargetType="{x:Type Border}">
<Setter Property="Background" Value="Black" />
<Style.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Background=" Value="Red" />
</Style>
</Style.Resources>
</Style>

Resources