I created a custom ControlTemplate for my TextBoxes and I can't manage to override the default behaviour of the ValidationRule errors.
The TextBox border just turn red and i can't find where to override this.
The thing i'd like to to in my ControlTemplate is something like this :
<EventSetter Event="HasError" Handler="TextBox_HasErrors"/>
And in my code behind :
private void TextBox_HasErrors(...)
{
//Change few things in my TextBox
}
As I Overrided the default ControlTemplate of the TextBox, i have this ScrollViewer x:Name="PART_ContentHost" which I think is responsible for the border coloration, but i don't know how and where to change that
Actually my usage of the textBox is like this :
<TextBox Tag="Email">
<TextBox.Text>
<Binding Path="Email" UpdateSourceTrigger="LostFocus">
<Binding.ValidationRules>
<local:TextBoxEmailValidationRule Domain=".com"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
Does some one know how i could change the red border behavior ?
The red border is defined in the default Validation.ErrorTemplate of the control. You can easily create your own error template by setting the attached property to a custom ControlTemplate:
<TextBox Tag="Email">
<TextBox.Text>
<Binding Path="Email" UpdateSourceTrigger="LostFocus">
<Binding.ValidationRules>
<local:TextBoxEmailValidationRule Domain=".com"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
<Validation.ErrorTemplate>
<ControlTemplate>
<StackPanel>
<!-- Placeholder for the TextBox itself -->
<AdornedElementPlaceholder x:Name="textBox"/>
<TextBlock Text="{Binding [0].ErrorContent}" Foreground="Red"/>
</StackPanel>
</ControlTemplate>
</Validation.ErrorTemplate>
</TextBox>
Please refer to this blog post for more information about this and data validation in WPF in general.
Related
How to Show Two textBox control content in third one using concatenating in WPF only in xaml binding no viewmodel code & code behind code.
Something like this should do the trick.
<TextBlock>
<Run Text="{Binding Path=Text, ElementName=txt1}"/>
<Run Text="{Binding Path=Text, ElementName=txt2}"/>
</TextBlock>
Where the ElementName property is the Name of your TextBoxes.
If you do not want a space between the runs, then put them on the same line like this.
<TextBlock>
<Run Text="{Binding Path=Text, ElementName=txt1}"/><Run Text="{Binding Path=Text, ElementName=txt2}"/>
</TextBlock>
EDIT:
Sorry just re-read the question, and believe you want the output to be in another TextBox. TextBoxes do not support the Run object so you have to approach this with a MultiBinding.
<TextBox>
<TextBox.Text>
<MultiBinding StringFormat="{}{0}{1}">
<Binding Path="Text" ElementName="txt1" />
<Binding Path="Text" ElementName="txt2" />
</MultiBinding>
</TextBox.Text>
</TextBox>
Hopefully this helps!
I am trying to validate some input data in WPF, and am using the DataResource proxy as described here:
http://www.wpfmentor.com/2009/01/how-to-add-binding-to-property-on.html
So, here's my XAML (simplified):
<mvvm:BaseDataView
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:md="clr-namespace:Mfc.Mvvm.Master"
>
<mvvm:BaseDataView.Resources>
<ResourceDictionary>
<md:DataResource x:Key="mmSS" BindingTarget="{Binding ElementName=tbMmss,Path=Text}"/>
<md:DataResource x:Key="mmTS" BindingTarget="{Binding ElementName=tbMmts,Path=Text}"/>
</ResourceDictionary>
</mvvm:BaseDataView.Resources>
...
<TextBox x:Name="tbMmts" HorizontalAlignment="Left" VerticalAlignment="Top" Grid.Row="2" Grid.Column="1" Width="100" Margin="3,3,0,0">
<TextBox.Text>
<Binding Path="MTS" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<md:DoubleRangeValidationRule MinValue="10.0" MaxValue="5000.0"/>
<md:LessThanValidationRule>
<md:LessThanValidationRule.LessThanChecker>
<md:ProxyForComparisonString
CompareTo="{md:DataResourceBinding DataResource={StaticResource mmSS}}">
</md:ProxyForComparisonString>
</md:LessThanValidationRule.LessThanChecker>
</md:LessThanValidationRule>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
...
<TextBox x:Name="tbMmss" HorizontalAlignment="Left" VerticalAlignment="Top" Grid.Row="3" Grid.Column="1" Width="100" Margin="3,3,0,0">
<TextBox.Text>
<Binding Path="STS" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<md:DoubleRangeValidationRule MinValue="10.0" MaxValue="5000.0"/>
<md:GreaterThanValidationRule>
<md:GreaterThanValidationRule.GreaterThanChecker>
<md:ProxyForComparisonString
CompareTo="{md:DataResourceBinding DataResource={StaticResource mmTS}}">
</md:ProxyForComparisonString>
</md:GreaterThanValidationRule.GreaterThanChecker>
</md:GreaterThanValidationRule>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
Validation works perfectly for the second textbox, but it crashes with a NullReferenceException for the first Textbox. The DataResource does not seem to get initialized, and Debugging says
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=tbMmss'. BindingExpression:Path=Text; DataItem=null; target element is 'DataResource'
Any ideas? Thank you!
You have got a completely wrong approach for this. Do not use Freezables. They are bad. They are not ment to be stuck in a resource and violated to deal like bridges!!! Please use attached properties or INotifyDataError info to solve this clean and properly.
I'm using Validation on TextBox as follows
<TextBox BorderThickness="1" Style="{DynamicResource TextBoxInError}"
Validation.ErrorTemplate="{StaticResource ValidationTemplate}">
<TextBox.Text>
<Binding Path="TimeBeforeDeletingPicture" Mode="TwoWay">
<Binding.ValidationRules>
<helpers:TimeBeforeDeletingRule/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
The validation fires when I leave the TextBox (apparently when it looses focus), I want to validate the input every time the text changes, I'm using MVVM so I don't want to mess with events, what's the correct clean way to achieve that.
Set UpdateSourcetrigger as follows
<TextBox.Text>
<Binding Path="TimeBeforeDeletingPicture" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged"/>
I am coding the following piece of code and it is looking like it is functional. However once I get a validation error it stays even if the validation error has been corrected. I am using validation at row level as well as at cell level on my DataGridTextColumn using the EditingElementStyle parameter but this can't be used in a DataGridTemplateColumn. Is there an equivalent that I can use as I am not sure how to proceed. Below is a sample of my code showing one of my DataGridTextColumns and my DataGridTemplateColumn.
<dg:DataGridTemplateColumn Header="Instrument" MinWidth="140">
<dg:DataGridTemplateColumn.CellTemplate>
<DataTemplate >
<TextBlock Text="{Binding Path=PRODUCTNO, Mode=TwoWay}"/>
</DataTemplate>
</dg:DataGridTemplateColumn.CellTemplate>
<dg:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox IsEditable="True" ItemsSource="{StaticResource TestList}">
<ComboBox.Text>
<Binding Path="PRODUCTNO" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<ValidationRules:IntegerValidationRule />
</Binding.ValidationRules>
</Binding>
</ComboBox.Text>
</ComboBox>
</DataTemplate>
</dg:DataGridTemplateColumn.CellEditingTemplate>
</dg:DataGridTemplateColumn>
<dg:DataGridTextColumn Header="BATCH No." Width="100" EditingElementStyle="{StaticResource CellEditStyle}">
<dg:DataGridTextColumn.Binding>
<Binding Mode="TwoWay" Path="BATCHNO">
<Binding.ValidationRules>
<ValidationRules:StringValidationRule/>
</Binding.ValidationRules>
</Binding>
</dg:DataGridTextColumn.Binding>
</dg:DataGridTextColumn>
Maybe try out what this person did. The code snippets on the page don't seem to work, but what he suggests is to rather use a customised DataGridTextColumn instead of DataGridTemplateColumn. I'm going to try that and I'll update my answer when (or if) it works.
I have a CheckBox and a WrapPanel in a Grid. The WrapPanel has two TextBox's inside of it. When the CheckBox is checked the whole WrapPanel is disabled. The TextBox's are bound to Properties.Settings.Default properties. The TextBox's also use ValidationRule's to validate the input.
What I would like to do: If one or both of the TextBox's have a validation error, I would like the act of checking the CheckBox to return the TextBox's text to the last good value from the Settings.Default property and thus clear the error.
I don't really care about sticking to some strict MVVM model anymore (this window is small and doesn't even have a ViewModel. What's the simplest way of achieving what I want?
I think I'm on the right path by adding an event handler to the Checked property of the CheckBox, but it is throwing NullReference as soon as I open the window, I don't yet have a reference to minBox and maxBox when the event handler is attached, I think.
private void AllPages_Checked(object sender, RoutedEventArgs e)
{
minBox.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
maxBox.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
}
;
<CheckBox Name="AllPages" Margin ="10,0,0,0" Grid.ColumnSpan="3" Grid.Row="1" Content="All Pages"
IsChecked="{Binding Source={StaticResource Settings}, Path=Default.AllPages, Mode=TwoWay}"/>
<WrapPanel Margin="10" Grid.Row="2"
IsEnabled="{Binding ElementName=AllPages, Path=IsChecked, Converter={StaticResource boolConvert}, Mode=OneWay}">
<TextBox Name="minBox" MaxWidth="30" MinWidth="30" MaxLength="3">
<TextBox.Text>
<Binding Source="{StaticResource Settings}" Path="Default.MinPage" Mode="TwoWay"
UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:MinValidationRule/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<Label Margin="0,0,0,0" Grid.ColumnSpan="3" Content="to"/>
<TextBox Name="maxBox" MaxWidth="30" MinWidth="30" MaxLength="3">
<TextBox.Text>
<Binding Source="{StaticResource Settings}" Path="Default.MaxPage" Mode="TwoWay"
UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:MaxValidationRule/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</WrapPanel>
Problem solved:
private void AllPages_Checked(object sender, RoutedEventArgs e)
{
if (minBox != null) minBox.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
if (maxBox != null) maxBox.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
}