XAML : Set Label Margin inside a DataTemplate - wpf

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.

Related

How to "inject" style in a DataTemplate

I am trying to separate the DataTemplates from the Styles in my code.
I use DataTemplate to define, e.g. that the data should be displayed as two buttons and I use styles to define, e.g. that the background of those buttons should be green.
What I am trying to achieve is having a DataTemplate defined in one file and using that in multiple UserControls where the style comes from the UserControls.
Let's say I have the following style in the Resources of a UserControl:
<Style x:Key="ButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="Green"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
Another UserControl might have something similar with different colors.
Then, I have a ContentControl in that UserControl that will have some view model and some DataTemplate:
<ContentControl Content="{Binding SelectedViewModel}"
ContentTemplate="{Binding SelectedDataTemplate}"/>
The DataTemplate can be something as simple as this:
<DataTemplate x:Key="TwoButtonsTemplate">
<StackPanel>
<Button Content="One"/>
<Button Content="Two"/>
</StackPanel>
</DataTemplate>
I would like the two buttons to have the ButtonStyle from the UserControl.Resources without directly referencing it. (So the DataTemplate can come from a different file or being able to use the DataTemplate in a similar context with another UserControl's style).
I tried to change the TargetType of ButtonStyle to ContentControl, assign the style to the ContentControl and set Foreground="{TemplatedParent Foreground}" on the Buttons, but in this way both Foreground will change when any of them (i.e. the ContentControl itself) is hovered.
Is there any way of "inheriting" a style in the DataTemplate or "injecting" the style from the UserControl?
P.S. I understand if I move the style into a separate file and reference that in the DataTemplate file I can simply use it as StaticResource, but that will couple the DataTemplate to that specific style and I won't be able to re-use it with other styles.
try DynamicResource:
<DataTemplate x:Key="TwoButtonsTemplate">
<StackPanel>
<Button Content="One" Style="{DynamicResource ButtonStyle}"/>
<Button Content="Two" Style="{DynamicResource ButtonStyle}"/>
</StackPanel>
</DataTemplate>
when TwoButtonsTemplate template is instantiated in UserControl, which declares ButtonStyle resource, that resource will be found and applied to buttons.

Style is being overridden when value uses Binding

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.

Silverlight: Make all descendants of an element have a margin?

Is there a way in Silverlight 4 to dictate that all elements within a StackPanel must have a margin, instead of specifying margin="10,0" on each one?
I'm afraid it's not possible declaratively in XAML with the StackPanel directly. It's the conceptual philosophy in Silverlight/WPF that a panel should not modify properties of its children. So you could implement your own Panel that does so anyway, or you could use an ItemsControl like that:
<ItemsControl>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentPresenter Margin="10,0" Content="{Binding Content}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
[...]
</ItemsControl>
An ItemsControl uses a StackPanel by default, you can use its ItemsPanel property to define another Panel as an ItemsPanelTemplate if you wish so.
The way I'd do it is by defining implicit styles in the StackPanel's resources, for each control type that will be used within the StackPanel. To save defining the value repeatedly for each control type, you can create a named base style that targets FrameworkElement and defines the style(s), from which the style for each control type can inherit. An example is below:
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<Style x:Key="CommonStyle" TargetType="FrameworkElement">
<Setter Property="Margin" Value="10,0" />
</Style>
<Style TargetType="Button" BasedOn="{StaticResource CommonStyle}" />
<Style TargetType="TextBlock" BasedOn="{StaticResource CommonStyle}" />
<Style TargetType="CheckBox" BasedOn="{StaticResource CommonStyle}" />
</StackPanel.Resources>
<Button>Button</Button>
<TextBlock Text="Text" />
<CheckBox>Check Box</CheckBox>
</StackPanel>
Note how each control in the StackPanel will have the margin applied, without needing to define it on each control.
Hope this helps...
Chris Anderson
PS. Blatant self promotion - this is based upon the inheritance trick in my book Pro Business Applications with Silverlight 4 :).
Put your stackpanel within a Border element and set the Border Padding to "10 0"
You can also do this programmatically; your StackPanel has a Children collection. You could use this to iterate through them and set the margin.

Underline the implicit Textblock created in Silverlight for a ContentPresenter when Content is a string?

I am trying to create a template for a content control such as Button or HeaderedContentControl etc. where the text is underlined.
I just want to underline the text when Content="This text is underlined" is specified.
It must continue to work as normal if Content is another UIElement.
Most posts asking this same question are satisfied with modifying the template to only work for a string as content. Scott Gu has a good article about styling buttons but doesn't address this issue.
The following sample will work if you actually pass in Content as an instance of type TextBlock but not as a string. Surely the visual tree has a TextBlock so it should style it. Perhaps this is a Sivlerlight limitation.
This example shows black text and big red text when I want it to display both as big red text.
<navigation:Page.Resources>
<Style TargetType="TextBlock" x:Key="style123">
<Setter Property="Foreground" Value="Red"/>
<Setter Property="FontSize" Value="72"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="TextDecorations" Value="Underline"/>
</Style>
</navigation:Page.Resources>
<StackPanel>
<!-- This doesn't work and shows black text -->
<ContentPresenter Content="Small black text">
<ContentPresenter.Resources>
<Style TargetType="TextBlock" BasedOn="{StaticResource style123}"/>
</ContentPresenter.Resources>
</ContentPresenter>
<!-- This works and shows red text -->
<ContentPresenter>
<ContentPresenter.Content>
<TextBlock Text="This is big red text"/>
</ContentPresenter.Content>
<ContentPresenter.Resources>
<Style TargetType="TextBlock" BasedOn="{StaticResource style123}"/>
</ContentPresenter.Resources>
</ContentPresenter>
</StackPanel>
You could subclass whatever actual ContentControl (i.e. Button) you are using and override OnContentChanged in order to reset the Content property to an underlined TextBlock if the newContent is a string. In the case that the newContent is not a string it would perform in the usual way.
public class UnderlineButton : Button
{
protected override void OnContentChanged(object oldContent, object newContent)
{
if (newContent is string)
{
TextBlock textBlock = new TextBlock();
textBlock.Text = newContent as string;
textBlock.TextDecorations = TextDecorations.Underline;
this.Content = textBlock;
}
base.OnContentChanged(oldContent, newContent);
}
}
It's kind of annoying to subclass just to accomplish this but it avoids messy style templates and subclassing ContentPresenter.
Try this example, using a DataTemplate to custom-render string content (I've just set the background to red):
<ContentControl Content="{Binding YourData}" >
<ContentControl.Resources>
<DataTemplate DataType="{x:Type s:String}">
<TextBlock Text="{Binding}" Background="Red" />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
EDIT: just as a note, you could pull this out into a ContentControl style rather than applying it inline each time, if you need better reusability...

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