Style is being overridden when value uses Binding - wpf

I have two styles set in my UserControl.Resources
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="white" />
</Style>
<Style TargetType="{x:Type Label}">
<Setter Property="Foreground" Value="white" />
</Style>
So that in my DataTemplate (and note that I chopped the rest off) I will have white text applied without having to change the properties on each and every Label and TextBlock element.
<DataTemplate x:Key="FileTransferItemTemplate">
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<Label Content="Transferring With: " />
<TextBlock Text="{Binding Path=OtherUserName, Mode=TwoWay}" />
</StackPanel>
</DataTemplate>
What happens though(and this caused me a long nightmare where I thought I was databinding improperly because I couldn't see any changes), is when the data is bound the foreground color defaults to black. My databound text was black on a black background, and I didn't even realize for the longest time.
The only way I can override this is manually setting the Foreground="White" on the TextBlock. The Label works fine for the color, because it's not databound.
Why is this happening, and how can I fix it?

The problem is not related to binding. It just seems that the lookup of an externally defined default style from inside a DataTemplate only works for elements that are derived from Control. Since TextBlock isn't derived from Control, your default style is not found.
This page cites the following two statements given by Microsoft:
This behavior is 'By Design' and this is why. Templates are viewed as
an encapsulation boundary. Elements produced by these templates fall
within this boundary. And lookup for a style with a matching
TargetType stops at this boundary. Hence the TextBlock in the repro
which is produced through a template does not pick up the Style in
question. Whereas the TextBlock defined outside the template does.
One way to work around this problem is to give an explicit name to the
Style and reference the style by this name on the TextBlock within the
template.
and
Templates are viewed as an encapsulation boundary when looking up an
implicit style for an element which is not a subtype of Control.

Related

What is the exact effect of declaring a resource?

When I create a style using XAML:
<Style x:Key="tbxWithValidation" TargetType="TextBox">
<Style.Setters>
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource errorTemplate}"/>
...
</Style.Setters>
</Style>
what does mean this declaration? Do I create a class or an instance?
I assume I create an instance of class Style, but I wonder in this case if this instance will be used each time I use the resource.
In other words, does Style="{StaticResource tbxWithValidation}" reuse the same instance, or does it create a new instance so that each control has its own style instance?
I'm asking this question, because while this may be not important for a style (maybe controls can share the same style instance without problems, not sure...), it seems to me there would be a problem with declaring a control template and using it multiple times (this template is used in the style above):
<ControlTemplate x:Key="errorTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="0,0,5,0" Foreground="Red" FontWeight="Bold" Text="!"/>
<AdornedElementPlaceholder/>
</StackPanel>
</ControlTemplate>
Can you help me clarifying this (I'm learning WPF)?
<Style x:Key="TbxWithValidation" TargetType="TextBox">
<Style.Setters>
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource ErrorTemplate}"/>
...
</Style.Setters>
</Style>
This declaration instructs the XAML parser create an instance of the class Style.
In this case, the Style (or the resource in general) and all nested ResourceDictionary or referenced objects (and therefore also the the ErrorTemplate) is 'reused' for every reference to it.
This is because of the x:Shared attribute. This attribute is implicitly set and true by default. When true this attribute instructs the XAML parser to reuse the instance of the object. You can set it explicitly to false:
<Style x:Shared="False" x:Key="TbxWithValidation" TargetType="TextBox">
...
</Style>
By design each instance of an object is allowed to exist only once in the object graph. 'Reuse' or sharing (x:Shared="True") instructs the XAML parser to internally create a copy of the shared instances, so that the XAML parser can insert them in various places in the element tree. But since this are copies they are actually referencing the same shared instance.
An exception is made to all objects that extend UIElement. Instances of those objects can only exist once in the object graph: that's why elements like System.Windows.Controls.Image will magically disappear when used in multiple positions in the graph e.g. like when a single icon is used with multiple Button elements. In this case only one icon at time would be visible.
Instances of UIElement can not be shared (referenced by more than one resource instance) - they are unique. This means the author has to create the required number of instances explicitly.
This is why e.g. when creating a Button instance in C# (code-behind) and then add it to e.g. two different Grid elements the following exception is thrown:
Specified element is already the logical child of another element.
So, this means a shared resource like a Style is only critical when it declares UIElement objects (like Image) that are referenced in more than one resource:
<Style x:Key="SaveButton" TargetType="ButtonBase">
<Setter Property="Content">
<Setter.Value>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Save"/>
<Image Source="Resources\icons\save.png" />
</StackPanel>
</Setter.Value>
</Setter>
</Style>
Here the XAML parser will reuse the same instance of Style (x:Shared is true by default). To do so it creates copies. But the Image can't be copied: it will only appear on one Button.
To solve this you would have to mark the Style as not shared:
<Style x:Shared="False" x:Key="SaveButton" TargetType="ButtonBase">
<Setter Property="Content">
<Setter.Value>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Save"/>
<Image Source="Resources\icons\save.png" />
</StackPanel>
</Setter.Value>
</Setter>
</Style>
Now the XAML parser must create a new instance of the Style for each reference.
Something somehow similar applies to ControlTemplate. It is also shared by default but not its content.
For each reference to the ControlTemplate, new instances of the content elements are created.
Therefore declaring the content of the Button as ControlTemplate or ContentTemplate (instead of setting the Button.Content directly via a Style like above), would also solve the problem described above.
StaticResource does not mean that the resource is static. It's a markup extension that instructs the XAML parser to lookup the resource tree to find a predefined instance. Same does DynamicResource. The only difference is that StaticResource instructs the XAML parser to resolve the reference to a resource at compile time, whereas DynamicResource let's the XAML parser create an expression instead, that will be evaluated at run time (deferred). DynamicResource therefore resolves the resource at run time.

