ContentPresenter - HorizonalAlignment not working - c#

I have created a custom control for the simplified usage of label: control in forms:
My problem is that, no matter what is being put into my control content, I am unable to align it, using my template code. The only way it works is when I put HorizontalAlignment="Right" into the TextBox control directly:
<local:ControlEntry Label="Enter something">
<!-- I don't like having HorizontalAlignment here! -->
<TextBox Height="20" Width="200" HorizontalAlignment="Right" />
</local:ControlEntry>
However, I would rather want to do that behind the scenes in my control template.
I'm talking about this particular XAML code:
<Border x:Name="Container"
HorizontalAlignment="Stretch"
Grid.Column="1">
<ContentPresenter Width="{Binding ActualWidth, ElementName=Container}" HorizontalAlignment="Right" />
</Border>
First, I stretch the HorizontalAlignment, so what it would fill the width of the grid column cell, then I'm trying to apply the Right HorizontalAlignment on the ContentPresenter. Unfortunately, no effect is visible.
Full control code:
<Style TargetType="{x:Type local:ControlLine}">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ControlLine}">
<!-- Create bottom content border -->
<Border BorderBrush="#000" BorderThickness="0,0,0,0.5">
<StackPanel>
<Grid>
<!-- Label -> Control -->
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!-- Label -->
<TextBlock Grid.Column="0"
Margin="0,12,15,12"
VerticalAlignment="Center"
Text="{Binding Label, RelativeSource={RelativeSource TemplatedParent}}"
TextWrapping="WrapWithOverflow" />
<!-- Display content -->
<Border x:Name="Container"
HorizontalAlignment="Stretch"
Grid.Column="1">
<ContentPresenter Width="{Binding ActualWidth, ElementName=Container}" HorizontalAlignment="Right" />
</Border>
</Grid>
<!-- Description -->
<TextBlock Text="The control description" />
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
How can I properly keep the width stretched and at the same time align the content of ContentPresenter to right?

Related

Drag & Drap doesn't work with custom Window.Style

I have a simple UI made with wpf. In there is a Page containig a CustomControl with a TreeView. To reorder the TreeNodes i am using wpf dragdrop. This works pretty well so far. Now i'm playing around with WindowChrome to create a borderless window. But the Problem: Now Drag & Drop won't work. If i try to drag an object the curser changes into a "no valid drop position"
What i've found out: If i delete my custom evererything works. But i have now idea what is missing to get the drag and drop function working with my custom style.
The TreeView:
<TreeView x:Name="StructureTree"
Grid.Row="0"
Padding="10,20,20,0"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
ItemsSource="{Binding Path=RootElement.Children}"
Background="{x:Null}"
BorderBrush="{x:Null}"
dd:DragDrop.IsDragSource="true"
dd:DragDrop.IsDropTarget="true"
dd:DragDrop.UseDefaultDragAdorner="true">
The Window.Resources:
<Window.Resources>
<Style TargetType="{x:Type local:MainWindow}" BasedOn="{StaticResource {x:Type Window}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<!-- Outer border with the dropshadow margin -->
<Border Padding="{Binding OuterMargin, FallbackValue=10}">
<!-- Main window outline -->
<Grid>
<!-- Opacity mask for corners on grid -->
<Border x:Name="Container"
Background="{StaticResource BackgroundVeryLightBrush}"
CornerRadius="{Binding WindowCornerRadius}" />
<!-- Window border and dropshadown -->
<Border CornerRadius="{Binding WindowCornerRadius}"
Background="{Binding BackgroundVeryLightBrush}" BorderBrush="#FF1E1E1E">
<Border.Effect>
<DropShadowEffect ShadowDepth="2" Opacity="0.2" BlurRadius="5" />
</Border.Effect>
<Border.OpacityMask>
<VisualBrush Visual="{Binding ElementName=Container}" />
</Border.OpacityMask>
</Border>
<!-- The main window content -->
<Grid>
<!-- Corner clipping -->
<Grid.OpacityMask>
<VisualBrush Visual="{Binding ElementName=Container}" />
</Grid.OpacityMask>
<Grid.RowDefinitions>
<!-- Title Bar -->
<RowDefinition Height="{Binding TitleHeight, FallbackValue=42}"/>
<!-- Window Content -->
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- Title Bar -->
<Grid Grid.Column="0" Grid.Row="0" Panel.ZIndex="1">
<Grid.ColumnDefinitions>
<!-- Icon -->
<ColumnDefinition Width="Auto"/>
<!-- Title -->
<ColumnDefinition Width="*"/>
<!-- Window Buttons -->
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!-- Icon -->
<Button Margin="1" Padding="0" Style="{StaticResource IconButton}" WindowChrome.IsHitTestVisibleInChrome="True" Command="{Binding MenuCommand}">
<!--<Image Source="/Images/Logo/Icon.ico"/>-->
</Button>
<!-- Title -->
<Viewbox Grid.Column="0" Grid.ColumnSpan="3" Margin="0">
<TextBlock Style="{StaticResource TitleText}"
Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Title, FallbackValue= 'Wellcome'}"/>
</Viewbox>
<!-- Window Buttons -->
<StackPanel Grid.Column="2" Orientation="Horizontal">
<Button Command="{Binding MinimizeCommand}" Style="{StaticResource WindowControlButton}" Content="_"/>
<Button Command="{Binding MaximizeCommand}" Style="{StaticResource WindowControlButton}" Content="[ ]"/>
<Button Command="{Binding CloseCommand}" Style="{StaticResource WindowCloseButton}" Content="X"/>
</StackPanel>
</Grid>
<!-- Page Content -->
<Border Grid.Row="1" Padding="{Binding InnerContentPadding}">
<ContentPresenter Content="{TemplateBinding Content}"/>
</Border>
</Grid>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
When I was experimenting with a similar scenario I found that WPF won't allow to drop an item if it can't detect a control under the cursor. The solution to this may be to add a stretched rectangle "under" your TreeView with transparent filling:
<Rectangle
Fill="Transparent"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"/>
<TreeView x:Name="StructureTree"...
Just so a control could be "under" your cursor and then you should get back your valid drop area.
Edit:
If it doesn't want to work, try to put the rectangle before this part in the template:
<!-- Opacity mask for corners on grid -->
<Border x:Name="Container"
Background="{StaticResource BackgroundVeryLightBrush}"
CornerRadius="{Binding WindowCornerRadius}" />
And since the basic window template uses AdornerDecorator, it's worth a shot to include in your template too, where needed:
<AdornerDecorator>
<ContentPresenter.../>
</AdornerDecorator>

