I am new to WPF and creating an application which as few text boxes. Text boxes are bound to some source using MVVM. Now when I click on save button, it should fire validation for all empty text boxes and save event should not be fired. How can I achieve this in WPF.
I have written validater but it is not called. See my code below:
<TextBox Width="250" Grid.Row="0" Grid.Column="1" Margin="10">
<TextBox.Text>
<Binding Path="ContinuousModel.FileName" ValidatesOnDataErrors="True" NotifyOnValidationError="True" Mode="TwoWay" ValidatesOnExceptions="True">
<Binding.ValidationRules>
<validate:RequiredFieldValidatation />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
public class RequiredFieldValidatation:ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
if (value == null || value.ToString() == string.Empty)
return new ValidationResult(false, "Value cannot be empty");
return ValidationResult.ValidResult;
}
}
The above validator is getting called only if some text is written then change focus then come back and remove value and then change focus.
NOTE: Using MVVM and datatemplates, I am loading varios user controls and those controls are bound to viewmodel. Save button is in different user control so I cannot validate all text boxes manually on save button click event.
Add the UpdateSourceTrigger property to your binding;
<Binding Path="ContinuousModel.FileName" UpdateSourceTrigger=PropertyChanged ValidatesOnDataErrors="True" NotifyOnValidationError="True" Mode="TwoWay" ValidatesOnExceptions="True">
<Binding.ValidationRules>
<validate:RequiredFieldValidatation />
</Binding.ValidationRules>
</Binding>
Related
In my project I have several textboxes with a method binded to TextChanged event:
<TextBox Grid.Column="12" Style="{StaticResource txtDataStyle1}" Width="100" TextChanged="Data_TextChanged">
<Binding Path="ConfigObject.Edit.Default" UpdateSourceTrigger="LostFocus">
<Binding.ValidationRules>
<local:GenericValidationRule>
<local:GenericValidationRule.Wrapper>
<local:Wrapper TipoInterno="{Binding Path=Content, Source={x:Reference txtTipo}}"/>
</local:GenericValidationRule.Wrapper>
</local:GenericValidationRule>
</Binding.ValidationRules>
</Binding>
</TextBox>
When the page loads and the textboxes are valorized with "ConfigObject.Edit.Default", the Data_TextChanged event is fired. How can omit it?
I would "use" that method only when I change its value. Any help?
Instead of handling the the TextChanged event in the view, you should implement your logic in the setter of the source property.
If you can't do this for some reason, you could simply return from the event handler if the TextBox hasn't yet been loaded:
private void Data_TextChanged(object sender, TextChangedEventArgs e)
{
TextBox textBox = (TextBox)sender;
if (textBox.IsLoaded)
{
//your code...
}
}
I'm developing a IDataErrorInfo to validate the textboxes I have inside my application. I have the following code:
The .cs class to validate:
public class UserInformation : IDataErrorInfo
{
public string _name;
public string _surname;
public string Name
{
get { return _name; }
set { _name = value; }
}
public string Surname
{
get { return _surname; }
set { _surname = value; }
}
public override string ToString()
{
return Name + " " + Surname;
}
public string this[string columnName]
{
get
{
if (columnName == null) return string.Empty;
string result = string.Empty;
if (columnName.Equals("Name"))
{
if (string.IsNullOrEmpty(_name))
result = "Name cannot be empty.";
}
return result;
}
}
public string Error { get; private set; }
}
The .xaml:
<TextBox Grid.Column="3" Grid.Row="0" Name="TextBoxName"
Style="{DynamicResource InnerTextBox}"
Validation.ErrorTemplate="{StaticResource ValidationErrorTemplate}">
<TextBox.Text>
<Binding Path="Name" Source="{StaticResource UserInformation}"
ValidatesOnDataErrors="True" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<ExceptionValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
And the ErrorTemplate:
<ControlTemplate x:Key="ValidationErrorTemplate">
<DockPanel >
<StackPanel Orientation="Horizontal" DockPanel.Dock="Top">
<Grid Width="20" Height="20">
<Ellipse Width="20" Height="20" Fill="Tomato" HorizontalAlignment="Center" VerticalAlignment="Center" />
<TextBlock Foreground="White" FontWeight="Heavy" FontSize="8" HorizontalAlignment="Center" VerticalAlignment="Center" TextAlignment="Center"
ToolTip="{Binding ElementName=ErrorAdorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">X</TextBlock>
</Grid>
<TextBlock Foreground="Tomato" FontWeight="12" Margin="2,0,0,0" FontSize="20"
Text="{Binding ElementName=ErrorAdorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}" />
</StackPanel>
<AdornedElementPlaceholder x:Name="ErrorAdorner" />
</DockPanel>
</ControlTemplate>
The code works fine when I'm typing. But when the TextBox is loaded, the validation occurs too. And I don't want it to happen when it gains focus, only when it looses it or I change the text (like the one published here).
How can I avoid the validation error to be considered on first TextBox load?
NOTE: Even if I set the UpdateSourceTrigger to LostFocus, it is still making the validations.
To acheive you goal you need to:
First, remove ValidatesOnDataErrors="True" on your Binding. As said in docs:
Setting this property provides an alternative to using the
DataErrorValidationRule element explicitly
And we're gonna use it explicitly. Then use DataErrorValidationRule instead of ExceptionValidationRule for correctly working with IDataErrorInfo and data errors.
And last, we need to use some properties that this rule gives us:
<Binding.ValidationRules>
<DataErrorValidationRule ValidatesOnTargetUpdated="False" />
</Binding.ValidationRules>
ValidatesOnTargetUpdated on false will not trigger validation when target itself changes (i.e. on load). You can also play with ValidationStep property for additional control.
Edit:
Ok, I see that you need to skip validation on load and you need to validate on lost focus even if the value was not changed. Well, validation rules does not support that, because if the value was not updated, then no changed events will be called and no validation will occur, regardless of UpdateSourceTrigger setting.
The easy way out is to emulate this functionality by adding LostFocus handler to TextBox itself:
private void ValidatedTextBox_LostFocus(object sender, RoutedEventArgs e)
{
var txt = (TextBox)sender;
txt.GetBindingExpression(TextBox.TextProperty).UpdateSource();
}
If you need this for several TextBoxes, you can move the code to some static class.
The same results can be achieved using Explicit update source trigger, wich can be a little bit more cleaner.
I dont have any example atm, because I moved to what you have. But You will need to create a class which will Inherit from ValidationRule which exist in system.windows.controls, and then override Validate method.
Then your xaml textbox would look something like this instead
<TextBox.Text>
<Binding Path="your binding here" UpdateSourceTrigger="LostFocus" >
<Binding.ValidationRules>
<validationClass:yourRule/> define this at the top of xaml page
</Binding.ValidationRules>
</Binding>
You should be able to find examples on msdn, and here about validation rules
I never used WPF , tooltip before I am assigned to implement textbox that receieve number in range 1-999 if it out of this range tooltip will be shown near the textbox and textbox is changedto red border while input is out of range. pls guide me i try to bind xaml and function in C# but nothing happen
<TextBox HorizontalAlignment="Left" Height="31" TextWrapping="Wrap" VerticalAlignment="Top" Width="276" Margin="73,71,0,0" PreviewTextInput="PreviewTextInput" PreviewKeyDown="TextboxPreviewKeydown" >
<TextBox.Text>
<Binding Path="Number">
<Binding.ValidationRules>
<ExceptionValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
and my C#
public int Number
{
get { return num; }
set
{
num = value;
if (value <= 0 || value >999)
{
throw new ApplicationException("Out of range");
}
}
In simplest way you can do this:
<TextBox Text="{Binding}">
<TextBox.ToolTip>
<TextBlock Text="{Binding }"/> // Here you can bind to property or give static value to show in tooltip
</TextBox.ToolTip>
</TextBox>
Thanks
What you need is to build a validation rule
You create a class ("MyValidation" for example) that extends "ValidationRule" and you implement it
Then in your xaml, you can do this
<TextBox>
<TextBox.Text>
<Binding...>
<Binding.ValidationRules>
<local:MyValidation/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
Grab two TextBox and say that you need to validate that their content are distinct strings.
Example :
Correct result : prefix1, prefix2
Incorrect result : prefix1, prefix1
To do that task I thought about using a MultiBinding but two problems arises then :
Where should it be placed ? currently it is on a dummy TextBox
Even using that dummy TextBox, the ValidationRule is never called
Not sure whether this approach is correct, how would you do that ?
<MultiBinding Converter="{StaticResource MyConverter}">
<Binding Path="GradientPrefix"
Source="{StaticResource Indices}"
UpdateSourceTrigger="PropertyChanged" />
<Binding Path="ColorPrefix"
Source="{StaticResource Indices}"
UpdateSourceTrigger="PropertyChanged" />
<MultiBinding.ValidationRules>
<gpl2Xaml:DistinctStringValidationRule />
</MultiBinding.ValidationRules>
</MultiBinding>
Here's the solution using a BindingGroup !
Error at BindingGroup level :
Error at BindingGroup and field levels :
No errors :
Here's the code :
<Window>
<Window.Resources>
<gpl2Xaml:Indices x:Key="Indices"
ColorIndex="1"
ColorPrefix="MyColor"
GradientIndex="1"
GradientPrefix="MyColor" />
</Window.Resources>
<Grid DataContext="{StaticResource Indices}"
Style="{StaticResource gridInError}"
Validation.ErrorTemplate="{StaticResource validationTemplate}">
<Grid.BindingGroup>
<BindingGroup>
<BindingGroup.ValidationRules>
<gpl2Xaml:DistinctValidationRule />
</BindingGroup.ValidationRules>
</BindingGroup>
</Grid.BindingGroup>
<TextBox x:Name="TextBoxGradientPrefix"
Style="{StaticResource textBoxInError}"
TextChanged="TextBoxGradientPrefix_OnTextChanged"
Validation.ErrorTemplate="{StaticResource validationTemplate}">
<Binding Path="GradientPrefix"
Source="{StaticResource Indices}"
UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<gpl2Xaml:StringValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox>
<TextBox x:Name="TextBoxColorPrefix"
Style="{StaticResource textBoxInError}"
TextChanged="TextBoxColorPrefix_OnTextChanged"
Validation.ErrorTemplate="{StaticResource validationTemplate}">
<Binding Path="ColorPrefix"
Source="{StaticResource Indices}"
UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<gpl2Xaml:StringValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox>
</Grid>
</Window>
Extra code to trigger validation every time :
private void TextBoxGradientPrefix_OnTextChanged(object sender, TextChangedEventArgs e)
{
grid.BindingGroup.CommitEdit();
}
private void TextBoxColorPrefix_OnTextChanged(object sender, TextChangedEventArgs e)
{
grid.BindingGroup.CommitEdit();
}
And the validation rule :
public class DistinctValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
var bindingGroup = value as BindingGroup;
if (bindingGroup == null) return new ValidationResult(false, "Not a BindingGroup");
var o = bindingGroup.Items[0] as Indices;
if (o == null) return new ValidationResult(false, "Not an Indices");
if (o.ColorPrefix == o.GradientPrefix)
return new ValidationResult(false, "Color prefix and Gradient prefix must be distinct.");
return new ValidationResult(true, null);
}
}
I am following a tutorial here. which shows a basic example on how to bind to a dependency property.
<Binding ElementName="This" Path="IPAddress" UpdateSourceTrigger="PropertyChanged">
where "This" is the name of the current window:
<Window x:Class="SOTCBindingValidation.Window1" x:Name="This"
whenever i try to do something like this, i keep getting the same error:
Cannot find source for binding with reference 'ElementName=GridControlControl1'. BindingExpression:Path=IPAddress; DataItem=null; target element is 'TextBox' (Name='AddressBox'); target property is 'Text' (type 'String')
my code:
<UserControl x:Class="WpfGridtest.GridControl" x:Name="GridControlControl1" ... />
<TextBox x:Name="AddressBox">
<TextBox.Text>
<Binding ElementName="GridControlControl1" Path="IPAddress" UpdateSourceTrigger="PropertyChanged">
</Binding>
</TextBox.Text>
</TextBox>
codebehind:
partial class GridControl : UserControl
public static readonly DependencyProperty IPAddressProperty = DependencyProperty.Register("IPAddress", typeof(string), typeof(GridControl), new UIPropertyMetadata("1.1.1.1"));
public string IPAddress
{
get { return (string)GetValue(IPAddressProperty); }
set { SetValue(IPAddressProperty, value); }
}
it's almost like something changed in .Net 4.0?
It depends on what you want. I'll try to offer a complete answer. One way to guarantee better success with this syntax is to use VS2010's XAML Binding Builder, which is how I assembled the syntax you're about to see.
If you want an element of the UserControl to display your IPAddress dependency property, (which looks like it's defined correctly to me), use this syntax within the body of the UserControl's markup:
<TextBlock Text="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type my:GridControl},
AncestorLevel=1},
Path=IPAddress}" />
Most XAML binding examples use this syntax rather than the more verbose hierarchical XML:
<TextBlock>
<TextBlock.Text>
<Binding Path="IPAddress">
<Binding.RelativeSource>
<RelativeSource Mode="FindAncestor"
AncestorType="{x:Type my:GridControl}"
AncestorLevel="1"
/>
</Binding.RelativeSource>
</Binding>
</TextBlock.Text>
</TextBlock>
...but both kinds of syntax produce the same results. Note here that the AncestorType is the class name of your UserControl, not the x:Name you would supply when using the UserControl in other markup.
Suppose you have a UI element in markup outside your UserControl, and you want access to your DependencyProperty for that other control. The markup looks something like this:
<my:GridControl
x:Name="GridControl1" IPAddress="192.168.1.1" />
<TextBox Text="{Binding ElementName=GridControl1, Path=IPAddress}"/>
Or, alternatively, this:
<TextBox>
<TextBox.Text>
<Binding ElementName="GridControl1" Path="IPAddress"/>
</TextBox.Text>
</TextBox>
Note here that this time you're using the x:Name property of the GridControl rather than the class name, and that you refer to it as an ElementName, and not an "Ancestor". In both cases, though, the Path is the declared name of the DependencyProperty you defined.
Try using RelativeSource:
<TextBox>
<TextBox.Text>
<Binding Path="IPAddress">
<Binding.RelativeSource>
<RelativeSource
Mode="FindAncestor"
AncestorType="{x:Type UserControl}"
AncestorLevel="1"
/>
</Binding.RelativeSource>
</Binding>
</TextBox.Text>
</TextBox>
Instead of {x:Type UserControl}you could insert your actual type there, i.e.:
<TextBox>
<TextBox.Text>
<Binding Path="IPAddress">
<Binding.RelativeSource>
<RelativeSource xmlns:my="clr-namespace:WpfGridtest"
Mode="FindAncestor"
AncestorType="{x:Type my:GridControl}"
AncestorLevel="1"
/>
</Binding.RelativeSource>
</Binding>
</TextBox.Text>
</TextBox>