WPF: Setting named color to resource? - wpf

I am modifying the control template of the WPF Calendar control to change the color of text and the Previous and Next arrows on the control. I want to set the color to a local SolidColorBrush resource called MyTextBrush.
The Previous and Next buttons have separate control templates, and each draws a Path object for its button's arrow. Here is the relevant markup for the Previous button:
<Path Margin="14,-6,0,0" Height="10" Width="6" VerticalAlignment="Center" HorizontalAlignment="Left" Stretch="Fill" Data="M288.75,232.25 L288.75,240.625 L283,236.625 z">
<Path.Fill>
<SolidColorBrush x:Name="TextColor" Color="#FF333333" />
</Path.Fill>
</Path>
Note that the color is named TextColor, using the x:Name property.
Here is my problem: The x:Name property is required--WPF throws an exception if it is missing. That means I can't simply replace the entire brush with a reference to the MyTextBrush resource, because I would lose the x:Name value. So, how do I reference MyTextBrush, while still retaining the x:Name property for the brush in this particular control template?
Thanks for your help.

So, how do I reference MyTextBrush,
while still retaining the x:Name
property for the brush in this
particular control template?
Regarding this problem it sounds like you are using a dodgy/fragile template. What control template is it?
If you have full source control of the template, remove references to the named element (most likely in a storyboard). They must be animating the brush for some reason.
The other option might be to just create another unused brush within your template (Perhaps on a hidden element) with the correct name to keep the template happy.
Lastly, you can try adding the x:Name onto the brush in the shared RD, but this is quite complicated and not sure its worth it!
Two more potential solutions:
Try binding just the Color property of the SCB... that should work as its a DP
Change the template animations so they do not use a named brush, but instead use a named parent then access the brush via the TargetProperty e.g. Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Storyboard.TargetName="myNamedParent"

The best solution seems to be to break the Color property out to its own tag, and use a resource reference for that. Here is what it looks like:
<!-- FS: Changed template brush color -->
<SolidColorBrush x:Name="TextColor">
<SolidColorBrush.Color>
<StaticResource ResourceKey="FsTextColor" />
</SolidColorBrush.Color>
</SolidColorBrush>

Related

Can you apply an opacity to a system-defined brush?

I know the WPF brush class has an Opacity property. We have a need to use a system-defined brush but with half the opacity. We'd like to do something like this (this is obviously fake code)...
<Border Background="{DynamicResource {x:Static SystemColors.HighlightBrushKey}, Opacity=0.5}" />
We of course can't change the opacity on a system-defined brush directly because that would mess up everywhere it's used.
What we'd like to do is to somehow define a converter which we take one brush and returns a copy of it with the opacity changed, but since this isn't a binding, we don't know where/how to apply a converter. Plus, if it's a dynamic resource, we need to make sure it tracks changes.
We also can't simply set the opacity on the Border directly either as then all of its children also have the same reduced opacity.
Our current work-around is instead of putting the content directly in the border, we put the border and its contents as siblings in a grid, then we do set the opacity on the border. Since the content is now on top of, instead of inside the border, it isn't affected by the opacity. It just means we've added extra stuff into the visual tree which is annoying, but it does work. It would be much better if we could simply adjust the opacity of a (copy of a) system brush right in the XAML.
A bit late, but for the sake of others...
You can create derivative solid color brushes with new opacities. To do this you simply borrow the color from the original brush used as the binding source, then set a new opacity.
<SolidColorBrush Color="{Binding Color, Source={StaticResource blue-light}}" Opacity="0.5" />
Maybe you could try creating a new brush based on the system color in stead of using the system brush directly, like this:
<Border>
<Border.Background>
<SolidColorBrush
Color="{DynamicResource {x:Static SystemColors.HighlightColorKey}}"
Opacity="0.5" />
</Border.Background>
</Border>
Well, I think I found it! Thanks to other work I've done, I came up with a DynamicResourceBinding concept (StaticResourceBinding too!) which you can use a converter to transform the brush in any way you want.
Here's a link to that page here on StackOverflow where I do this for both Dynamic and Static resources...
Post 33816511: How to create a DynamicResourceBinding

