Can you use ValidationRules on ListBox.SelectedItems? - wpf

I want to use ValidationRules to verify that a few ListBox controls have at least one item selected.
I tried doing it this way:
<ListBox ItemsSource="{Binding Path=AvailableItems}"
Behaviors:MultiSelectorBehaviours.SynchronizedSelectedItems="{Binding ChosenItems}"
x:Name="ListBoxItems">
<ListBox.Tag>
<Binding ElementName="ListBoxItems" Path="SelectedItem">
<Binding.ValidationRules>
<ValidationRules:NotNullValidationRule />
</Binding.ValidationRules>
</Binding>
</ListBox.Tag>
</ListBox>
But my NotNullValidationRule never gets called. Note that the SynchronizedSelectedItems is a special attached property I use to synchronize the SelectedItems to a custom collection (described here). That's why I do my validation on a 'fake' Binding applied to Tag instead.
Is there a way to validate ListBox.SelectedItems?

Validation is done only in TwoWay and OneWayToSource mode bindings. If you turn the Binding around, binding SelectedItem to tag in either TwoWay or OneWayToSource mode the validation is triggered.
Validation is there to protect the target property. So when you set Tag, validation makes sure the Tag is valid and SelectedItem can be set with a new value. The following code works (SelectedItem binds TwoWay automatically IIRC.)
<ListBox x:Name="list">
<ListBox.SelectedItem>
<Binding ElementName="list" Path="Tag">
<Binding.ValidationRules>
<local:SelectedValidationRule />
</Binding.ValidationRules>
</Binding>
</ListBox.SelectedItem>
</ListBox>

Related

Validation is not getting fired when setting updatesourcetrigger to lost focus

I am using below code to validate textbox.
<TextBox.Text>
<Binding Path="Name" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True">
<Binding.ValidationRules>
<Validate:RquiredFiledValidation ErrorMessage="Please Provide Login Name"></Validate:RquiredFiledValidation>
</Binding.ValidationRules>
</Binding>
Above code is working fine but when changing updatesourcetrigger to Lostfocus it stop working.
<TextBox.Text>
<Binding Path="Name" UpdateSourceTrigger="LostFocus" ValidatesOnDataErrors="True">
<Binding.ValidationRules>
<Validate:RquiredFiledValidation ErrorMessage="Please Provide Login Name"></Validate:RquiredFiledValidation>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
Any help would be appreciated.
The spelling of Field is wrong:
<Validate:RquiredFieldValidation ErrorMessage="Please Provide Login Name"></Validate:RquiredFieldValidation>
UPDATE:
From the UpdateSourceTrigger Enumeration page on MSDN:
PropertyChanged: Updates the binding source immediately whenever the binding target property changes, which means that the source value will be updated each time the bound property changes.
whereas
LostFocus: Updates the binding source whenever the binding target element loses focus i.e., the source value will be updated each time the destination control loses focus. If you don’t make any property change on target, source will stay the same.
As Jim Zhou says here, we could invoke the BindingExpression.UpdateSource method to force the data source to reset. So in this case, we can hook up LostFocus event on the TextBox and invoke the
BindingExpression.UpdateSource method explicitly. Something like this:
private void OnLostFocus(object sender, RoutedEventArgs e)
{
TextBox text = sender as TextBox;
BindingOperations.GetBindingExpression(text, TextBox.TextProperty).UpdateSource();
}
Also have a look at ValidatesOnTargetUpdated property of ValidationRule. It will validate when the data is first loaded. This is good if you're trying to catch empty or null fields. Than you can add something like this in your Binding Validation rule:
<DataErrorValidationRule ValidatesOnTargetUpdated="True" />
Not much related but you may like dkozl's answer.
Hope that helps.:)

WPF: Initializing a TextBox and binding it to a validation rule

I try to validate the IP-Address a user enters into a text box of a WPF Dialog. The text box is supposed to be initialized with 127.0.0.1. This is the XAML:
<TextBox
Height="23"
Width="98"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Name="ip_address"
Text="127.0.0.1">
<TextBox.Text>
<Binding Path="Left" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:IPValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
This attempt to bind the text box to the validation rule causes an error, because the attribute Text already has the value 127.0.0.1. My question is this: How can I achieve initializing and binding simultaneously?
Regards, RSel
PS: Initializing the text box in Window_Loaded doesn't work either. The box just remains empty. Without the binding to the rule it works.
A couple options:
Set an initial value in the property that the textbox is bound to. The binding should pick this up when the control loads. I'm not sure if this meets your goals though.
Use the TargetNullValue property of the binding object to specify what to show when the source is null.
Here's MSDN on option 2:
http://msdn.microsoft.com/en-us/library/system.windows.data.bindingbase.targetnullvalue.aspx

WPF/XAML: ExceptionValidationRule different when applying in code vs markup?

