Using BasedOn and Trigger to manipulate nested property in parent Style - c#

We have a file of standard styles. One style, SectionGroup, we use on all our GroupBox elements. It has a custom template which uses a Border to put an underline below the header, among other things.
On one page, we have a checkbox next to a GroupBox header; when the user unchecks the checkbox, the contents of the GroupBox hide (visibility collapsed) and the header remains. Unfortunately the underline under the header then looks ugly; we also want to hide this.
I've given it my best attempt, so the parent SectionGroup style now looks like this:
<Style x:Key="SectionGroup" TargetType="GroupBox">
<Style.Resources>
<Thickness x:Key="HeaderBorderThickness">0,0,0,1</Thickness>
</Style.Resources>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GroupBox">
<Grid Margin="0">
...
<Border Grid.Row="0" BorderThickness="{DynamicResource HeaderBorderThickness}" >
<TextBlock Text="{Binding Header, RelativeSource={RelativeSource AncestorType=GroupBox}}"/>
</Border>
...
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
By defining the HeaderBorderThickness resource and using it as a DynamicResource, I can override it in my page (as explained in this answer):
<GroupBox>
<GroupBox.Style>
<Style TargetType="GroupBox" BasedOn="{StaticResource SectionGroup}">
<Style.Resources>
<Thickness x:Key="HeaderBorderThickness">0,0,0,0</Thickness>
</Style.Resources>
<!-- TODO triggers here.. -->
</Style>
</GroupBox.Style>
<GroupBox.Header>Section One</GroupBox.Header>
...contents...
</GroupBox>
So indeed, by redefining a Thickness of the same key, the DynamicResource works as expected and there is no underline on the header.
Now I need to toggle it based on a trigger/binding. I'm pretty new to this, but elsewhere in this page I have figured out to do stuff like this:
<Grid Visibility="{Binding Path=FooBoolean, Converter={StaticResource BooleanToVisibility}}">
I think there's a little more magic involved in our viewmodel class (followed the example of existing bindings & properties), but it works.
Now the question is -- how do I bind the boolean value in FooBoolean, to the HeaderBorderThickness resource value? Or what other means can I use to accomplish my goal?

It seems to me that you could do this in a much more WPF way with a DataTrigger, perhaps something like this:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="{Binding Header, RelativeSource={RelativeSource AncestorType=
GroupBox}}"/>
<Rectangle Grid.Row="1" Height="1" Margin="0,2,0,0" Fill="Black">
<Rectangle.Style>
<Style>
<Setter Property="Rectangle.Visibility" Value="Visible" />
<Style.Triggers>
<DataTrigger Binding="{IsChecked, ElementName=YourCheckBox}"
Value="False">
<Setter Property="Rectangle.Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
</Grid>
This method enables you to set the Width, Height, Padding and whatever other properties of the line. If you can't access the CheckBox directly from the Style, then you could try adding a bool property to bind to both the Checkbox.IsChecked property and the DataTrigger.Binding property. Or just manage the Rectangle.Visibility in your own method.

Related

C# WPF The global style not working in some parts of the code [duplicate]