XAML : Set Label Margin inside a DataTemplate

I have a DataTemplate directly inside a Resource Dictionary. Inside the template is a label. The margin property isn't being applied how I expected (it has no effect)
<DataTemplate x:Key="HeaderContainerStyle">
<Label Margin="10" Text="{Binding}"/>
</DataTemplate>
And I can't solve the issue with a border as it appears its illegal (?)
<DataTemplate x:Key="HeaderContainerStyle">
<Border Padding="10">
<Label Text="{Binding}"/>
</Border>
</DataTemplate>
I get an error saying cannot resolve symbol Border.
When I try to add it in a ViewCell, application throws an exception:
System.ArgumentException: Value was an invalid value for HeaderTemplate
Parameter name: value
In Xamarin.Forms there is no Border class. Instead you should use Frame class which I think is equivalent for Border in WPF.
Like #Nick said, if you want to use DataTemplates in Xamarin.Forms you need to add ViewCell in DataTemplate and then next next element inside that (for example Grid, StackLayout or Label).
If it comes to Padding, in Xamarin.Forms its only applicable for layout (e.g. Grid, StackLayout) classes. Margin can be specified for view (e.g. Label, Button) and layout classes.
Getting back to your code basing on your HeaderContainerStyle I think you are trying to create style for Label, right?
To do that in Xamarin.Forms you should add new create new Style in ResourceDictionary for specific TargetType.
Example Style for Label class:
<ResourceDictionary>
<Style x:Key="labelRedStyle" TargetType="Label">
<Setter Property="HorizontalOptions"Value="Center" />
<Setter Property="VerticalOptions" Value="Center" />
<Setter Property="FontSize" Value="15" />
<Setter Property="TextColor" Value="Black" />
<Setter Property="Margin" Value="15,10" />
</Style>
<ResourceDicrionary>
And example usage:
<Grid>
<Label Style="myLabelStyle" />
</Grid>
Let me know if it helped! Waiting for more questions :)
I'm assuming this is Xamarin.Forms and not WPF but if your DataTemplate is used by a ListView it is missing a ViewCell.
<DataTemplate x:Key="HeaderContainerStyle">
<ViewCell>
<Label Margin="10" Text="{Binding}"/>
</ViewCell>
</DataTemplate>
However, if your DataTemplate is used in your own custom control that was created then the ViewCell may not be needed and another issue with that control may be the cause of why Margin is not working.

DataBinding inside a ContentTemplate inside a Style in Silverlight

