Custom validation error message overlapped with other control - wpf

I have create custom validator and error template. Error template is below.
<ControlTemplate x:Key="errorTmp">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
</Grid.RowDefinitions>
<Border Grid.Row="1" BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder x:Name="Adorner"/>
</Border>
<TextBlock Grid.Row="0" Foreground="Red" Text="{Binding ElementName=Adorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}" Margin="0,0,0,5"></TextBlock>
</Grid>
</ControlTemplate>
problem is error message is getting overlapped on other control.

You need to reserve space for the element in the adorner layer. You could do this by increasing the Margin property of the TextBox control itself when its Validation.HasError property returns true.
In this case you could set the top margin of the TextBox to the same height as the first row of the Grid in your Validation.ErrorTemplate:
<TextBox />
<TextBox Text="{Binding Username, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}">
<Validation.ErrorTemplate>
<ControlTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
</Grid.RowDefinitions>
<Border Grid.Row="1" BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder x:Name="Adorner"/>
</Border>
<TextBlock Grid.Row="0" Foreground="Red" Text="{Binding ErrorContent}"></TextBlock>
</Grid>
</ControlTemplate>
</Validation.ErrorTemplate>
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<!-- increase the top margin of the TextBox in order for the error content not to overlap the control above -->
<Setter Property="Margin" Value="0 20 0 0" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
Since the rendering of an adorner is independent from rendering of the UIElement that the adorner is bound to, there is no way for the TextBox to automatically adjusts its position when adorner is visible. That's why you have to reserve the space for the adorner element explicitly yourself.

Another possible way is to directly include another element for error displaying and set its visibility based on HasErrors property, therefore positions of other elements could be automatically adjusted.
<TextBlock x:Name="TextBlockDate" DockPanel.Dock="Left" Text="{Binding Data}"/>
<TextBlock Foreground="Red" Text="{Binding ElementName=TextBlockData, Path=(Validation.Errors)[0].ErrorContent}" Visibility="{Binding Path=HasErrors, Converter={StaticResource BooleanToVisibilityConverter}}"/>

Related

Nested ContentControls with template

I have a custom WindowStyle, the XAML looks like this:
<Style TargetType="{x:Type Window}"
x:Key="WindowStyle">
/** Some setters **/
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ContentControl">
<AdornerDecorator>
<Grid Background="#88000000"
x:Name="WindowBackgroundGrid">
<Border x:Name="WindowContentBorder"
Background="{DynamicResource WindowBackground}"MaxHeight="{Binding Source={x:Static SystemParameters.FullPrimaryScreenHeight}}"
MaxWidth="{Binding Source={x:Static SystemParameters.FullPrimaryScreenWidth}}"
Margin="20">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- Header -->
<Border BorderBrush="{DynamicResource BorderBrushColor}"
Background="{DynamicResource PaneHeader_Background}"
Grid.Row="0">
<TextBlock Text="Title"Foreground="{DynamicResource DefaultForeground}"
FontSize="16"
FontWeight="Bold"
Margin="5,5,2,5" />
</Border>
<!-- Content -->
<ScrollViewer Grid.Row="1"
Margin="5">
<ContentPresenter Content="{TemplateBinding Content}" />
</ScrollViewer>
</Grid>
</Border>
</Grid>
</AdornerDecorator>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Now I want the inner Grid in a seperate Style so that I can use it elsewhere.
<Style x:Key="WindowContentStyle"
TargetType="{x:Type ContentPresenter}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- Header -->
/** Border control **/
<!-- Content -->
<ScrollViewer Grid.Row="1"
Margin="5">
<ContentPresenter Content="{Binding Content, RelativeSource={RelativeSource TemplatedParent}}" />
</ScrollViewer>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
And I use a ContenPresenter in my WindowStyle to present it:
<ContentPresenter>
<ContentPresenter.Style>
<Style TargetType="{x:Type ContentPresenter}"
BasedOn="{StaticResource WindowContentStyle}" />
</ContentPresenter.Style>
</ContentPresenter>
Problem
The edit above didn't give me any errors, but it doesn't present my WindowContentStyle.
When I set the Content property of the Window control and load the style
this.window.Content = view;
this.window.Style = (Style)Application.Current.TryFindResource("WindowStyle");
the content is shown in the ContentPresenter in the WindowStyle and not in WindowContentStyle. Because of this, the Template is not used and I don't have a header with a title.
How can I make my outer ContentPresenter pass on the Content to my inner ContentPresenter (the one in WindowContentStyle)?
Thanks in advance!
Greetings
Loetn
You should use a ContentControl to display your content, not a ContentPresenter. From the ContentPresenter Class page on MSDN:
You typically use the ContentPresenter in the ControlTemplate of a ContentControl to specify where the content is to be added.
From the ContentControl Class page on MSDN:
A ContentControl has a limited default style. If you want to enhance the appearance of the control, you can create a new DataTemplate.