I am trying to learn something about WPF and I am quite amazed by its flexibility.
However, I have hit a problem with Styles and DataTemplates, which is little bit confusing.
I have defined below test page to play around a bit with styles etc and found that the Styles defined in <Page.Resources> for Border and TextBlock are not applied in the DataTemplate, but Style for ProgressBar defined in exactly the same way is applied.
Source code (I just use Kaxaml and XamlPadX to view the result)
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="Background" Value="SkyBlue"/>
<Setter Property="BorderBrush" Value="Black"/>
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="CornerRadius" Value="5"/>
</Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="FontWeight" Value="Bold"/>
</Style>
<Style TargetType="{x:Type ProgressBar}">
<Setter Property="Height" Value="10"/>
<Setter Property="Width" Value="100"/>
<Setter Property="Foreground" Value="Red"/>
</Style>
<XmlDataProvider x:Key="TestData" XPath="/TestData">
<x:XData>
<TestData xmlns="">
<TestElement>
<Name>Item 1</Name>
<Value>25</Value>
</TestElement>
<TestElement>
<Name>Item 2</Name>
<Value>50</Value>
</TestElement>
</TestData>
</x:XData>
</XmlDataProvider>
<HierarchicalDataTemplate DataType="TestElement">
<Border Height="45" Width="120" Margin="5,5">
<StackPanel Orientation="Vertical" Margin="5,5" VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBlock HorizontalAlignment="Center" Text="{Binding XPath=Name}"/>
<ProgressBar Value="{Binding XPath=Value}"/>
</StackPanel>
</Border>
</HierarchicalDataTemplate>
</Page.Resources>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel Orientation="Vertical" VerticalAlignment="Center">
<Border Height="45" Width="120" Margin="5,5">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBlock HorizontalAlignment="Center" Text="Item 1"/>
<ProgressBar Value="25"/>
</StackPanel>
</Border>
<Border Height="45" Width="120" Margin="5,5">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBlock HorizontalAlignment="Center" Text="Item 2"/>
<ProgressBar Value="50"/>
</StackPanel>
</Border>
</StackPanel>
<ListBox Margin="10,10" Width="140" ItemsSource="{Binding Source={StaticResource TestData}, XPath=TestElement}"/>
</StackPanel>
</Page>
I suspect it has something to do with default styles etc, but more puzzling is why some Styles are applied and some not. I cannot find an easy explanation for above anywhere and thus would like to ask if someone would be kind enough to explain this behaviour in lamens' terms with possible links to technical description, i.e. to MSDN or so.
Thanks in advance for you support!
I discovered a simple workaround for this. For any elements that are not able to search outside the data template encapsulation boundary (i.e. are not being implicitly styled), you can just declare an empty style within the data template for that element type and use the BasedOn attribute of the style to find the correct implicit style outside the data template to apply.
In the example below, the TextBox is able to search outside the data template encapsulation boundary (because it inherits from Control?), but the TextBlock is not able to, so I declare the empty style for it which can search outside the data template.
<ItemsControl.ItemTemplate>
<DataTemplate>
<DataTemplate.Resources>
<Style TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}" />
</DataTemplate.Resources>
<DockPanel>
<TextBlock Text="{Binding Name}" />
<TextBox Text="{Binding Value}" />
</DockPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
This is actually by design. Elements that do not derive from Control will not pick up implicit Styles, unless they are in the application resources.
This link explains this in more detail, or you can view the Connent bug report.
I've looked into this also, and I personally think it's a bug. I've noticed that the style is set if you name your styles like so:
<Style x:Key="BorderStyle" TargetType="{x:Type Border}">
etc...
and explicitly set your DataTemplate to use those styles:
<HierarchicalDataTemplate DataTemplate="TestElement">
<Border Height="45" Width="120" Margin="5,5", Style="{StaticResource BorderStyle}">
I think that it's possible that for DataTemplates (and maybe ControlTemplates), they default to having a null style, unless you explicitly set them.
That to me is not meant to happen - it's not a logical way of WPF working...
This is because ListBox is a logical parent of your datatemplate items, now remember, all properties those are "inheritable" like font, forecolor etc, are derived from the logical parent and ListBox already overrides it in its own default style, thats why this will not work. However in this case, you can use named styles as Mr. Dave has suggested, but I think if it does not work then this is a known problem in case of List Box etc, you can refere to my question here, i had similar problem in listbox, and the answers in my question are in more detail.

Is it possible to provide 'arguments' to Custom control Styles & Templates?

