WPF Validation Control Template overlapping - wpf

I've got a user control with a control template to show validation errors, validation template:
<ControlTemplate x:Key="TextBoxPropertyValidationTemplate">
<StackPanel>
<Border BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder x:Name="MyAdorner" />
</Border>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" MaxHeight="16" MaxWidth="16"
Source="{Binding Source={StaticResource ValidationIcon}, Converter={StaticResource UriConverter}}"
Margin="1" RenderOptions.BitmapScalingMode="HighQuality"
VerticalAlignment="Center" HorizontalAlignment="Center" />
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Left"
Text="{Binding ElementName=MyAdorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}"
TextWrapping="Wrap" Grid.Column="1" FontSize="10" Foreground="Red" />
</Grid>
</StackPanel>
</ControlTemplate>
And I can't seem to get around a rather irritating problem which looks like this:
I've been trying to play around with margins on the user control and on the template also some Height=Auto etc but all these don't really help. Any ideas anyone?
If that helps the main user control (which nests the ones with validation) is in a TabItem with a AdornerDecorator.
Any help appreciated.

I'd say this is because your error message is on the AdornerLayer, which doesn't participate in the same layout as your control. MSDN says "rendering of an adorner is independent from rendering of the UIElement that the adorner is bound to." and that is why the message is just put on top of everything.
You could put the error text into the original template, hide it based on Validation.HasError and include it in the layout process that way.
But changing the layout of the control might not be the best way to go if a validation error occurs. You might consider providing additional information in a ToolTip.

Alternately, instead of using ControlTemplate, you could put the error message TextBlock beside the TextBox, and set its Text property binding the TextBox's ErrorContent.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBox x:Name="txtName" Grid.Row="0">
<TextBox.Text>
<Binding Path="Name" NotifyOnValidationError="True" ValidatesOnExceptions="True" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<common:RequiredFieldValidationRule/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<TextBlock Grid.Row="1" Text="{Binding ElementName=txtName,Path=(Validation.Errors)[0].ErrorContent}"
Visibility="{Binding ElementName=txtName,Path=Validation.HasError,Converter=...}" />
</Grid>

Related

RichTextbox, FlowDocument unable to left align

I have a RichTextBox and a FlowDocument like so in my ResourceDictionary (no-code behind), and my original window loads with a relatively wider size.
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="55"/>
</Grid.ColumnDefinitions>
<FlowDocumentScrollViewer DockPanel.Dock="Left" Padding="0" Grid.Column="0"
HorizontalAlignment="Left" VerticalContentAlignment="Center" BorderBrush="LightGreen" BorderThickness="2">
<FlowDocument Name="Msg" TextOptions.TextFormattingMode="Display">
<FlowDocument.Tag>
<MultiBinding Converter="{StaticResource MyConv}">
<Binding RelativeSource="{RelativeSource Self}"/>
<Binding Path="Speed"/>
</MultiBinding>
</FlowDocument.Tag>
</FlowDocument>
</FlowDocumentScrollViewer>
<Button Content="Button" Margin="1,0,4,0" MinWidth="25" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Center"/>
</Grid>
Here we can see that the FlowDocumentScrollViewer's default size/border extends till the full (grid) width irrespective of its inner content, and because of which the Button goes out of visual scope.
I also tried RichTextbox instead of FlowDocumentScrollViewer but its the same problem.
As a work-around I can do Margin='-20,0,0,0' on the Button but that's not a good idea as that will force the button over the text when window is made much smaller.
Please help!

How to set BackColor of a CheckBox in WPF