Binding to the UserControl Inside HierarchicalDataTemplate in Silverlight

I have customized TreeView in usercontrol.
In the HierarchicalDataTemplate I have an image with direction (arrow for the example).
When the application\UserControl flow direction change I need to flip the image.
So i tried binding the Image.FlowDirection (Which is flipping the image automatic when RightToLeft) to the UserControl.FlowDirection
<Image FlowDirection="{Binding Path=FlowDirection,
ElementName=MyUserControl}" ... />
But it is not working. I guess because he can't find the UserControl from inside the template.
I've tried this binding outside the template - and it's working fine.
Any solution ?
How can I get my UserControl from inside the template ?
--UPDATE--
There are two places of binding in this xaml. The first is in the style of the button, and it is working, and the second in the template of the TreeViewItem - and there it's not working.
<UserControl x:Class="MyTree"
...
d:DesignHeight="218" d:DesignWidth="284" x:Name="MyLayerListControl">
<UserControl.Resources>
<Style x:Key="CloseButtonStyle" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<VisualStateManager.VisualStateGroups>
...
</VisualStateManager.VisualStateGroups>
<Border x:Name="CloseButtonBorder" BorderThickness="0" Margin="3" CornerRadius="0" Background="Gray" >
<!-- THIS BINDING IS WORKING -->
<Image Source="{StaticResource back}" Margin="2"
FlowDirection="{Binding Path=FlowDirection, ElementName=MyLayerListControl}"/>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="Transparent">
<Button Style="{StaticResource CloseButtonStyle}" />
<Grid>
<Grid.ColumnDefinitions></Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Grid.Resources>
<sdk:HierarchicalDataTemplate x:Key="LayerListTemplate" ItemsSource="{Binding Path=Childrens}" >
<Grid>
<Border CornerRadius="2" BorderBrush="#FF6DBDD1" BorderThickness="1" Background="#FFBADDE9" Opacity="0.5" />
<StackPanel Orientation="Vertical">
<!-- The Item -->
<StackPanel Orientation="Horizontal" Margin="3">
<!-- THIS BINDING IS NOT WORKING -->
<Image x:Name="ArrowImage" Width="16" Height="16" Source="{StaticResource arrow}"
FlowDirection="{Binding Path=FlowDirection, ElementName=MyLayerListControl}"/>
</StackPanel>
</StackPanel>
</Grid>
</sdk:HierarchicalDataTemplate>
</Grid.Resources>
<sdk:TreeView ItemsSource="{Binding}" ItemTemplate="{StaticResource LayerListTemplate}" x:Name="MyTreeView" Grid.Row="1" />
</Grid>
</Grid>
</UserControl>
As #Chris says, add "DataContext" in front of you binding path, as you are binding to the Control itself, so if FlowDirection is a property of the DataContext, you need to have that.
Also, make sure you have Mode=TwoWay in your binding.
As an alternative, you can you use RelativeSource so you don't have to hard code an ElementName.
Check out: http://tonychampion.net/blog/index.php/2011/12/7th-day-of-silverlight-ancestor-relative-source-binding/

How to show the scrollbar in expander when content overflows the window

I'm using two expander with TextBox one after another. During writing text the TextBox dynamically changes height. When size of TextBox is higher then parent window the scrollbar isn't shown. Here is example:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="150" Width="150">
<Grid Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="*" Name="GridRow1"></RowDefinition>
<RowDefinition Height="*" Name="GridRow2"></RowDefinition>
</Grid.RowDefinitions>
<Expander Grid.Row="0">
<TextBox TextWrapping="Wrap" VerticalScrollBarVisibility="Auto"/>
</Expander>
<Expander Grid.Row="1">
<TextBox TextWrapping="Wrap" VerticalScrollBarVisibility="Auto"/>
</Expander>
</Grid>
</Window>
I need to set max height of expander to half size of parent window (window is resizable). The scrollbar should be displayed if the text is longer than half size of the window. Other, when both expander are closed they should be close to each other on the top.
Scrollbar works well when in row definition is asterisk (*) but closed expander aren't not together at the top.
Apply your requirement in a Style DataTrigger for RowDefinition
<Grid Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Name="GridRow1">
<RowDefinition.Style>
<Style TargetType="{x:Type RowDefinition}">
<Setter Property="Height"
Value="*" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=expanderOne,
Path=IsExpanded}"
Value="False">
<Setter Property="Height"
Value="Auto" />
</DataTrigger>
</Style.Triggers>
</Style>
</RowDefinition.Style>
</RowDefinition>
<RowDefinition Name="GridRow2"
Height="*" />
</Grid.RowDefinitions>
<Expander x:Name="expanderOne"
Grid.Row="0">
<TextBox TextWrapping="Wrap"
VerticalScrollBarVisibility="Auto" />
</Expander>
<Expander Grid.Row="1">
<TextBox TextWrapping="Wrap"
VerticalScrollBarVisibility="Auto" />
</Expander>
</Grid>