I have 2 buttons, both of which are using a style to keep a consistent UI. However for 1 button, I would like to provide an image for, and the other just text.
I suppose I could create a new style, copy everything over, and the reformat it to my liking, but this seems like waste, is time consuming, and i think i would have to do it for each instance I wish to have a image on a button. I mean, that's fine, but I just want to know if there is an alternative that would make things more elegant.
I think I should somehow be able to push an 'arguement' or data to a style, either in or out of XAML to format the style, or something that would accomplish this (I'm sure the terminology is wrong).
Here is the Button Style:
<Style x:Key="Control_Button" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Image> <!-- Optional Image here --> </Image>
<TextBlock Name="btn" Text="{TemplateBinding Content}" Padding="16" VerticalAlignment="Center">
<TextBlock.TextDecorations>
<TextDecoration Location="Underline" />
</TextBlock.TextDecorations>
</TextBlock>
<!-- FIX ME: not underlined normally -->
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="TextBlock.IsMouseOver" Value="True">
<Setter TargetName="btn" Property="TextDecorations" Value="none" />
<!-- FIX ME: underlined on hover -->
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
What you are asking for is not possible without creating a custom control or user control.
What you should do though is setting the contents of the button the way you like. If you want only a string, you can set it directly:
<Button>my text</Button>
or with a binding:
<Button Content={Binding textProperty} />
To include an image in the button, add a panel as content, in this example I added a StackPanel, but you can also use a Grid or any other element:
<Button>
<Button.Content>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding myImagePath}" />
<TextBlock Text="{Binding myText}" />
</StackPanel>
</Button.Content>
</Button>

How to use xaml vector images as image source in Windows 8 app

