WPF: Address nested styles by key - c#

I have a fairly complex WPF UserControl that needs a lot of custom styling, and several different styles for the same control types. The same styles are not used in any other places.
I would like to use nested styles (using Style.Resources) as a sort of namespacing mechanism as follows:
Example user control:
<UserControl Style="{StaticResource AwesomeControl}>
<Grid>
<Button Style="{StaticResource ButtonA}"/>
<Button Style="{StaticResource ButtonB}"/>
</Grid>
</UserControl>
How I want to define my styles:
<ResourceDictionary>
<Style TargetType="UserControl" x:Key="AwesomeControl">
<Style.Resources>
<Style TargetType="Button" x:Key="ButtonA"> </Style>
<Style TargetType="Button" x:Key="ButtonB"> </Style>
</Style.Resources>
</Style>
</ResourceDictionary>
However, this does not work. From what I can tell, it does not seem possible to address nested styles by their key. (I have searched around a lot, but cannot find a single example doing something like this.)
I can make it work easily by removing the nesting of styles, keeping them all at top level. However, then I have to change their keys to something like AwesomeControlButtonA, etc. to distinguish them from other parts of the application
That does not seem ideal to me.
So my question is:
Is something like I am trying with the code above possible? If not, are there other ways of namespacing I can use to prevent awkward keys like AwesomeControlButtonA?

Maybe DynamicResource could solve your problem
<Grid>
<Button Style="{DynamicResource ButtonA}"/>
<Button Style="{DynamicResource ButtonB}"/>
</Grid>
In context:
<Grid>
<Grid.Resources>
<Style x:Key="AW" TargetType="UserControl">
<Style.Resources>
<Style TargetType="Button" x:Key="AB">
<Setter Property="Background" Value="Red" />
</Style>
<Style TargetType="Button" x:Key="BB">
<Setter Property="Background" Value="Yellow" />
</Style>
</Style.Resources>
</Style>
<Style x:Key="AR" TargetType="UserControl">
<Style.Resources>
<Style TargetType="Button" x:Key="AB">
<Setter Property="Background" Value="Green" />
</Style>
<Style TargetType="Button" x:Key="BB">
<Setter Property="Background" Value="Blue" />
</Style>
</Style.Resources>
</Style>
</Grid.Resources>
<StackPanel>
<UserControl Style="{StaticResource AW}">
<StackPanel>
<Button Content="A" Style="{DynamicResource AB}" />
<Button Content="A" Style="{DynamicResource BB}" />
</StackPanel>
</UserControl>
<UserControl Style="{StaticResource AR}">
<StackPanel>
<Button Content="A" Style="{DynamicResource AB}" />
<Button Content="A" Style="{DynamicResource BB}" />
</StackPanel>
</UserControl>
</StackPanel>
</Grid>

Related

Bring textbox on top of canvases below it

I have three canvases and each one contains textboxes.
The first canvas contains a textbox bigger than the canvas and should be on top of the below canvases.
I tried to use z-index but it seems canvases are always on top on the textboxes.
You could explictly create the item containers, i.e. ListBoxItems.
The ListBoxItems will be direct child elements of the ListBox's ItemsPanel and hence have a common parent element, which is required to make the ZIndex property work:
<ListBox>
<ListBoxItem Panel.ZIndex="1">
<Canvas ...>
...
</Canvas>
</ListBoxItem>
<ListBoxItem>
<Canvas ...>
...
</Canvas>
</ListBoxItem>
<ListBoxItem>
<Canvas ...>
...
</Canvas>
</ListBoxItem>
</ListBox>
I would also apply some styling to make your code way shorter and easier to maintain. Should look kinda like that before applying Clemenses solution:
<ListBox>
<ListBox.Resources>
<Style TargetType="{x:Type Canvas}">
<Setter Property="Width" Value="500" />
<Setter Property="Background" Value="Blue" />
<Setter Property="Height" Value="40" />
</Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Width" Value="30" />
<Setter Property="Canvas.Top" Value="10" />
<Setter Property="Height" Value="20" />
</Style>
</ListBox.Resources>
<Canvas Name="firstCanvas"
Top="80"
Left="100"
ZIndex="3">
<TextBox Name="BigText"
Background="White"
Canvas.ZIndex="6"
Canvas.Left="50"/>
<TextBox Canvas.Left="100"/>
<TextBox Canvas.Left="150"/>
<TextBox Canvas.Left="200"/>
</Canvas>
<Canvas Top="80"
Left="100"
ZIndex="1">
<TextBox Canvas.Left="100"/>
<TextBox Canvas.Left="150"/>
<TextBox Canvas.Left="200"/>
</Canvas>
<Canvas Top="80"
Left="100"
ZIndex="2">
<TextBox Canvas.Left="100"/>
<TextBox Canvas.Left="150"/>
<TextBox Canvas.Left="200"/>
</Canvas>
</ListBox>

How do I remove the X axis labels from this chart?