Collapse ContentControl if Content is Collapsed

I've got a ContentControl which has a style containing a border and other visual decorations. I want these decorations to disappear when the content is collapsed, so I figured I have to set the visibility of the ContentControl to collapsed in this case. I got this style for my ContentControl decoration:
<Style x:Key="DecoratedItem1" TargetType="{x:Type c:DecoratedItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type c:DecoratedItem}">
<StackPanel Orientation="Horizontal">
<Border BorderBrush="Black" BorderThickness="2" CornerRadius="2">
<StackPanel Orientation="Horizontal">
<Image Source="/Images/file.png"/>
<ContentPresenter Name="wContent"/>
</StackPanel>
</Border>
</StackPanel>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding ElementName=wContent, Path=Content.Visibility}" Value="Collapsed">
<DataTrigger.Setters>
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger.Setters>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The DecoratedItem class is just a subclass of ContentControl with additional DependencyProperties which are not relevant to this issue, I just wanted to note that I already have a subclass to which I could add code, if necessary.
This works when the content of the ContentControl is a UIElement, however if the content is generated by a DataTemplate it complains about not being able to find the Visibility property.
<!-- works -->
<c:DecoratedItem Style="{StaticResource DecoratedItem1}">
<TextBlock Text="ABC" Visibility="Collapsed"/>
</c:DecoratedItem>
<!-- doesn't work -->
<c:DecoratedItem Style="{StaticResource DecoratedItem1}" Content="ABC">
<c:DecoratedItem.Resources>
<DataTemplate DataType="{x:Type clr:String}">
<TextBlock Text="{Binding}" Visibility="Collapsed"/>
</DataTemplate>
</c:DecoratedItem.Resources>
</c:DecoratedItem>
The error for the second case diplayed in the debug output window is:
System.Windows.Data Error: 40 : BindingExpression path error:
'Visibility' property not found on 'object' ''String' (HashCode=-885832486)'.
BindingExpression:Path=Content.Visibility;
DataItem='ContentPresenter' (Name='wContent');
target element is 'DecoratedItem' (Name='');
target property is 'NoTarget' (type 'Object')
I understand why this happens, but don't know how to fix my style to work as I want it. I don't mind adding helper code to the DecoratedItem subclass if necessary. Any idea how to fix this?
[edit 1]
Some more explanation in regard to the proposed answer:
I can't enforce that the Content is always an UIElement. This is a model-view design after all, and of course I simplified the example a lot. In the real project the content is a model selected from the DataContext, which can be of several different types, and the DataTemplate builds a presentation for that model. Some of the DataTemplates decide (depending on model-state) that there is nothing to present and switch Visibility to Collapsed. I would like to propagate that information to the decorating container. The example above really just presents the problem and not the motivation, sorry.
[edit 2]
Not sure how knowing more about the model would help the problem, but here we go. The data in the Content field doesn't have much in common since it can be a lot of things, this DecoratedItem is supposed to be reusable to give a common visual style to items shown on some forms. Content can be stuff like work items whose DataTemplate collapses them if they are disabled; other kinds of Content can be incomplete and get collapsed. Of course other kinds never may get collapsed.
But note that the data model doesn't really have much to do with the question, which still is how to bind against the Visibility of the expanded content element (after possibly exposing it through the subclass in a bindable way).
There are a couple of ways of describing what's wrong. In the first, working example:
<c:DecoratedItem Style="{StaticResource DecoratedItem1}">
<TextBlock Text="ABC" Visibility="Collapsed"/>
</c:DecoratedItem>
the Content property of the ContentControl is set to be a TextBlock, which is a UIElement with a Visibility property. (This assumes that you have not changed the ContentPropertyAttribute of your derived class DecoratedItem to be something other than Content). Thus, your DataTrigger binding can correctly evaluate:
<DataTrigger Binding="{Binding ElementName=wContent, Path=Content.Visibility}" Value="Collapsed">
Contrast the working case with the failing one:
<c:DecoratedItem Style="{StaticResource DecoratedItem1}" Content="ABC">
in which the Content property is set to an instance of a String, which does not have a Visibility property.
The other way to describe what's wrong is to note that, even though you supply a DataTemplate for the case of Content being a String, Content is still a String and still does not have a Visibility property. In other words, the statement that the Content is generated by the DataTemplate is incorrect -- your DataTemplate just told the control how to display Content of type String.
The general answer to your question is that, if you want the DataTrigger in DecoratedItem1 bound with a Path of Content.Visibility, you need to make sure the content you put in it is always a UIElement. Conversely, if you want to be able to put any sort of Content into the control, you need to trigger off of something else.
The specific answer to your question, strictly, relies on your broader intent (in particular, on how the Visibility of the Content of your control will be set/modified). A couple of possibilities:
if you really want your DataTrigger binding of the form, "Content.Visibility", make sure that the Content is always a UIElement. For instance, use the working form of the style and then bind the Text of TextBlock to something appropriate. However, this doesn't fit so well with the idea of your derived control as a ContentControl, so...
your DataTrigger could probably bind to something else. It seems like, from the way the question is formed, that there is some other property or code-behind that will control whether the various content entities are Visible or not.
finally, you could add an additional DataTrigger to the TextBlock. This DataTrigger would set the visibility of its parent based on its own visibility. Then, bind the DataTrigger in style DecoratedItem1 with Path "Visibility" instead of "Content.Visibility", essentially chaining together Visibilities manually.
Edit
Based on what you've described about how you want to use this, it sounds like you need to consider the visual tree. You might augment your DecoratedItem control to have the following functionality: if all its visual children that are UIElments have a visibility of Collapsed (or if it has no visual children), it is also Collapsed (or, whatever logic makes sense for the desired functionality in terms of the Visibility of its visual children). You'd need to use the VisualTreeHelper class from code -- in particular, the GetChildrenCount and GetChild methods. You'd also, in your DecoratedItem class, override OnVisualChildrenChanged (while still calling the base class method) so that you can get UIElement.IsVisibleChanged events for the visible children.