In WinForm we can set BackColor of a CheckBox
How do I do this in WPF? I try
<CheckBox Content="CheckBox" Background="Red"/>
but this only changes the rectangle border color
I also try
<CheckBox>
<TextBlock Text="CheckBox" Background="Red"/>
</CheckBox>
but this only changes the text background color, not including the rectangle
=======
Thank you all for the solutions. I think the simplest way works for me :)
If you experiment a little with panels you get there:
<Grid Background="Red" HorizontalAlignment="Left">
<CheckBox Content="test" />
</Grid>
is pretty close to what you want.
Tried it myself ;-)
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<CheckBox FlowDirection="RightToLeft" background="Red"
IsChecked="False" />
<Border Grid.Column="1"
Margin="20 0 0 0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Background="LightGray">
<TextBlock HorizontalAlignment="Left"
Foreground="Black"
Style="{StaticResource RegularTextblock}"
Text="Checkbox1" />
</Border>
</Grid>
You are asking for little too much. Using Blend I made created a relevant style for the CheckBox.
The code was too big. So SO did not allow to display. Here is the pastie link
For the Background there is a grid markGrid. I added a Background and a TemplateBinding to force the CheckBox to change the colour. The drawback is the Path colour will be visible very faintly if the background is dark.

Validated Textbox in a UserControl

