ValidationRules within control template - wpf

I have control thats inherits from textbox
public class MyTextBox : TextBox
This has a style
<Style TargetType="{x:Type Controls:MyTextBox}">
one of the Setters is
<Setter Property="Template">
I want to be able to set the Binding.ValidationRules to something in the template, thus affecting all instances of this type of textbox.
I can therefore make textboxes for say Times, Dates, Numerics, post/zip codes.. or whatever i want,
I don't want to have to set the validation rules every time i create a textbox. I just want to say i want a NumericTextBox and have it validate in whatever way is set in the template.
Is this possible?
All i have seen so far is the ValidationRules being set on each instance of the control e.g.
<TextBox x:Name="txtEMail" Template={StaticResource TextBoxErrorTemplate}>
<TextBox.Text>
<Binding Path="EMail" UpdateSourceTrigger="PropertyChanged" >
<Binding.ValidationRules>
<local:RegexValidationRule Pattern="{StaticResource emailRegex}"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
(from http://www.wpftutorial.net/DataValidation.html)

As you see, validation rules are set along with bindings. I came across the same problem and the working solution for me was to do something like this:
public MyTextBox()
{
this.Loaded += new RoutedEventHandler(MyTextBox_Loaded);
}
void MyTextBox_Loaded(object sender, RoutedEventArgs e)
{
var binding = BindingOperations.GetBinding(this, TextBox.ValueProperty);
binding.ValidationRules.Add(new MyValidationRule());
}
The problem here is to be sure that the binding is set before we add the validation rule, hence the use of Loaded, but i'm not sure if this will work on every scenario.

I am not sure it is possible to do exactly what you want. You might be able to do it with a lot of trickery in property setters (I wouldn't rely on this, since it would involve modifying bindings, which are by definition dynamic), or with code behind/custom controls.
Instead I suggest you push your validation into your ViewModel, probably with IDataErrorInfo. There are many articles out there. Here's one of the first ones I found with a search just now for "MVVM validation":
MVVM - Validation
Doing this will allow you to use standard OO composition techniques, so you can avoid repeating yourself :)

Related

I need an attached property to monitor parent for INotifyDataErrrorInfo

If I have the following binding
<TextBox Text="{Binding XXX.Name, ValidatesOnNotifyDataErrors=True}"/>
it doesn't work because only the DataContext implements INotifyDataErrorInfo and raises "XXX.Name" errors but ValidatesOnNotifyDataErrors tries to monitor XXX for error events not the data context.
However I am sure somebody could figure out how to write an attached property to do the following
<TextBox Grid.Column="5" Text="{Binding Binding.Name, c:ValidatesOnNotifyDataErrorsOnDataContext=True}"/>
where the data context is monitored not the child. Anybody got an idea how to start with that?
I think this is possible to implement, but because of the flexibility of bindings (RelativeSource, MultiBindings and whatnot) it would be difficult to make something like this that is truly robust. Personally, I think it would be cleaner to to implement INotifyDataErrorInfo at every level of the structure (and for parts of the structure that you don't own, like your Point example, use proxy classes that mirror the properties).
Anyway, Binding is a MarkupExtension, not a DependencyObject, which means attached properties can't be applied to it. You could inherit Binding to add your own properties, but this isn't very useful since it doesn't give you any overridable methods.
It shouldn't be necessary to extend Binding though, since all you want is a custom ValidationRule. Setting ValidatesOnNotifyDataErrors=True is equivalent to adding a NotifyDataErrorValidationRule:
<TextBox>
<TextBox.Text>
<Binding Path="XXX.Name">
<Binding.ValidationRules>
<NotifyDataErrorValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox>
</TextBox>
So you just need to replace NotifyDataErrorValidationRule with your own rule. If you override this Validate overload (which is passed the binding expression), you should be able to access the full binding path (through ParentBinding) and look up an error.

WPF DataGrid validation when bound to a DataView

I am attempting to validate input on a DataGrid which is populated by a DataView (e.g. myDataGrid.ItemsSource = myDataView). However, all the WPF DataGrid validation examples I have seen assume that the DataGrid source is a C# class. I can't figure out how to hook up a single cell (i.e. a column) to a code-behind validation. Can someone give an example or point me to one?
Ok, I did some quick tests; I think the main impediment to really doing it manually is that the DataGridCell doesn't create any bindings for its content when assigning directly a DataView for display. However, if you're willing to jump through the hoops of assigning custom DataGridTemplateColumns when creating the DataGrid, you can access the bindings on, say, the TextBox instances inside the template, and set custom validation errors on them.
Well actually, either this or override the default style of the textboxes generated by the DataGrid; you see, the default textbox style doesn't have any borders or anything, so setting a validation error on it doesn't have any effect. Making it into a normal textbox with DataTemplate or overriding its style would enable you to actually make something visible as an effect of setting the validation error.
However you'll need to do some research by yourself; I did a quick prototype and it works, but I can't create the binding correctly in the DataTemplate (either I forgot all the WPF I knew since I worked with it last, or I only know how to work with proper bindings :)). If you get somewhere with that it's easier going forward:
Somehow get access to a BindingExpression; what I did was attach a handler to the LostFocus event and checking if what lost focus was a textbox that was inside a DataGridCell; if so, I simply mark that binding as invalid (with t representing a TextBox instance):
var x = t.GetBindingExpression(TextBox.TextProperty);
Validation.MarkInvalid(x, new ValidationError(new ExceptionValidationRule(), x.ParentBinding, "error", new Exception("error")));
I must confess I'm not sure anymore what each argument's role is in the ValidationError constructor is (I think the exception message is displayed by default in the validation tooltip? Or is it the error content?), but I'm sure you can figure it out. Now if you don't mind, I think I'll take a break... like I said, it's not easy going against the flow :)
So I did some more research, and what I was basically missing was that I can specify the column name with the Path attribute of a Binding (or even use the column ordinal in brackets, e.g. Path="[0]"). After that realization, everything is basically the same as using a class property. So a typical DataGrid column definition I use follows:
<DataGridTextColumn Header="Regular" EditingElementStyle="{StaticResource ValidationErrorStyleBoxRA}" ElementStyle="{StaticResource ValidationErrorStyleBlockRA}" Width="60">
<DataGridTextColumn.Binding>
<Binding Path="HourlyRate" StringFormat="F3" ValidatesOnExceptions="True" ValidatesOnDataErrors="True" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:HourlyRatesAmountValidate ValidatesOnTargetUpdated="True" />
</Binding.ValidationRules>
</Binding>
</DataGridTextColumn.Binding>
</DataGridTextColumn>