Binding within a template

I have the following ControlTemplate, as shown below, and I would like to avoid hard coding the hight and width of the image, instead I would like to bind the Height and width of the Image control
<Image Source="/Rotate.Pictures;component/Images/error.png"
Stretch="Uniform"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Height="14"
Width="14"/>
to the TextBlock's Text (TextBlock named "ErrorText") property path = "Height". Both Height and Width of the image should be bound to the Height value of the TextBlock's Text Height property
Does anyone know how I can accomplish this?
Template:
<Grid IsSharedSizeScope="True">
<Grid.Resources>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<StackPanel>
<AdornedElementPlaceholder>
<Border BorderBrush="Red" BorderThickness="2"/>
</AdornedElementPlaceholder>
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="/Rotate.Pictures;component/Images/error.png" Stretch="Uniform" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Height="14"
Width="14"/>
<TextBlock x:Name="ErrorText" Text="{Binding ErrorContent}" Foreground="Red"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
Key to the answer is to reference past controls (control that the thread of execution already encountered in the "{Binding ElementName=ErrorText, Path=ActualHeight}"). So the solution uses a grid, then specify Grid.Column 1 before specifying Grid.Column 0 that references the ElementName in Grid.Column 1.
<Style TargetType="{x:Type TextBox}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<StackPanel>
<AdornedElementPlaceholder>
<Border BorderBrush="Red" BorderThickness="2"/>
</AdornedElementPlaceholder>
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock x:Name="ErrorText" Grid.Column="1" Text="{Binding ErrorContent}" Foreground="Red"/>
<Image Grid.Column="0" Source="/Rotate.Pictures;component/Images/error.png"
Height="{Binding ElementName=ErrorText, Path=ActualHeight}"
Width="{Binding ElementName=ErrorText, Path=ActualHeight}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

XAML GridRow doesn't scale automatically when ErrorTemplate is displayed

I have a XAML window with a TextBox, and this TextBox has an ErrorTemplate.
The ErrorTemplate is shown below, and as you can see, I have an AdornedElementPlaceholder, followed by a textbox whose Text field is bound to the ErrorContent:
<ControlTemplate x:Key="ValidationErrorTemplateTextBlock" TargetType="{x:Type Control}">
<Border BorderBrush="Red" BorderThickness="1">
<StackPanel Orientation="Vertical">
<AdornedElementPlaceholder Name="AdornedElementPlaceholder" />
<TextBlock Text="{Binding ElementName=AdornedElementPlaceholder, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}"
FontSize="10"
Background="Red"
Foreground="White"
Padding="2" />
</StackPanel>
</Border>
</ControlTemplate>
<TextBox IsEnabled="{Binding SendMessage}"
Text="{Binding AutoMessageSubject, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource StyleBase}"
Validation.ErrorTemplate="{StaticResource ValidationErrorTemplateTextBlock}"
HorizontalAlignment="Stretch"
Grid.Row="3"
Grid.Column="1"
Grid.ColumnSpan="2" />
This works fine, except for one thing: the TextBox is inside a GridRow, with a Height="Auto". The row scales itself based on the textbox, but when the ErrorTemplate appears, with an extra TextBox on the bottom - the GridRow doesn't scale up to contain the new TextBox, and the new TextBox overlaps the elements below it.
How can I solve this?
Validation.ErrorTemplate: Gets or sets the ControlTemplate used to generate validation error feedback on the adorner layer.
This means that if you use Validation.ErrorTemplate, the validation errors are displayed on the layer above usual content, so the "second" TextBlock is displayed over the grid, not within the grid cell.
I would implement INotifyDataErrorInfo instead of semi-obsolete IDataErrorInfo, use a custom textbox style, and bind the visibility of the second TextBlock to HasErrors property. The example below uses a ToolTip instead of the second TextBlock:
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip DataContext="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget}">
<ItemsControl DisplayMemberPath="ErrorContent" ItemsSource="{Binding Path=(Validation.Errors)}" />
</ToolTip>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
You might want to try adding Row/Column Definitions:
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>