I have created a UserControl - a Labeled TextBox which is working pretty well, except for the validation template. When there's an error the validation control template shows up but it fills the whole space including the Label. I only want it to be as big as the TextBox. How to fix this?
Here's the xaml:
<UserControl x:Class="Infrastructure.CustomControls.LabelTextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="LTB">
<Grid HorizontalAlignment="{Binding}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="tbl"
FontFamily="{Binding}"
FontSize="{Binding}"
Text="{Binding ElementName=LTB, Path=LabelText}"
Height="{Binding ElementName=LTB, Path=LabelHeight}"
Width="{Binding ElementName=LTB, Path=LabelWidth}"
VerticalAlignment="Center"/>
<TextBox x:Name="tbx"
Grid.Column="1"
FontFamily="{Binding}"
FontSize="{Binding}"
IsReadOnly="{Binding ElementName=LTB, Path=IsReadOnly}"
MaxLength="{Binding ElementName=LTB, Path=TextMaxLength}"
Text="{Binding ElementName=LTB, Path=Text}"
Height="{Binding ElementName=LTB, Path=TextHeight}"
Width="{Binding ElementName=LTB, Path=TextWidth}"
VerticalAlignment="Center">
<Validation.ErrorTemplate>
<ControlTemplate>
<DockPanel LastChildFill="True"
ToolTip="{Binding ElementName=aep, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">
<TextBlock DockPanel.Dock="Right"
Foreground="Red"
FontSize="14pt" Text="*"
Margin="-15,0,0,0"
FontWeight="Bold"/>
<Border BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder Name="aep"/>
</Border>
</DockPanel>
</ControlTemplate>
</Validation.ErrorTemplate>
</TextBox>
</Grid>
</UserControl>
The reason why this happens is that you're implementing IDataErrorInfo in your view and not your UserControl. This causes the default WPF red border to appear for the entire usercontrol.
To get your defined error template to appear, you'll need to implement IDataErrorInfo in your usercontrol and add ValidatesOnDataErrors=True to your binding expression.
If you'd like to keep the IDataErrorInfo logic in your view and not in your UserControl (which is pretty reasonable), you'll need to define a validation template for the user control in the view:
<Window>
<local:UserControl>
<Validation.ErrorTemplate>
<ControlTemplate>
...
</ControlTemplate>
</Validation.ErrorTemplate>
</local:UserControl>
</Window>
To get it to only show the border for the TextBox, you can play with the border's width using a converter that'll take the entire usercontrol's width as a parameter and return the width of the textbox; possibly something like this:
<Border BorderBrush="Red" BorderThickness="1" Width="{Binding ElementName=ph, Path=ActualWidth, Converter={StaticResource myConverter}}">
<AdornedElementPlaceholder Name="ph" />
</Border>
Thanks for the response, it helped me figure out where the problem is. And for that i vote it as useful.
What i did was programmatically query the UserControl validation for errors in the TextBoxChangedEvent and set the validation error manually (http://wpftutorial.net/ValidationErrorByCode.html) for the TextBox.

Make the last TextBox in a DataTemplate Stretch

I have an ItemsControl:
<Border Grid.Row="1" Margin="20" BorderBrush="AliceBlue" BorderThickness="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<ItemsControl Margin="10" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ItemsSource="{Binding SelectedEventHistoryEntryCollection}" ItemTemplateSelector="{StaticResource computerEventHistoryDataTemplateSelector}"/>
</Border>
With some datatemplates. I'm testing the first template:
<DataTemplate x:Key="DetailsDataTemplate">
<Grid>
<Label Width="150" HorizontalAlignment="Left" VerticalAlignment="Top" Content="{x:Static resx:Resources.Label_ServiceDept}"/>
<TextBox Margin="110,0,0,0" Width="200" IsReadOnly="True" Text="{Binding ServiceDepartment}" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<Label Width="150" Margin="0,40,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Content="{x:Static resx:Resources.Label_SLA}"/>
<TextBox Margin="110,40,0,0" Width="200" IsReadOnly="True" Text="{Binding SLA}" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<Label Width="150" Margin="0,80,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Content="{x:Static resx:Resources.Label_Details}"/>
<TextBox Margin="110,80,10,10" IsReadOnly="True" TextWrapping="Wrap" Text="{Binding Details}"/>
</Grid>
</DataTemplate>
I would like the last Textbox in the datatemplate to use up the remaining space, but nothing I tried works. How can I get this uncooperateive TextBox to stretch?
Edit: Removed the Height Property on the Textbox.
Change the grid to a DockPanel with LastChildFill="true".
You can then get rid of all of the Margins and let WPF do the layout automatically.
Use a <DockPanel> instead of a <Grid>.
The last item in the DockPanel uses remaining space.
Generally, I use <Grid.RowDefinitions> and <Grid.ColumnDefinitions> in conjunction with star sizing * instead of margins for this type of layout.
UPDATE 1: (Removed for clarity)
UPDATE 2: When I wind up in situations like this where I can’t figure out where to apply a binding or a template I try to back up and look at the problem differently. I almost always take it back to the MVVM pattern. In this case, your Model is your EventHistory object. Your ViewModel has an ObservableCollection<EventHistory>. And your View is simply binding to that collection. So, to get something like this:
You would use something like this for your View:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="8" />
<RowDefinition Height="1.5*" />
</Grid.RowDefinitions>
<DataGrid Grid.Row="0" AutoGenerateColumns="True"
ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True"
HorizontalGridLinesBrush="DarkGray" VerticalGridLinesBrush="DarkGray" />
<GridSplitter Grid.Row="1"
Background="Transparent"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
<Border Grid.Row="2" BorderBrush="DarkGray" BorderThickness="1" CornerRadius="3" Padding="8">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="Status" />
<TextBox Grid.Row="0" Grid.Column="1" Margin="0,0,0,8" Text="{Binding Path=Status}" />
<Label Grid.Row="1" Grid.Column="0" Content="Detailed Description" />
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Path=Description}" />
</Grid>
</Border>
</Grid>
And that is just fine -- because that is what you are trying to achieve. The bindings on the 2 labels and textboxes at the bottom of the screen don’t have to be part of any data template. They are part of the view (everything inside the red border in the screenshot). All of the resizing works and everything is good. If you really want to move things into a DataTemplate, it is probably possible, but this seemed more natural at this point.
NOTE: After creating the View (area inside the red border) I hosted it in the main window leaving an area to the right as per your screenshot. I also took a few liberties with a grid splitter, star resizing and margins so things would take up all of the available space while maintaining the pictured proportions.
Hopefully that helps!
I was a little slow on the uptake with my first answer. After realizing what you were after I don't think that approach was correct. Also, I don't think you can easily achieve what you're after using DataTemplates. However, I do think you have a few options:
Check into Prism since is is good at doing things like building composite applications. However, it seems like WAY overkill for this problem. So, a more direct approach may be...
Build out custom controls for each separate detail view you have and then write some custom logic to load each view as needed. You would set it up like this...
You main grid and your details view host (i.e. the ContentControl) would be set up something like this:
<Grid Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="8" />
<RowDefinition Height="1.5*" />
</Grid.RowDefinitions>
<DataGrid Grid.Row="0" />
<GridSplitter Grid.Row="1" Background="Transparent"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
<Border Grid.Row="2" BorderBrush="DarkGray" BorderThickness="1" CornerRadius="3" Padding="8">
<ContentControl Grid.Row="2" x:Name="myContent" />
</Border>
</Grid>
And each of your custom controls for your individual detail views would be set up something like this:
<UserControl x:Class="WpfApplication1.CustomUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="Status" />
<Label Grid.Row="1" Grid.Column="0" Content="Description" />
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Status}" />
<TextBox Grid.Row="1" Grid.Column="1" TextWrapping="Wrap" Text="{Binding Description}" />
</Grid>
</UserControl>
At run time a row is selected in your DataGrid, you would have to load the correct user control with some code like this:
myContent.Content = new CustomUserControl();
Each of your custom controls would have to use star sizing, etc. to get the layouts to look right - which is what you were after with your question. Obviously there is still a lot of wireup that would need to be done.
That should give you what you are after. Sorry for the run-around on the first answer!