I'm defining the following style in XAML:
<Style TargetType="telerik:RadDiagramShape" x:Key="styleShapeBase">
<Setter Property="Width" Value="120" />
<Setter Property="Height" Value="60" />
<Setter Property="IsResizingEnabled" Value="False" />
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock x:Name="lblName" Text="{Binding Name}" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
Then in the code-behind I'm assigning the data context. I want to draw a shape with some text in it that comes from an object (if this end up working I'm going to put more info there). I'm doing it like this:
var shape = new RadDiagramShape();
shape.Style = (Style)Resources["styleShapeBase"];
shape.DataContext = item.DataContext;
Where item is a simple POCO that has a Name property of type string (this part works, I've traced it, i.e. the DataContext is correctly assigned).
But the data binding never occurs. Is it by design (i.e. no data binding inside a content template), if not what's wrong? Thanks,
You can use Bindings in your DataTemplates. In this case, the Binding will look for a Name property on whatever you set as the Content of your RadDiagramShape.
You should ensure that your class has this property and that it is a string.
If that still doesn't work, can you post details of how you set the style and Content of each instance of RadDiagramShape, and of the object you are trying to bind to?
Somewhere in the Control Template for the RadDiagramShape class, there will be a ContentPresenter with its ContentTemplate bound to the one you have defined. The problem is that the ContentTemplate is only used if the Content property is also set. Otherwise nothing will be loaded into that ContentPresenter.
To make this work, you must set the Content property on the instance of this element.
This is a good place to start understanding what the DataContext property is

Scrollbars in WPF Tab Item Issues

I have a Tab Control that has a whole set of close able tab items, each tab item has View with a that has a scroll able section(each view is the same not same instance), the issue that I am having is that if you scroll on one tab its cascades to all the other tabs, I was wandering if someone can tell me how I can stop this from happening?
Thanks All :)
This is the default behavior if your TabControl specifies a ContentTemplate.TabControls use virtualization, so they will re-use the template when you switch tabs instead of creating a new one each time.
This means the same ScrollViewer is being used regardless of which tab you are on. You can prove this by adding a Loaded event to your ScrollViewer and you'll see it only gets called once.
One way around this is to use a DataTemplate that has x:Shared="False", so it won't share the template. I have not tested to see if there are any performance issues with this.
<DataTemplate x:Key="TestTemplate" x:Shared="False">
<local:UserControl1 />
</DataTemplate>
<Style x:Key="TabItemStyle" TargetType="{x:Type TabItem}">
<Setter Property="Header" Value="Test" />
<Setter Property="ContentTemplate" Value="{StaticResource TestTemplate}" />
</Style>
...
<TabControl ItemsSource="{Binding SomeCollection}"
ItemContainerStyle="{StaticResource TabItemStyle}" />
Note that this seems to be very fussy... for example I need to put my ScrollViewer in a UserControl or it won't work. I also need to set TabItem.ContentTemplate instead of TabControl.ContentTemplate.

Can my WPF Style Setter use a TemplateBinding?

I'm trying to do something like this...
<Style
x:Key="MyBorderStyle"
TargetType="Border">
<Setter
Property="Padding"
Value="{TemplateBinding Padding}" />
</Style>
...but I get the error:
'Padding' member is not valid because it does not have a qualifying type name.
How do I provide a "qualifying type name"?
Note: The reason I'm trying to do this, is that I'd like to include the same Border in a series of similar ControlTemplates.
I also tried this:
<Setter
Property="Padding"
Value="{TemplateBinding GridViewColumnHeader.Padding}" />
...and it actually compiled, but then when I ran the app, I got a XamlParseException:
Cannot convert the value in attribute 'Value' to object of type ''.
I thought maybe qualifying Padding with GridViewColumnHeader (which is the ControlTemplate I want to use this style with) would work, but no dice.
EDIT:
Well, according to the documentation for TemplateBinding, it says:
Links the value of a property in a control template to be the value of some other exposed property on the templated control.
So it sounds like what I'm trying to do is just plain impossible. I really would like to be able create reusable styles for certain controls in my control templates, but I guess the template bindings cannot be included in these styles.
TemplateBinding should work for the case where you're templating a control and you want to bind the value of a property of that control to a property of a different control inside the template. In your case you're templating something (call it MyControl), and that template will include a border whose Padding should be bound to MyControl's padding.
From MSDN documentation:
A TemplateBinding is an optimized form of a Binding for template scenarios, analogous to a Binding constructed with {Binding RelativeSource={RelativeSource TemplatedParent}}.
But for whatever reason, specifying TemplatedParent as the source for the binding doesn't seem to work within Style Setters. To get around that you can specify the relative parent to be an AncestorType of the control you're templating (which effectively finds the TemplatedParent providing you haven't embedded other MyControls in the MyControl template).
I used this solution when I was trying to custom template a Button control in which the (String) Content of the Button needed to be bound to the Text property of a TextBlock in the ControlTemplate for the button. Here's what that code looked like:
<StackPanel>
<StackPanel.Resources>
<ControlTemplate x:Key="BarButton" TargetType="{x:Type Button}">
<ControlTemplate.Resources>
<Style TargetType="TextBlock" x:Key="ButtonLabel">
<Setter Property="Text" Value="{Binding Path=Content, RelativeSource={RelativeSource AncestorType={x:Type Button}} }" />
</Style>
</ControlTemplate.Resources>
<Grid>
<!-- Other controls here -->
<TextBlock Name="LabelText" Style="{StaticResource ButtonLabel}" />
</Grid>
</ControlTemplate>
</StackPanel.Resources>
<Button Width="100" Content="Label Text Here" Template="{StaticResource BarButton}" />
</StackPanel>
The {TemplateBinding ...} shortcut is not available in a Setter.
But nobody will stop you using the full verbose version such as:
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Padding}".
A property can be qualified simply by prefixing it with the type name. For example, Border.Padding instead of Padding.
However, I'm not sure it makes sense for your scenario. TemplateBindings are used inside a control template.

Resources