I created some assets in inkscape and would like to use them as icons in a windows 8 application. I have done some reading and it seams that while .Net 4.5 supports SVG, the modern ui profile does not. I converted the svg to xaml using this tool.
I get the following xaml.
<Canvas xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Name="svg2997" Width="744.09448" Height="1052.3622" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Canvas x:Name="layer1">
<Path Fill="#FFCCCCCC" Stroke="#FF000000" StrokeThickness="1.34377062" StrokeMiterLimit="4" x:Name="path3007" Data="M372.58272,134.72445C167.96301,134.72445 2.06820310000001,300.58818 2.06820310000001,505.20789 2.06820310000001,709.8276 167.96301,875.72241 372.58272,875.72241 577.20243,875.72241 743.06616,709.8276 743.06616,505.20789 743.06616,300.58818 577.20243,134.72445 372.58272,134.72445z M280.73888,251.77484L455.94149,251.77484 455.94149,413.70594 628.16035,413.70594 628.16035,588.97071 455.94149,588.97071 455.94149,773.71514 280.73888,773.71514 280.73888,588.97071 106.22005,588.97071 106.22005,413.70594 280.73888,413.70594 280.73888,251.77484z" />
</Canvas>
</Canvas>
If I add this directly to my apps xaml it will render however the scale is way off.
I would like to use this as an image source for an image object if possible.
<Image HorizontalAlignment="Left" Height="100" Margin="127,37,0,0" VerticalAlignment="Top" Width="100" Source="Assets/plus_circle.xaml"/>
Can this be done?
Most AppBar buttons are based on a style included in StandardStyles called AppBarButtonStyle.
To customize the text of the button you set the AutomationProperties.Name attached property, to customize the icon in the button you set the Content property, and it's also a good idea to set the AutomationProperties.AutomationId attached property for accessibility reasons.
Here's an example of a button customized using this approach:
<Style x:Key="FolderButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
<Setter Property="AutomationProperties.AutomationId" Value="FolderAppBarButton"/>
<Setter Property="AutomationProperties.Name" Value="Folder"/>
<Setter Property="Content" Value=""/>
</Style>
As mentioned above, to customize the icon you set the Content property. The challenge is how you set the content so it displays your custom vector art.
It turns out you can place any path Xaml, even yours, into a Viewbox to change its scale. That was my first approach, but it doesn't work. In fact, it seems any time you use Xaml expanded notation to set the Content property for a button it doesn't work.
<Style x:Key="SquareButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
<Setter Property="AutomationProperties.AutomationId" Value="SquareAppBarButton"/>
<Setter Property="AutomationProperties.Name" Value="Square"/>
<Setter Property="Content">
<Setter.Value>
<!-- This square will never show -->
<Rectangle Fill="Blue" Width="20" Height="20" />
</Setter.Value>
</Setter>
I actually think this is a bug, but luckily there is a workaround.
Tim Heuer wrote an excellent article on the simplest way to use a Xaml Path as the artwork for a button. That article is here:
http://timheuer.com/blog/archive/2012/09/03/using-vectors-as-appbar-button-icons.aspx
In short, you need to define a style that sets up all the bindings correctly:
<Style x:Key="PathAppBarButtonStyle" BasedOn="{StaticResource AppBarButtonStyle}" TargetType="ButtonBase">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Path Width="20" Height="20"
Stretch="Uniform"
Fill="{Binding Path=Foreground, RelativeSource={RelativeSource Mode=TemplatedParent}}"
Data="{Binding Path=Content, RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
</DataTemplate>
</Setter.Value>
</Setter>
Then you create a style that inherits from that style and you paste in your path. Here is the style for your artwork you listed above:
<Style x:Key="CrossButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource PathAppBarButtonStyle}">
<Setter Property="AutomationProperties.AutomationId" Value="CrossAppBarButton"/>
<Setter Property="AutomationProperties.Name" Value="Cross"/>
<Setter Property="Content" Value="M372.58272,134.72445C167.96301,134.72445 2.06820310000001,300.58818 2.06820310000001,505.20789 2.06820310000001,709.8276 167.96301,875.72241 372.58272,875.72241 577.20243,875.72241 743.06616,709.8276 743.06616,505.20789 743.06616,300.58818 577.20243,134.72445 372.58272,134.72445z M280.73888,251.77484L455.94149,251.77484 455.94149,413.70594 628.16035,413.70594 628.16035,588.97071 455.94149,588.97071 455.94149,773.71514 280.73888,773.71514 280.73888,588.97071 106.22005,588.97071 106.22005,413.70594 280.73888,413.70594 280.73888,251.77484z"/>
</Style>
And finally, you use it in your AppBar like this:
<Button Style="{StaticResource CrossButtonStyle}" />
Dev support, design support and more awesome goodness on the way:
http://bit.ly/winappsupport
I'm pretty positive you can't just inject Path Data into an Image Source and expect it to magically work unless it's through a Drawing Object as Source. What you can however do is adopt your Path into a ContentControl for re-use in the same way without having to go through the trouble of Drawing objects for every instance.
So instead of;
<Image Source="..."/>
Just do something like this and plop it in your Object.Resources or ResourceDictionary;
<Style x:Key="YourThingy" TargetType="ContentControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ContentControl">
<Path Fill="#FFCCCCCC" Stroke="#FF000000" StrokeThickness="1.34377062" StrokeMiterLimit="4" x:Name="path3007" Data="M372.58272,134.72445C167.96301,134.72445 2.06820310000001,300.58818 2.06820310000001,505.20789 2.06820310000001,709.8276 167.96301,875.72241 372.58272,875.72241 577.20243,875.72241 743.06616,709.8276 743.06616,505.20789 743.06616,300.58818 577.20243,134.72445 372.58272,134.72445z M280.73888,251.77484L455.94149,251.77484 455.94149,413.70594 628.16035,413.70594 628.16035,588.97071 455.94149,588.97071 455.94149,773.71514 280.73888,773.71514 280.73888,588.97071 106.22005,588.97071 106.22005,413.70594 280.73888,413.70594 280.73888,251.77484z" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Then just plop it on your view wherever and as many times as you like;
<ContentControl Style="{StaticResource YourThingy}"/>
You will however want to play with that Path of yours. It seems set a large size, but hopefully this provides a good alternative for your circumstance. Cheers!

Reusable way to put a bright red box around whatever element currently has focus?