How do the default color, font family and font size for a TextBlock and Label get set in an WPF app?

Edit: I guess the question wasn't stated very clearly. It actually composes of 4 separate ones:
How does a TextBlock get its default color, if the client app doesn't provide any style, either programmatically or through xaml?
How does a Label get its default color?
How does a TextBlock get its default font size and font family, if the client app doesn't provide any style, either programmatically or through xaml?
How does a Label get its default font size and font family?
BTW, the questions are not about how to change or define styles for the color/font size/font family of a TextBlock or a Label, although they are somehow related. I think I already knew the answer for #2, that is a Label gets its color from SystemColors.ControlTextBrushKey and by overriding ConrolTextBrushKey like so:
<SolidColorBrush x:Key="{x:Static SystemColors.ControlTextBrushKey}" Color="Red"/>
You would be able to "globally" change color for Labels. After some research, I guess I also find the answer for #1: A TextBlock inherits the its foreground color from its containing Window, which by default gets its Foreground color from SystemColors.WindowTextBrushKey. By defining a color for the WindowTextBrush like so:
<Window.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.WindowTextBrushKey}"
Color="Yellow"/>
</Window.Resources>
You would be able to change the "foreground" color for the TextBlocks inside the Window.
Question #3 and #4 remain puzzles for me, but I am assuming they have to do with the SystemFonts.
Hope this makes sense. I really like to know the answers as they have been bothering me for a while. Many thanks!
Below is the original post:
If you look into the style for a Label in the theme (for example "aero.normalcolor.xaml") that comes with Windows, you can find
<Setter Property="Foreground"
Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
Which sets the color for a Label. But there is no where the FontSize property is specified in the style, which I assume has something to do with the SystemFonts. For a TextBlock, it looks even more mysterious as the style for it in "aero.normalcolor.xaml" has only 4 lines:
<Style x:Key="{x:Type TextBlock}"
TargetType="{x:Type TextBlock}">
<Setter Property="TextWrapping"
Value="NoWrap"/>
<Setter Property="TextTrimming"
Value="None"/>
</Style>
Where does a Label or a TextBlock get the values for its color and font size/family from, if the app doesn't set any, and where are those hooks in WPF?
Edit:
This is a test drive attempting to set the TextBlock color through SystemColors.ControlTextBrush (assuming that's where a TextBlock gets its default color from, which seems to be false):
<Window x:Class="TestFontColor.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<StackPanel.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.ControlTextBrushKey}" Color="Red"/>
</StackPanel.Resources>
<Button Content="This is red."/>
<Label Content="This is blue.">
<Label.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.ControlTextBrushKey}" Color="Blue"/>
</Label.Resources>
</Label>
<TextBlock Text="TextBlock: This is still black."/>
</StackPanel>
As far as I remember, in most cases classes like TextBlock, TextBox and many others take the text color from the TextElement.Foreground property. The property value is inherited throughout the visual tree, i.e. you may set it on the root element and have most of the text change its color. For example:
<Grid TextElement.Foreground="Red">
<TextBlock Text="test"/>
</Grid>
In fact, the same is true even for labels: the setter in their default style simply sets the TextElement.Foreground to one of the system colors.
However, this is true only for the default state of controls. Altered states, like highlighting, are not inherited, but rather taken from the system colors, as Rachel has written.
UPDATE
The same is true for FontSize and FontFamily. They are properties of the TextElement class that have attached property usage. They inherit their values. Once you set a value on a visual tree item, all its children will get the same value. Unless they override it either by an explicit property assignment, or by style and so on.
Once again, text color font size and font family are governed by the value of TextElement.Foreground, TextElement.FontSize and TextElement.FontFamily attached dependency properties on a specific visual element.
Some controls, like Label explicitly set their Foreground to some brush. It happens so that the brush is one of the SystemColors. But it doesn't have to be true for all controls. Others (TextBlock, TextBox, etc.) don't override the property value and just use some default settings evaluated on startup. The same happens to FontSize and FontFamily. You do not need to set them wherever in order for them to work. That's how WPF works.
Supposedly, the values depend on the system theme. I believe they are evaluated during the app startup. Perhaps they are configurable.
UPDATE 2
Answers to your new questions:
How does a TextBlock get its default color, if the client app doesn't provide any style, either programmatically or through xaml?
It takes it from the inherited value of the TextElement.Foreground attached dependency property. By default it is inherited from the root visual element, which in turn is simply set to the default value of the dependency property (Brushes.Black). See also
How does a Label get its default color?
It takes it from the value of the TextElement.Foreground attached dependency property. Since its default style sets it to the {DynamicResource {x:Static SystemColors.ControlTextBrushKey}, it gets bound to the system color.
How does a TextBlock get its default font size and font family, if the client app doesn't provide any style, either programmatically or through xaml?
The same as for its text color. MSDN says that for the default value of the font size is SystemFonts.MessageFontSize which depends on system settings. Font family is determined in similar way from SystemFonts.MessageFontFamily.
Both these default values are passed to the FrameworkPropertyMetadata constructor upon dependency property registration in the TextElement static constructor.
Going deeper: SystemFonts.MessageFontFamily and SystemFonts.MessageFontSize wrap internal SystemParameters.NonClientMetrics which in turn are retrieved from the WIN32 native SystemParametersInfo http://msdn.microsoft.com/en-us/library/ms724947. Thus the WPF is tightly integrated with all Windows UI stuff like themes, fonts, etc.
How does a Label get its default font size and font family?
The same as for TextBlock. Label derives from ContentControl which in turn derives from Control. Control class adds itself as an owner of the TextElement.FontFamily and TextElement.FontSize properties with the same default values.
See also:
Property Value Inheritance
UPDATE 3
You should understand the main idea: the values are inherited. It means they might be inherited from anywhere, from any control. You can tell exactly which one it is inherited from only for a certain logical tree structure. You change it a bit - and the colors change. Someone sets a property's value explicitly - and all children will inherit the value. Therefore your questions make little practival sense. But they are still interesting from the perspective of undestanding the WPF.
Overriding default values
Although you cannot change the values of the SystemFonts properties (they are read-only), you don't have to. To change the font size and family for the whole window, simply assign the desired values to the TextElement attached properties on the Window:
<Window TextElement.FontSize="20" TextElement.FontFamily="Century Gothic">
..
</Window>
and all controls that do not explicitly override the inheritance will receive the settings. For those that do override - you'll have to override their default styles or even throw them away if they hard-code the values.
The same approach works for TextElement.Foreground (and Background and so on).
The default colors are pulled from the operating system's settings.
You can overwrite them by creating a brush which has the a key that references a SystemColors brush key
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Red"/>
According to this: http://msdn.microsoft.com/en-us/library/ms788718.aspx
By default, WPF uses the GlobalUserInterface.composite font in your Windows\Fonts directory.
And according to this: http://msdn.microsoft.com/en-us/library/system.windows.controls.textblock.fontsize(v=vs.95).aspx
A non-negative value that specifies the font size, measured in pixels. The default is 11.
In addition, you can find many of the other default values stored in various places in the MSDN site: http://msdn.microsoft.com/en-us/library/system.windows.controls.textblock.fontstyle(v=VS.95).aspx for the style, which says:
The requested font style, which is a FontStyle that is obtained from one of the FontStyles property values. The default is Normal.

XAML: Convert a Brush to a Color?

I am creating a custom control with two text colors, ColorA and ColorB. ColorA is wired to the Foreground property of the control, and ColorB is wired to a custom dependency property called ForegroundAlt. Both properties are Brush objects. The control's XAML gets the property values using this markup:
<SolidColorBrush x:Key="BrushA" Color="{Binding Path=Foreground, RelativeSource={RelativeSource TemplatedParent}}" />
<SolidColorBrush x:Key="BrushB" Color="{Binding Path=ForegroundAlt, RelativeSource={RelativeSource TemplatedParent}}" />
I need to animate sme text between the two colors in the control template, and that's where I am running into problems.
Normally, I would simply create a data binding to each Brush.Color property, like this:
To="{Binding Source={StaticResource BrushB}, Path=Color}"
But that won't work here. It turns out that you can't use bindings on an animation inside a control template.
As a workaround, I would like to create a pair of Color resources to go along with the Brush resources:
<Color x:Key="ColorA" ??? />
<Color x:Key="ColorB" ??? />
Each Color resource should have the color of its corresponding brush. I could then reference the colors as static resources, and avoid having to data bind from within the animation.
So, here are my questions:
-- How would I declare the Color resources?
-- Is there a simpler way to get the job done?
Thanks for your help.
If I've understood this correctly, what you are trying will not work. Even if you define the Colors as resources, you will still have to bind them to the brush resources and you are back to square one!
One solution is to do it in code behind rather than in the template. Since its a custom control you are building its should be pretty straightforward to add it in th code behind without screwing up the design.

WPF set named style elements from code behind?

I have a user control that applies a style to button, with the style containing a ControlTemplate section. Within the ControlTemplate, there are various UI elements such as an Ellipse and a Path.
If I give those elements -- the Ellipse and Path -- a name with x:Name, can I access them from code behind?
It appears the style's Ellipse and Path are not visible because I get a compile error (C#).
Am I going about this the wrong way?
Because a template can be instantiated multiple times, it's not possible to bind a generated member via x:Name. Instead, you have to find the named element within the template applied to a control.
Given simplified XAML:
<ControlTemplate x:Key="MyTemplate">
<Ellipse x:Name="MyEllipse" />
</ControlTemplate>
You would do something like this:
var template = (ControlTemplate)FindResource("MyTemplate");
template.FindName("MyEllipse", myControl);
Or even more simply:
var ellipse = (Ellipse)myControl.Template.FindName("MyEllipse", myControl);
You can read about FrameworkTemplate.FindName.
Some examples and discussion here, here and here.

Resources