I've run across the need to apply the ExceptionValidationRule to many textboxes on a form in WPF. I can do this with markup and I get the desired result (the textbox gets a red outline when an invalid value is entered) but only when I supply the rule in markup:
<TextBox x:Name="Name" Width="150" >
<TextBox.Text>
<Binding Path="Name" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" NotifyOnValidationError="True">
<Binding.ValidationRules>
<ExceptionValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
But when I apply the value using code:
Name.GetBindingExpression(TextBox.TextProperty).ParentBinding.ValidationRules.Add(new ExceptionValidationRule());
I don't get the desired results. This code is applied in a userControl's constructor after the InitalizeComponent() call. The user control has the textbox "Name" as a child control.
I've gone through and I can see, when using both, that two validation rules are put in the ValidationRules collection but when I am using just the code version I don't get the desired result of a red outline around the textbox when an invalid value is entered.
Am I just misunderstanding a fundamental rule to WPF?
Or, is there a way I can apply this validation rule using a Style? I'd prefer that, to tell you the truth.
Thanks,
M
You can't change a Binding after it has been used, and apperently that goes for the ValidationRules as well. You can create a new Binding in code but that's probably not what you're after.
Binding binding = new Binding("Name");
binding.Mode = BindingMode.TwoWay;
binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
binding.NotifyOnValidationError = true;
binding.ValidationRules.Add(new ExceptionValidationRule());
nameTextBox.SetBinding(TextBox.TextProperty, binding);
A Style won't work either since a Binding or ValidationRule doesn't derive from FrameworkElement. What I would do in your situation is a subclassed Binding where you add all the things you need. Something like this
<TextBox x:Name="Name" Width="150" >
<TextBox.Text>
<local:ExBinding Path="Name"
Mode="TwoWay"
UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
</TextBox>
The ExBinding, adding ValidationRule etc.
public class ExBinding : Binding
{
public ExBinding()
: base()
{
NotifyOnValidationError = true;
ValidationRules.Add(new ExceptionValidationRule());
}
}

WPF Textbox Binding not Responding when ValidationRule Fired

I have a textbox that has a ValidationRule applied to it:
<TextBox Style="{StaticResource StandardTextBox}"
Grid.Column="1" Grid.Row="4"
IsReadOnly="{Binding SaveModeText}"
MaxLength="50">
<TextBox.Text>
<Binding Path="Individual.SurName"
UpdateSourceTrigger="PropertyChanged"
ValidatesOnDataErrors="True"
ValidatesOnExceptions="True"
NotifyOnValidationError="True">
<Binding.ValidationRules>
<valid:RequiredTextBoxValidationRule
ErrorMessage="Please enter a last name" />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
When the page loads the textbox contains the correct value based on its binding. If I delete the value from the textbox the ValidationRule fires properly and I see the error message as expected. My application contains a "Discard Changes" button which reloads the DataContext. The hope was it would reset all of the bindings and once again this textbox would display the original value. For some reason, all other values on the page that do not have a ValidationRule associated with them get reset properly, but this textbox does not.
If I remove the ValidationRule from the XAML the value resets properly. If I handle validation through IDataErrorInfo, the validation fires properly and the value resets properly. Because I have used ValidationRules throughout my application, I was wondering if anyone had come across this issue and resolved it. At this point I would prefer to stick with the implemented ValidationRules if possible, instead of switching everything over to IDataErrorInfo.
Since your you are modifying the value in code, wouldn't you need to have Mode=TwoWay in your binding for it to update? I don't have time to dig in and see if that's what's wrong, but it may be a place to start.

set button datacontext to multiple elements

I've got a button that I need to be disabled when validation errors occur in my window. The items on which these errors can occur are all textboxes.
I've bound my Button's datacontext as such:
DataContext="{Binding ElementName=txtEmail}"
Now with this, I can set the button style to disabled when validation errors occur in the email textbox, but I want to do it also when it occurs in other textboxes in my window?
How can I set this binding to multiple textboxes?
You can't, at least not directly. You could use a MultiBinding with all of the desired text boxes as inputs, but you will need to provide an IMultiValueConverter to "combine" the various text boxes into one object (such as a list):
<Button>
<Button.DataContext>
<MultiBinding Converter="{StaticResource ListMaker}">
<Binding ElementName="txtEmail" />
<Binding ElementName="txtFirstName" />
<Binding ElementName="txtLastName" />
</MultiBinding>
</Button.DataContext>
</Button>
And it is then that resulting list object that will be passed to your trigger, so you won't be able to access the Validation.HasError property directly: your DataTrigger will also need to bring in a converter which converts the list object into a boolean indicating whether Validation.HasError is set for anything in the list. At this point you might as well just forget about triggers and bind IsEnabled using a MultiBinding:
<Button>
<Button.IsEnabled>
<MultiBinding Converter="{StaticResource AllFalse}">
<Binding Path="(Validation.HasError)" ElementName="txtEmail" />
<Binding Path="(Validation.HasError)" ElementName="txtFirstName" />
<Binding Path="(Validation.HasError)" ElementName="txtLastName" />
</MultiBinding>
</Button.DataContext>
</Button>
(Here the AllFalse converter returns true if all inputs are false, and false if any input is true.)
A better approach, however, may be, instead of binding the Button directly to other UI elements, have your data object -- the same object that your text boxes are binding to -- expose an IsValid property (with suitable change notifications), and bind your Button.IsEnabled to that:
<Button IsEnabled="{Binding IsValid}" />
This moves you towards a MVVM-style solution which helps with things like testability (e.g. it's easy to create tests for the IsValid property; it's much harder to create tests for Button.IsEnabled).
For the MVVM approach you could try implementing a command router from ICommand.
<Button Command="{Binding Path=Commands.MyButtonCommand}" Style="{StaticResource MyButtonStyle}" ></Button>
where the Commands property is part of the ViewModel. You then have control over what functionality the command implements as well as whether it is enabled or not. Testing is then a whole lot easier.

Resources