Right align panorama header

It seems that there should be a very easy solution to this but I cannot find it and it's killing me ...
All I wanna do is to add a right aligned refresh button (or image, or whatever) to the Header of a PanaromaItem in a Panorama, so pressing it reloads the data. (look at the image below)
I tried overriding the PanoramaItem.Header:
<phone:PanoramaItem.Header>
<Grid>
<TextBlock Text="first item" />
<Image source="blah" HorizontalAlignment="Right" />
</Grid>
</phone:PanoramaItem.Header>
and I tried using Grid, StackPanel, ViewPanel and every other layout controller that I knew and couldn't achieve this unless I set a constant width.
Any idea?
Might as well answer this one correctly, just incase someone needs it.
You need to override the PanoramaItemStyle and set the Header ContentControl to Stretch.
HorizontalContentAlignment="Stretch" HorizontalAlignment="Stretch"
That way when you apply the template it will cover the whole width. Here is the full Override Style Template.
<phone:PhoneApplicationPage.Resources>
<Style x:Key="Chubs_PanoramaItemStyle" TargetType="phone:PanoramaItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="phone:PanoramaItem">
<Grid Background="{TemplateBinding Background}" Margin="12,0,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ContentControl x:Name="header" CharacterSpacing="-35" ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" FontSize="66" FontFamily="{StaticResource PhoneFontFamilySemiLight}" Margin="12,-2,0,38" HorizontalContentAlignment="Stretch">
<ContentControl.RenderTransform>
<TranslateTransform x:Name="headerTransform"/>
</ContentControl.RenderTransform>
</ContentControl>
<ContentPresenter Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" Grid.Row="1" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</phone:PhoneApplicationPage.Resources>
Now apply that style to the PanoramaItem like so
<phone:PanoramaItem Style="{StaticResource Chubs_PanoramaItemStyle}">
<phone:PanoramaItem.Header>
<Grid>
<TextBlock Text="header1"/>
<Image Source="/Assets/ApplicationIcon.png" Stretch="None" HorizontalAlignment="Right"></Image>
</Grid>
</phone:PanoramaItem.Header>
</phone:PanoramaItem>
And Screenshot of the results:
By default when we set horizontal alignment to the image within panorama item header then it is not working But we can fix the width of the header textblock and set the alignment right for the image.
<controls:PanoramaItem.Header>
<Grid>
<TextBlock Text="First" Width="400"/>
<Image Source="/ApplicationIcon.png" Width="60" Height="60" HorizontalAlignment="Right" />
</Grid>
</controls:PanoramaItem.Header>
I tried horizontalalign="Right" is not working but to achive this you could set left margin:
<phone:PanoramaItem Header="My Books" Foreground="LightGray" Margin="-10,0,0,0">
<phone:PanoramaItem.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Foreground="#72C158" Text="{Binding Content, RelativeSource={RelativeSource TemplatedParent}}" Margin="0,30,0,0" FontSize="55" FontFamily="/Font/BKANT.TTF#Book Antiqua" TextAlignment="Left" FontWeight="Normal"/>
<Button Height="50" Width="50" Margin="130,20,0,0"/>
</StackPanel>
</DataTemplate>
</phone:PanoramaItem.HeaderTemplate>
</phone:PanoramaItem>
As described here, you will need to override the pivotitem template to set the HorizontalAlignment of the Header to Stretch.
After that you should be able to align stuff to the right of the header.

How to Affect TabPanel Template's Position based on TabStripPlacement Property WPF

I created a control template for all TabControl in my application. The template places the TabPanel at the left of the tab item's main content.
<Style TargetType="{x:Type TabControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TabPanel Grid.Column="0"
Panel.ZIndex="1"
Margin="0,0,0,0"
IsItemsHost="True"
Background="Transparent" />
<Border Grid.Column="1"
BorderBrush="Black"
BorderThickness="1"
CornerRadius="0,12,12,12">
<Border.Background>
<SolidColorBrush Color="Green" />
</Border.Background>
<ContentPresenter ContentSource="SelectedContent" />
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This is initially what I want my tab controls to look like. But when I set the TabStripPlacement property to Top, it still stays in the left side. Is there a way to define in the control template to adjust the position of TabPanel based on the TabStrip property? Thanks in advanced.
You need to use triggers in your style that react to the change in the TabStripPlacement property.
This stackoverflow post provides an example of this.

Categories