Add ValidationRules into a single xaml line OR shorthand ValidationRules

I'm using a PasswordBox which exposes a dependency property such that I can bind to it. The problem is that by using it like so, I cannot shorthand the Binding.ValidationRules to this syntax:
<PasswordBox services:RPLPasswordBoxBinder.BindPassword="True"
services:RPLPasswordBoxBinder.BoundPassword="{Binding Path=LoginUser.Parola, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
</PasswordBox>
I set my ValidationRules to a textbox like this:
<TextBox.Text>
<Binding Path="LoginUser.Parola" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<some validation rule/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
Is there any way to specify the ValidationRules collection to my PasswordBox in a single xaml line? Or maybe there's another clever solution for validating user input into my password box?
Some clarifications:
I'm using MVVM and I don't want to use code behind.
I want to add only a single ValidationRule. Maybe the problem with shorthanding Binding.ValidationRules is that this property is a collection. One validationrule would suffice in my situation.
There's a similar question on stackoverflow here. My problem is different as I don't just want to increase readability but actually validate my PasswordBox.
I suggest that you base your data model class on IDataErrorInfo and then validation is performed there and not in the code behind.
There are plenty of examples, but here's one for starters and another here.

Access Datacontext And A Property In Code Behind At The Same Time

I am using MVVM/WPF and trying to do something seemingly simple, but cant find a clean solution.
I want to do the following:
When a property changes in the model (WPF Textbox text would be changed in this case), use a method to perform other operations on the UI relating to the property bound.
Currently i am using a multibinding on the tooltip (to get the textbox datacontext + binding path), but this is a bit of a hack.
<TextBox x:Name="textBox" Text="{Binding Model.MyProperty}">
<TextBox.ToolTip>
<MultiBinding Converter="{StaticResource brNewMultiConverter}">
<!-- This to trigger the converter in all required cases.
Without it, i cant get the event to fire when filling
the model initially
-->
<Binding ElementName="textBox" Path="Text" />
<!-- This has the properties i need, but wont fire without
the binding above -->
<Binding ElementName="textBox" />
</MultiBinding>
</TextBox.ToolTip>
</TextBox>
I would like to make something re-usable and maybe for different controls, hence i am not just using the textchanged event.
If anyone could point me in the right direction, it would be much appreciated.
OK, so far as your Multibinding there, what are you trying to accomplish there? I don't know what your converter is supposed to do, but can it not be done with an IValueConverter implementing class? I am assuming not, it looks like you are passing the textbox to the converter.
As far as having a method do several things when your model properties get updated, you can have the viewmodel subscribe to events on your model class. Just declare the object WithEvents (VB.NET) and add event handlers for On[PropertyName]Changed.
When implementing MVVM, I tend to treat the codebehind as a second class citizen. I do my best to push all logic off to the ViewModel or View if I can. I have almost completely stopped using Converters as much of that logic can be duplicated in ViewModels, and if it is something that I want to re-use, I usually just have a little helper class that gets whatever passed to it, does something, and passes it back out. I have never really had that great a relationship with IValueConverter...
Other than that, it is unclear exactly what you are trying to do. Could we get some more clarification?
It looks like you're trying to have the tooltip have the content of the textbox, if so why not just do this?
<TextBox Text="{Binding Model.MyProperty}" ToolTip="{Binding Model.MyProperty}"/>
If that's not what you want, but want the tooltip to change based on the value of the textbox then do that in your viewmodel e.g.
public class MyViewModel
{
string _MyProperty;
public string MyProperty
{
get { return _MyProperty;}
set
{
_MyProperty = value;
OnPropertyChanged("MyProperty");
OnPropertyChanged("MyToolTipProperty"); //force WPF to get the value of MyToolTipProperty
}
}
public string MyToolTipProperty
{
get
{
//return what you want
}
}
}
and then in your markup:
<TextBox Text="{Binding Model.MyProperty}" ToolTip="{Binding Model.MyToolTipProperty}"/>