Keeping myself concise and to the point I wish to remove the labels or at the very least modify them from my X-Axis :
I seem to be using a modified version of this library that allows me to use some tools usually won't work in wp7.
xmlns:charting="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit"
This is the LineSeries definitions as of right now :
<charting:Chart x:Name="chart" Title="Humidity Readings" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" Foreground="#FF3C58C8">
<chartingToolkit:LineSeries x:Name="HumidityGraph" Title="Humidity" BorderThickness="2" ItemsSource="{Binding Data}" IndependentValuePath="Time" DependentValuePath="Measurement"/>
</charting:Chart>
In response to chridam I tried implementing his suggestion - No succes.
<charting:Chart x:Name="chart" Title="Humidity Readings" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" Foreground="#FF3C58C8">
<charting:Chart.LegendStyle>
<Style TargetType="visualizationToolkit:Legend">
<Setter Property="Width" Value="0"/>
<Setter Property="Height" Value="0"/>
</Style>
</charting:Chart.LegendStyle>
<!--there doesn't seem to be an AxisLabelStyle that I can create a style in.-->
<Style x:Key="EmptyStyle" TargetType="charting:NumericAxisLabel">
<Setter Property="StringFormat" Value="" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="charting:NumericAxisLabel">
<TextBlock />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<chartingToolkit:LineSeries x:Name="HumidityGraph" Title="Humidity" BorderThickness="2" ItemsSource="{Binding Data}" IndependentValuePath="Time" DependentValuePath="Measurement" TransitionDuration="0:1:1.5" FontSize="18.667">
<charting:LinearAxis AxisLabelStyle="{StaticResource EmptyStyle}" Orientation="X" ShowGridLines="True"/>
</chartingToolkit:LineSeries>
</charting:Chart>
Try specifying a style for Axis Label that sets the StringFormat property value to empty
<chartingToolkit:CategoryAxis>
<chartingToolkit:CategoryAxis.AxisLabelStyle>
<Style TargetType="AxisLabel">
<Setter Property="StringFormat" Value=""></Setter>
</Style>
</chartingToolkit:CategoryAxis.AxisLabelStyle>
</chartingToolkit:CategoryAxis>
Update:
Another alternative is to apply the style template
<Style x:Key="EmptyStyle" TargetType="charting:NumericAxisLabel">
<Setter Property="StringFormat" Value="" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="charting:NumericAxisLabel">
<TextBlock />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
to this:
<charting:LineSeries.DependentRangeAxis>
<charting:LinearAxis AxisLabelStyle="{StaticResource EmptyStyle}"
Orientation="X"
ShowGridLines="True"/>
</charting:LineSeries.DependentRangeAxis>
For more resources on styling the chart, Styling a Silverlight Chart

How I can hide a Button inside a Control Template?

How can I set Visibility="Visible" for the Button inside the Control Template when the IsSendBtnVisible property in the code-behind is true?
Here's my WPF page:
<Page
x:Class="CardViewPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CardViewPage">
<Grid Name="content" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<DocumentViewer Margin="0" Grid.Row="0" Name="documentViewer" />
</Grid>
</Page>
Here's my Custom Template for the document viewer on this page:
<Style TargetType="{x:Type DocumentViewer}">
...
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DocumentViewer}">
...
<Button
Click="btnSendToServer_Click"
Width="25"
Height="25"
Visibility="Collapsed" />
...
</ControlTemplate>
</Setter>
...
</Style>
U need to declare DependancyProperty for ur DocumentViewer and use TemplateBinding in xaml ControlTemplate (UrProperty for example)
<ControlTemplate TargetType="{x:Type DocumentViewer}">
...
<Button Click="btnSendToServer_Click"
Width="25"
Height="25"
Visibility="{TemplateBinding UrProperty}"
/>
...
</ControlTemplate>
I suggest you to use data triggers to achieve this...
<Button
Click="btnSendToServer_Click"
Width="25"
Height="25">
<Button.Style>
<Style>
<Setter Property="Button.Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsSendBtnVisible}" Value="True">
<Setter Property="Button.Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Make sure to set visibility to collapsed in the style but not in the button properties..
Note: Binding for data trigger may change depending on your data context
Dima Martovoi,bathineni - thanks for replies.
Dima Martovoi, i think inherit from DocumentViewer is to hard for this small problem.
I tried to use variant with DataTrigger from bathineni's solution, but it's not works. Don't know, why.
Problem was solved using next binding:
<Button
Visibility="{Binding RelativeSource={RelativeSource AncestorType=Page},Path=SendToServerVisiblity}">
</Button>
where
public Visibility SendToServerVisiblity
{
get
{
if (IsOnlineMode)
return Visibility.Visible;
return Visibility.Collapsed;
}
}
in page code-behind