Source versus DataContext in XAML

Which of these methods is best?
<Window.Resources>
<sys:Int16 x:Key="MyValue">123</sys:Int16>
</Window.Resources>
<StackPanel>
<!-- method 1 -->
<TextBlock Text="{Binding}" DataContext="{StaticResource MyValue}" />
<!-- method 2 -->
<TextBlock Text="{Binding, Source={StaticResource MyValue}}" />
</StackPanel>
As with many "which is better" questions. I would say that "it depends" on the context.
They both exist because they both can serve a purpose in different contexts. Given only what you have shown above, I would choose Example 2.
When you set the DataContext, however, all of its children will inherit that DataContext. So maybe instead you are using a Button. And within you Button, you want to jazz it up a bit and display the text four times each with a different color. As you can see below, I would then choose Example 1.
Example 1: (note the DataContext is on the button, and the TextBlocks don't need the Source like they do in Example 2)
<Button DataContext="{StaticResource MyValue}" Height="Auto" Width="Auto" HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding}" Foreground="Red" />
<TextBlock Grid.Row="1" Grid.Column="0" Text="{Binding}" Foreground="Blue" />
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding}" Foreground="Yellow"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding}" Foreground="Green" />
</Grid>
</Button>
Example 2:
<Button Height="Auto" Width="Auto" HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding, Source={StaticResource MyValue}}" Foreground="Red" />
<TextBlock Grid.Row="1" Grid.Column="0" Text="{Binding, Source={StaticResource MyValue}}" Foreground="Blue" />
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding, Source={StaticResource MyValue}}" Foreground="Yellow"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding, Source={StaticResource MyValue}}" Foreground="Green" />
</Grid>
</Button>
When you're binding to a simple object that only has one representation like an Int16 in your case, you're probably only going to bind and display that value once, and thus option 2 makes most sense.
A good rule of thumb... if you find yourself setting the 'Source' to the same thing more than one binding, you should probably just bind the DataContext of a common parent FrameworkElement.
I would say that if I had to choose between the two, I would go with method 2. DataContext is really more for Databinding an item to a more complex underlying object and eases the databinding of many data values.
Just out of curiosity, why are you doing it this way? Does your code change the value of MyValue at some point? Is there no better way for you to do it for some reason?
The DataContenxt DependencyProperty allows you to easily bind across all of proeprties for a DependencyObject.
The Source DependenceyProperty of a Binding allows you to point that specific binding to the source you want, regardless of the DataContext.
This becomes really helpful when you are doing more complex bindings for ListViews. For instance:
<Window.Resources>
<local:MyConverter x:Key="MyConverter" />
</Window.Resources>
<Grid>
<ComboBox ItemsSource="{Binding Source={StaticResource MyConverter}, Path=DisplayValues}" DataContenxt={Binding ElementName=lvwItems Path=SelectedItem} SelectedItem="{Binding Converter={StaticResource MyConverter}"/>
<ListView Name="lvwItems"......
The above example just shows off that I set the itemssource to a property in the 'MyConverter' called DisplayValues, the datacontext is what I am working with on that combobox though, which is handling the SelectedItem property of the ListView named 'lvwItems'.
Hope this helps.

Resources