Binding ValidationRules in one line?

I have several one-line binding already written and I'd like to keep it that way if possible and if it still is humanly readable.
Is there any way to rewrite this
<TextBox.Text>
<Binding Path="SomePath" NotifyOnValidationError="True" >
<Binding.ValidationRules>
<local:ValidationRule1></local:ValidationRule1>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
in one line?, like
<TextBox Text="{Binding Path=SomePath, [ValidationRule1...]}" />
I think there is no one-liner and besides the standard version is more readable.
There is no one-liner out of the box, but it's perfectly possible to make one yourself by taking advantage of markup extensions. More specifically, create a custom method of defining bindings for typical scenarios in your project.
A useful thing to note is that Binding class is itself a markup extension and thus provides an implementation of ProvideValue. It means that a custom binding markup extensions can just create or take a binding, fill out its values as needed and provide the value by the newly modified binding.
For example, if you commonly use a single validation rule and want to keep it in a one-liner, you may want to create an extension like this:
using System;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Markup;
namespace MyProject.Markup
{
public class SingleValidationBindingExtension : MarkupExtension
{
private Binding _binding;
public SingleValidationBindingExtension(Binding binding, ValidationRule validationRule)
{
_binding = binding;
_binding.ValidationRules.Add(validationRule);
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return _binding.ProvideValue(serviceProvider);
}
}
}
Then, somewhere in your XAML:
<UserControl xmlns:ext="clr-namespace:MyProject.Markup;assembly=MyProject"
...>
<UserControl.Resources>
<local:ValidationRule1 x:Key="ValidationRule1"/>
</UserControl.Resources>
<!-- Note that you can customize other binding properties within the inner {Binding ...} markup -->
<TextBox Text="{ext:SingleValidationBinding {Binding SomePath}, {StaticResource ValidationRule1}}" />
</UserControl>
VoilĂ ! Now it took only one line to apply the validation rule to the binding.
Granted, having an extension specifically for a single-validation bindings may seem wasteful. However, with this simple proof-of-concept it's easy to expand upon the idea of custom binding markups. For example, you may group commonly used combinations of converters and validations rules into objects, define them as static resources and then apply them to bindings using a custom binding definition.

Resources