I've got some windows with mainly comboboxes, textboxes, and checkboxes. When you click on one to get focus I need a way to have them be outlined with a colorful box (boss' orders). Is there a way to do this easier than overriding the default style of all of these controls? I've never done that before, so it would take a lot of mucking around on my part to figure it out.
You can try adding a FocusVisualStyle to the Controls that need different focus rectangle styles.
From above link
The second mechanism is to provide a separate style as the value of the FocusVisualStyle property; the "focus visual style" creates a separate visual tree for an adorner that draws on top of the control, rather than changing the visual tree of the control or other UI element by replacing it.
Something like this in your Window's Xaml
<Window.Resources>
<Style x:Key="NewFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Border>
<Rectangle Stroke="Red" Margin="2" StrokeThickness="1" StrokeDashArray="1 2" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
or your Application.Xaml file.
<Application.Resources>
<Style x:Key="NewFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Border>
<Rectangle Stroke="Red" Margin="2" StrokeThickness="1" StrokeDashArray="1 2" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
Usage:
<ComboBox FocusVisualStyle="{StaticResource NewFocusVisual}" Height="23" HorizontalAlignment="Left" Margin="238,102,0,0" Name="ComboBox1" VerticalAlignment="Top" Width="120" />
<CheckBox FocusVisualStyle="{StaticResource NewFocusVisual}" Content="CheckBox" Height="16" HorizontalAlignment="Left" Margin="238,71,0,0" Name="CheckBox2" VerticalAlignment="Top" />
<TextBox FocusVisualStyle="{StaticResource NewFocusVisual}" Height="23" HorizontalAlignment="Left" Margin="238,144,0,0" Name="TextBox1" VerticalAlignment="Top" Width="120" />
If you want the Focus rectangle to change for every type of focus event Microsoft states that:
From Microsoft: Focus visual styles act exclusively for keyboard focus. As such, focus visual styles are a type of accessibility feature. If you want UI changes for any type of focus, whether via mouse, keyboard, or programmatically, then you should not use focus visual styles, and should instead use setters and triggers in styles or templates that are working from the value of general focus properties such as IsFocused or IsFocusWithin.
Give this a shot it works for a TextBox haven't checked your other Controls
<Application.Resources>
<Style TargetType="TextBox" >
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Control.BorderBrush" Value="Red" />
<Setter Property="Control.BorderThickness" Value="3" />
</Trigger>
</Style.Triggers>
</Style>
</Application.Resources>

I need help implementing a style into my radTreeView

I want to take the highlight background off of the radtreeview. I created a style to do this, but I keep getting errors and exceptions such as "Items collection must be empty." If I comment out the style the application works fine, so I know that it is the cause of the problem. I am fairly new to WPF, and I am sure I just don't understand how to use styles yet.
Thanks for your help. Here is the code.
<Grid x:Name="LayoutRoot" Background="Salmon">
<telerik:RadTreeView x:Name="radTreeView" Margin="8" ItemsSource="{Binding Errors}" Background="Salmon" Style="{StaticResource treeStyle}">
<Style TargetType="{x:Type telerik:RadTreeViewItem}" x:Name="treeStyle">
<Setter Property="Focusable" Value="False"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" Value="{x:Null}"/>
<Setter Property="BorderBrush" Value="{x:Null}"/>
</Trigger>
</Style.Triggers>
</Style>
<telerik:RadTreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding SubItems}" >
<Grid Background="Salmon">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="{Binding Description}" IsHitTestVisible="False" />
<ListBox Grid.Row="1" ItemsSource="{Binding Messages}" Margin="20,0,0,0" BorderBrush="#00000000" BorderThickness="0" Background="Salmon" IsHitTestVisible="False" >
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Message}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</HierarchicalDataTemplate>
</telerik:RadTreeView.ItemTemplate>
</telerik:RadTreeView>
</Grid>
</UserControl>
If you know that this is not going to work, I was also trying to get rid of the highlight with the style code:
<Style TargetType="TreeViewItem">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="#FFF"/>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="#000"/>
</Style.Resources>
</Style>
You get exceptions because your style tag is actually an item in the tree, and you have ItemsSource set.
Surround the style with <telerik:RadTreeView.ItemContainerStyle> tag.
This should solve the exception but it will not give you the result that you expect since the control template of the tree view item is actually showing another border that is not affected by the Background property. You will need to change the control template.
Telerik change the styles between releases, so giving you a template of a wrong version will probably won't help you.
But, you can go to the installation folder for Telerik and look for a folder called "Themes". There you'll find a solution with all the themes for telerik.
Choose the one that you use.
Find the resource dictionary for the tree view and copy the style and template for the item to your project.
Change xmlns definitions, make sure you have all the brushes and resources that the style depends upon.
Run to see that the style is ok.
In the template, find the VisualState with x:Name="MouseOver" and delete the storyboard inside it.

Categories