I have a WPF user control which contains a TextBox and some buttons. Now I want to change the way in which the VerticalContentAlignment property of the user control is handled in order to change only the vertical content alignment of the contained text box and not of the user control itself. So how can I override the VerticalContentAlignment dependency property of the user control in order to achieve my desired behavior?
You could write a ControlTemplate for the UserControl that simply ignores the VerticalContentAlignment property. Then bind the TextBox's VerticalAlignment to the VerticalContentAlignment property of the UserControl, e.g. by a RelativeSource Binding.
<UserControl ...>
<UserControl.Template>
<ControlTemplate TargetType="UserControl">
<ContentPresenter
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>
</ControlTemplate>
</UserControl.Template>
<Grid Background="AliceBlue">
<TextBlock
Text="Hello"
VerticalAlignment="{Binding VerticalContentAlignment,
RelativeSource={RelativeSource AncestorType=UserControl}}"/>
</Grid>
</UserControl>
Test case:
<Grid>
<local:MyUserControl
Height="100"
VerticalAlignment="Center"
VerticalContentAlignment="Bottom"/>
</Grid>
Related
I got started learning Universal Windows Apps. For my demo application, I am trying to change the placeholder foreground of a TextBox control. To set its color, I set a resource key called TextControlPlaceholderForeground (as per this documentation) on the same XAML page definition that contains the control. However, the placeholder color of TextBox is not set. It looks as if no placeholder text is set when the control is not in a focused state. When it receives focus, the placeholder becomes visible, but still not the color that is set to it. Here is my XAML:
<Page
x:Class="MyApp.AuthenticationPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.Resources>
<SolidColorBrush x:Key="TextControlPlaceholderForeground" Color="Green" />
</Page.Resources>
<Grid>
<Border
HorizontalAlignment="Center"
VerticalAlignment="Bottom"
Margin="0,0,0,10"
Background="White"
Padding="10"
CornerRadius="10"
Width="300"
>
<StackPanel Orientation="Vertical">
<!-- The placeholder of the TextBox below doesn't become green -->
<TextBox x:Name="emailInput" PlaceholderText="Email" Margin="0,0,0,10" />
<!-- However, the placeholder of this PasswordBox becomes green -->
<PasswordBox x:Name="passwordInput" PlaceholderText="Password" Margin="0,0,0,10" />
<Button HorizontalAlignment="Stretch" Content="LOG IN" Click="onLoginSubmit" Style="{StaticResource AccentButtonStyle}"/>
<HyperlinkButton FontSize="11" Foreground="Black" Content="Forgot your password?" HorizontalAlignment="Center" />
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<TextBlock Text="Don't have an account yet?" Foreground="Black" FontSize="11" Padding="0,0,5,0"/>
<HyperlinkButton Padding="0" FontWeight="Normal" >
<TextBlock Text="Create new account" Foreground="Black" TextDecorations="Underline" FontSize="11"/>
</HyperlinkButton>
</StackPanel>
</StackPanel>
</Border>
</Grid>
</Page>
An observation: When I try to set TextBox's placeholder text color via Style tags, it works.
<Style x:Key="TextBoxStyle" TargetType="TextBox">
<Setter Property="PlaceholderForeground" Value="Green" />
</Style>
I didn't understand what happens here. I know I am missing something very small but couldn't see it.
First, let me explain why the second approach works. There is a little bit of difference between the PasswordBox and the TextBox about how they defined the Placeholder Text color. Both of the PasswordBox and the TextBox has a PlaceholderTextContentPresenter element in their styles which is a TextBlock. But they have different value for the Foreground property when using this TextBlock.
TextBox:
<TextBlock x:Name="PlaceholderTextContentPresenter" Foreground="{Binding PlaceholderForeground, RelativeSource={RelativeSource Mode=TemplatedParent}, TargetNullValue={ThemeResource TextControlPlaceholderForeground}}"
PasswordBox
<TextBlock x:Name="PlaceholderTextContentPresenter" Foreground="{ThemeResource TextControlPlaceholderForeground}"
You could see that the PasswordBox is directly using the TextControlPlaceholderForeground that you defined as the color but the TextBox is using a binding and the binding source is the PlaceholderForeground property. So when you set this property, the TextBox will show the color as you want.
So back to the first question, if you want to change the foreground of the Placeholder Text, you will need to create a default style of the TextBox and change the binding of the Foreground of the PlaceholderTextContentPresenter element to use the TextControlPlaceholderForegrounddirectly like what the **PasswordBox ** did.
I have a Windows Store application that ueses datatemplates and datatemplateselectors to style various content for display.
I have a TemplateSelector that returns a Template based an parameters. This template also uses a contentpresenter which itself has a TemplateSelector.
I have tried calling the inner TemplateSelector in this ways:
<DataTemplate x:Key="Template1">
<Grid>
<Border Background="{StaticResource ApplicationBackgroundBrush}">
<Grid>
<ContentPresenter Content="{TemplateBinding DataContext}">
<ContentPresenter.ContentTemplateSelector>
<my:MyTemplateSelector />
</ContentPresenter.ContentTemplateSelector>
</ContentPresenter>
</Grid>
</Border>
</Grid>
Using the TemplateBinding, i get an exception that the DataContext Property cannot be found (DependencyProperty cannot be created from string).
My first attempt was this:
<DataTemplate x:Key="Template2">
<Grid>
<Border Background="{StaticResource ApplicationBackgroundBrush}">
<Grid>
<ContentPresenter Content="{Binding DataContext}">
<ContentPresenter.ContentTemplateSelector>
<my:MyTemplateSelector />
</ContentPresenter.ContentTemplateSelector>
</ContentPresenter>
</Grid>
</Border>
</Grid>
The problem with this is that the Content and DataContext are null when the TemplateSelector gets called. How can i solve this problem?
Try this, I had a similar problem because I missed the Property Keyword. Hope it applies to your situation.
Content={TemplateBinding Property=DataContext}
It works if you use ContentControl instead of ContentPresenter.
I have a ContentControl on which I have a grid. Column 1 will be a TreeView, Column 2 is a splitter and I have set column 3 to be a ContentPresenter. Defined as such:
<ContentPresenter Grid.Column="2" Name="PlaceHolder" />
If I then add my ContentControl to a Page and add an item to the ContentControl it does not just appear in column 2 but instead spans the entire width of the ContentControl. This is how I add something to the ContentPresenter:
<MyNamespace:MyControlName>
<Button>Hello world!</Button>
</MyNamespace:MyControlName>
How can I get the button to only appear in column 2 and have the TreeView in column 0 appear as normal?
Thanks in advance.
You are setting your ContentControl.Content property in the ContentControl to your Grid, then when you use the control you are overwriting ContentControl.Content to set it as a Button instead
You need to set your Grid to be the ContentControl.ContentTemplate instead of the actual Content, and modify your ContentPresenter show the Content property
<ContentControl ...>
<ContentControl.Template>
<ControlTemplate TargetType="{x:Type ContentControl}">
<Grid>
...
<ContentPresenter Grid.Column="2" Name="PlaceHolder"
Content="{TemplateBinding Content}" />
...
</Grid>
</ControlTemplate>
</ContentControl.Template>
</ContentControl>
What is the difference between
ControlTemplate
DataTemplate
HierarchalDataTemplate
ItemTemplate
Control Template
A ControlTemplate specifies the visual structure and visual behavior of a control. You can customize the appearance of a control by giving it a new ControlTemplate. When you create a ControlTemplate, you replace the appearance of an existing control without changing its functionality. For example, you can make the buttons in your application round rather than the default square shape, but the button will still raise the Click event.
An Example of ControlTemplate would be
Creating a Button
<Button Style="{StaticResource newTemplate}"
Background="Navy"
Foreground="White"
FontSize="14"
Content="Button1"/>
ControlTemplate for Button
<Style TargetType="Button" x:Key="newTemplate">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="RootElement">
<!--Create the SolidColorBrush for the Background
as an object elemment and give it a name so
it can be referred to elsewhere in the control template.-->
<Border.Background>
<SolidColorBrush x:Name="BorderBrush" Color="Black"/>
</Border.Background>
<!--Create a border that has a different color by adding smaller grid.
The background of this grid is specificied by the button's Background
property.-->
<Grid Margin="4" Background="{TemplateBinding Background}">
<!--Use a ContentPresenter to display the Content of
the Button.-->
<ContentPresenter
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Margin="4,5,4,4" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
More about ControlTemplate
Data Templates
Data Template are a similar concept as Control Templates. They give you a very flexible and powerful solution to replace the visual appearance of a data item in a control like ListBox, ComboBox or ListView. WPF controls have built-in functionality to support the customization of data presentation.
An Example for the DataTemplate would be
<!-- Without DataTemplate -->
<ListBox ItemsSource="{Binding}" />
<!-- With DataTemplate -->
<ListBox ItemsSource="{Binding}" BorderBrush="Transparent"
Grid.IsSharedSizeScope="True"
HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Key" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
<TextBox Grid.Column="1" Text="{Binding Value }" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
More about DataTemplates and Triggers
Item Templates
You use the ItemTemplate to specify the visualization of the data objects. If your ItemsControl is bound to a collection object and you do not provide specific display instructions using a DataTemplate, the resulting UI of each item is a string representation of each object in the underlying collection.
An Example for Item Template would be
<ListBox Margin="10" Name="lvDataBinding">
<ListBox.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="Name: " />
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
<TextBlock Text=", " />
<TextBlock Text="Age: " />
<TextBlock Text="{Binding Age}" FontWeight="Bold" />
<TextBlock Text=" (" />
<TextBlock Text="{Binding Mail}" TextDecorations="Underline" Foreground="Blue" Cursor="Hand" />
<TextBlock Text=")" />
</WrapPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
When you set an ItemTemplate on an ItemsControl, the UI is generated as follows (using the ListBox as an example):
During content generation, the ItemsPanel initiates a request for the ItemContainerGenerator to create a container for each data item. For ListBox, the container is a ListBoxItem. The generator calls back into the ItemsControl to prepare the container.
Part of the preparation involves the copying of the ItemTemplate of the ListBox to be the ContentTemplate of the ListBoxItem.
Similar to all ContentControl types, the ControlTemplate of a ListBoxItem contains a ContentPresenter. When the template is applied, it creates a ContentPresenter whose ContentTemplate is bound to the ContentTemplate of the ListBoxItem.
Finally, the ContentPresenter applies that ContentTemplate to itself, and that creates the UI.
If you have more than one DataTemplate defined and you want to supply logic to programmatically choose and apply a DataTemplate, use the ItemTemplateSelector property.
The ItemsControl provides great flexibility for visual customization and provides many styling and templating properties. Use the ItemContainerStyle property or the ItemContainerStyleSelector property to set a style to affect the appearance of the elements that contain the data items. For example, for ListBox, the generated containers are ListBoxItem controls; for ComboBox, they are ComboBoxItem controls. To affect the layout of the items, use the ItemsPanel property. If you are using grouping on your control, you can use the GroupStyle or GroupStyleSelector property.
For more information, see Data Templating Overview.
ControlTemplaes defines the "look" and the "behavour" of a control. A button is rectangular by default. A ListBox has a white background by default. These are all defineed by Control's ControlTemple.
A DataTemplae helps a Control with Layout of Data that it holds. If a list of Users are added to listbox and you would like UserName to show up before UserPassword then you will define this inside a DataTemples. DataTemples is assigned to the ItemTemplate (4) Property of the ListBox.
HierarchalDataTemplte is same as DataTemples except that it deal with Hierarchal Data Source. It is commonlly used with TreeView Control.
I'm trying to have a custom control that requires 2 or more areas of the XAML to be defined by a child control - that inherits from this control. I'm wondering if there's a way to define multiple contentpresenters and one which acts as the default content presenter
<MyControl>
<MyControl.MyContentPresenter2>
<Button Content="I am inside the second content presenter!"/>
</MyControl.MyContentPresenter2>
<Button Content="I am inside default content presenter" />
</MyControl>
Is this possible, how do I define this in the custom control's template?
The template can just bind the separate ContentPresenter instances like this (I've only set one property here but you'll likely want to set others):
<ContentPresenter Content="{TemplateBinding Content1}"/>
<ContentPresenter Content="{TemplateBinding Content2}"/>
The control itself should expose two properties for content and set the default using the ContentPropertyAttribute:
[ContentProperty("Content1")]
public class MyControl : Control
{
// dependency properties for Content1 and Content2
// you might also want Content1Template, Content2Template, Content1TemplateSelector, Content2TemplateSelector
}
You can use an "ItemsControl" with a custom template.
<ItemsControl>
<ItemsControl.Style>
<Style TargetType="ItemsControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<StackPanel Orientation="Horizontal">
<ContentControl Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Items[0]}"/>
<ContentControl Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Items[1]}"/>
<ContentControl Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Items[2]}"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ItemsControl.Style>
<TextBlock Text="Item 1"/>
<TextBlock Text="Item 2"/>
<TextBlock Text="Item 3"/>
</ItemsControl>
Here's another option that doesn't require making a custom control and is more typesafe than doing the ItemsControl thing (if type safety is something you want..perhaps not):
...Use an attached property!
Create an attached property of the appropriate type. We happened to need a text control so I did a string TextContent attached property. Then create a TemplateBinding to it from the template, and when instantiating in Xaml set it there as well. Works nicely.