Show "pop up window" when is mouser over listBox item

I bind observable collection on listBox. I have data tempate on listbox item. It consit one image control and som textBlock.
If is mouse over on some listBox item I would like achieve this behavior:
Show PopUp/ToolTip (some "rectangle" with controls) and bind values from listBox current item.
And on textBox in item data template I have style, I would like change color of text in textBlock, for example from black to green.
Style is here:
<Style x:Key="FriedNickStyle" TargetType="TextBlock">
<Setter Property="Margin" Value="2,2,2,2"/>
<Setter Property="FontSize" Value="13"/>
<Setter Property="FontWeight" Value="Medium"/>
<Setter Property="Foreground" Value="Black"/>
</Style>
Sory for my english, I have problem how describe this behavior correct. I try many thing but any of them doesn’t work good.
Here is it my style:
<DataTemplate x:Key="FriendListBoxItemTemplate">
<Grid Name="RootLayout">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.3*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="60"></RowDefinition>
</Grid.RowDefinitions>
<Image Margin="4,4,4,2" Grid.Column="0">
<Image.Source >
<MultiBinding Converter="{StaticResource avatarConverter}">
<Binding Path="ProfilePhoto"></Binding>
<Binding Path="StatusInfo.IsLogged"></Binding>
</MultiBinding>
</Image.Source>
</Image>
<Grid Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock
Text="{Binding Path=Nick}"
Style="{StaticResource FriedNickStyle}"
Grid.Column="0" Grid.Row="0">
</TextBlock>
</Grid>
</Grid>
<DataTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<!--SHOW SOME POP UP WINDOW and bind properties from ITEM (VALUE)-->
<!--Change color of textBlock-->
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
Thank everybody who help me.
Well, I found this turorial, this article, by the MSDN and another stack overflow's question.
Basically, here's how:
<Popup Margin="10,10,0,13"
Name="Popup1"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="194"
Height="200"
IsOpen="True"> // change this to open it
<TextBlock Name="McTextBlock" Background="LightBlue" >
This is popup text
</TextBlock>

How do I Show/Hide a Grid Row and Grid Splitter based on a Toggle Button?

Currently I have a toggle button that is bound to a boolean property (DualLayout) in my code behind. When the boolean is set to True, then I want my second row in my grid (and grid splitter) to hide and have the first row take up the entire space of the grid. Once the boolean is set to False, I want the grid splitter and bottom row to appear.
Here is a snippet of my xaml
<ToggleButton Name="toggleLayout" Margin="66,1,0,1" Width="25" HorizontalAlignment="Left" IsChecked="{Binding DualLayout}" Checked="toggleLayout_Clicked" Unchecked="toggleLayout_Clicked">
<ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}">
<Style.Triggers>
<Trigger Property="IsChecked" Value="true">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type ToggleButton}">
<Image Source="Images/PlayHS.png"/>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="ToolTip" Value="Receive and Transmit Windows Split."/>
</Trigger>
<Trigger Property="IsChecked" Value="false">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type ToggleButton}">
<Image Source="Images/PauseHS.png"/>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="ToolTip" Value="Receive and Transmit Windows Combined."/>
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
<Grid x:Name="transmissionsGrid" Margin="0,28,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*" MinHeight="100" />
</Grid.RowDefinitions>
<transmission:TransmissionsControl x:Name="transmissionsReceive" TransmissionType="Receive" Margin="0,0,0,5" />
<GridSplitter Name="gridSplitter1" Grid.Row="0" Background="White" Cursor="SizeNS" Height="4" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Foreground="Firebrick" />
<transmission:TransmissionsControl x:Name="transmissionsTransmit" TransmissionType="Transmit" Grid.Row="1" />
</Grid>
This is untested, but I believe it should work.
First, if you want your first row to take up the whole space, you'll want to define your RowDefinitions as
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto" /> <!-- Edit: Removed MinHeight="100" -->
</Grid.RowDefinitions>
For showing/hiding the controls, you'll need to bind their Visibility property either to your DualLayout property (if the class properly implements INotifyPropertyChanged), or (perhaps more simply) to the IsChecked property of the ToggleButton.
For instance (the same applies to the GridSplitter):
<!-- EDIT: Added MinHeight="100" here instead -->
<transmission:TransmissionsControl x:Name="transmissionsTransmit"
TransmissionType="Transmit"
Grid.Row="1"
MinHeight="100"
Visibility={Binding ElementName=toggleLayout,
Path=IsChecked,
Converter={StaticResource boolToVis}}" />
At some level above the controls in question (here I am doing it at the window level) you need to add built-in BooleanToVisibilityConverter resource:
<Window.Resources>
<BooleanToVisibilityConverter x:Key="boolToVis" />
</Window.Resources>

Resources