Creating Layered Custom Control Templates (WPF 4 / .net 4.0 / C#)

I need some help in trying to recreate the following style:
The sea of red can be ignored because it's just the background the textbox is sitting on.
To create the textbox I use the following xaml:
<TextBox Name="tbSorageName"
Grid.Column="1"
Width="250"
Height="30"
Margin="0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
IsReadOnly="True"
Style="{StaticResource MainTextBoxStyle}"
Text="{Binding SelectedStorage.Name,
Mode=OneWay,
UpdateSourceTrigger=PropertyChanged}" />
To style it, I used the following style:
<Style x:Key="MainTextBoxStyle" TargetType="TextBox">
<Setter Property="FontSize" Value="16" />
<Setter Property="Foreground" Value="Snow" />
<Setter Property="FontFamily" Value="Calibri" />
<Setter Property="TextAlignment" Value="Center" />
<Setter Property="Background" Value="{DynamicResource MainTextBox_BGBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource MainTextBox_BorderBrush}" />
<Setter Property="BorderThickness" Value="2" />
<Setter Property="TextAlignment" Value="Left" />
</Style>
The Brushes I use are:
<SolidColorBrush x:Key="MainTextBox_BGBrush" Color="#3A3A3A" />
<SolidColorBrush x:Key="MainTextBox_BorderBrush" Color="#656565" />
That creates the basic textbox I use in my application, but I want to take my design much further by floating some meaningful text that describes the contents of textbox on the right hand side of the text box - So it should be anchored to the right hand side.
You can do this easily via 2 approaches.
1.Create a user control that consists of a textbox and a textblock. The Xaml for the UserControl would look similar to the following
<Grid Width="{Binding Path=Width, ElementName=tbSorageName}" Height="{Binding Path=Height, ElementName=tbSorageName}">
<TextBox Name="tbSorageName"
Grid.Column="1"
Width="250"
Height="30"
Margin="0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
IsReadOnly="True"
Style="{StaticResource MainTextBoxStyle}"
Text="hello" />
<TextBlock HorizontalAlignment="Right" Margin="5" Foreground="Red" FontStyle="Italic">Name</TextBlock>
</Grid>
2.Overlay an Adorner on the textbox and draw the description over the Textbox yourself via an OnRender Override.
I would go with the Usercontrol approach since that is cleaner and easier to maintain.

WPF: how to style a class like in css?

Let's say I have a UserControl with 4 Borders:
<Border />
<Border />
<Border />
<Border />
Now in my Resources I can go:
<Style TargetType="{x:Type Border}">
... change some properties here
</Style>
Now this is all good, but it will target all borders in my UserControl.
But what if I just want to target a subset of them?
I'd like to go:
<Border Class="Type1" />
<Border Class="Type1" />
<Border />
<Border />
And then go:
<Style TargetType="{x:Type Border}" TargetClass="Type1">
... change some properties here
</Style>
But this obviously doesn't exist, is there some other way I can achieve what I'm after?
Thanks
Though the syntax isn't quite as clean as in CSS, it is a lot more specific.
To build on your example, what you're looking for is:
<Border Style="{StaticResource Type1}" />
<Border Style="{StaticResource Type1}" />
<Border />
<Border />
And then go:
<Style TargetType="{x:Type Border}" x:Key="Type1">
... change some properties here
</Style>
Remember that WPF styles don't actually cascade like CSS does.
A more detailed styling reference:
https://web.archive.org/web/20141210000517/http://dotnetslackers.com/articles/wpf/StylesResourcesAndControlTemplatesInWPF.aspx
Something that I find most people are not aware of is WPF's ability to nest Styles within Style.Resources. For example:
<!-- Define a new style for Borders called InfoBox, that will have a red background,
and further override all buttons within it to have Yellow Text. An extra style,
"Strawberry" is also defined, that lets specific buttons be selected to be styled
as Green FG on DarkRed BG -->
<Style TargetType="{x:Type Border}" x:Key="InfoBox">
<Setter Property="Background" Value="Red"/>
<Style.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="DarkYellow"/>
</Style>
<Style TargetType="{x:Type Button}" x:Key="Strawberry">
<Setter Property="Foreground" Value="Green"/>
<Setter Property="Background" Value="DarkRed"/>
</Style>
</Style.Resources>
</Style>
...
<Border Style="{DynamicResource InfoBox}">
<StackPanel>
<Button Content="I am a banana!"/>
<Button Style="{DynamicResource Strawberry}" Content="I am red!"/>
</StackPanel>
</Border>
While not exactly the same as CSS (There isn't much support for standard pseudo-selectors), this gives you a huge amount of power and flexibility. Couple this with skillful use of ItemsControls and you can do some great things.
you can set the style directly on the <Border> using an x:key and the StaticResource (or DynamicResource) property of the Border. if you would like to change the style at runtime, then you should lean towards using the DynamicResource over the StaticResource.
<Style x:Key="something" TargetType="{x:Type Border}">
</Style>
<Border style="{StaticResource something}"/>
<Style x:Key="styleKey" TargetType="{x:Type Border}">
... change some properties here
</Style>
and
<Border Style="{StaticResource styleKey}"

Categories

Resources