I got a text box on which I added a validation rule in the xaml. The rule works and it detects errors, but only after the user gives the focus to some other element in the window, like another text box.
This is the defintion:
<TextBox x:Name="textBoxLongitude" Grid.Row="1" Grid.Column="1" Margin="0,0,0,10" VerticalContentAlignment="Center">
<TextBox.Text>
<Binding Path="CustomerLongitude">
<Binding.ValidationRules>
<local:IsDoubleValidationRule MaxValue ="180"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
I've tried solving the issue by adding this to the xaml of the text box:
TextChanged="textBoxTextChanged"
And the implementation:
private void textBoxTextChanged(object sender, TextChangedEventArgs e)
{
CommandManager.InvalidateRequerySuggested();
}
It didn't help..
How can I make the validation rule detect the error and when it's fixed even without the user needing to give the focus to another control?
Set the binding's UpdateSourceTrigger to PropertyChanged to commit and re-evaluate a bound value on every change instead of LostFocus.
Bindings that are TwoWay or OneWayToSource listen for changes in the target property and propagate them back to the source. This is known as updating the source. Usually, these updates happen whenever the target property changes. This is fine for check boxes and other simple controls, but it is usually not appropriate for text fields. Updating after every keystroke can diminish performance and it denies the user the usual opportunity to backspace and fix typing errors before committing to the new value. Therefore, the default UpdateSourceTrigger value of the Text property is LostFocus and not PropertyChanged.
This is how to do it: <Binding Path="CustomerLongitude" UpdateSourceTrigger="PropertyChanged">
Related
I have a combobox that is editable and a textbox.
<TextBox x:Name="textBox" HorizontalAlignment="Left" Height="23" Margin="86,149,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="120"/>
<ComboBox x:Name="comboBox" HorizontalAlignment="Left" VerticalAlignment="Top" Width="120" Margin="282,150,0,0" IsEditable="True" PreviewMouseDown="ComboBox_PreviewMouseDown"/>
I don't understand why ComboBox_PreviewMouseDown does not trigger, when the focus is on the textbox and I click on the combobox. It just highlights the text in the combobox and sets the focus. Clicking in the combobox when it already has the focus, PreviewMouseDown fires.
Is that what's happening here? Why is a PreviewMouseDown in an unfocused combobox not working?
When ComboBox.IsEditable is set to True, the ComboBox internally sets the focus (and keyboard focus) to the edit TextBox to make it instantly available for text input. This makes total sense as the intention when clicking the edit TextBox is always to enter or edit some text. Otherwise, the user would have to click the TextBox twice to make it receive focus for text input (keyboard focus).
So, to prevent focus stealing, the author marked the MouseDown event as handled i.e. RoutedEventArgs.Handled is set to true. (This is the reason why most non-preview events are marked handled by most controls).
Also, the author wanted to prevent the moving of the caret when clicked into the edit TextBox for the first time (to give it focus): the PreviewMouseDown event's RoutedEventArgs.Handled will only be set to true, if the edit TextBox has no keyboard focus and the drop-down panel is closed. (That's why the second click into the TextBox will pass through to be handled by an added event handler).
To achieve the behavior you expect, you have to handle the UIElement.PreviewGotKeyboardFocus event or the attached Keyboard.PreviewGotKeyboardFocusevent on the ComboBox.
Alternatively register the event handler using the UIElement.AddHandler method and set the handledEventsToo parameter to true:
this.MyComboBox.AddHandler(
UIElement.PreviewMouseDownEvent,
new RoutedEventHandler(MyComboBox_PreviewMouseDown),
true);
I ran into this same issue myself. A simple and effective workaround is to wrap your ComboBox in a lightweight ContentPresenter, then attach your PreviewMouseDown handler to that, like so:
<ContentPresenter x:Name="MyComboBoxWrapper"
PreviewMouseDown="MyComboBoxWrapper_PreviewMouseDown">
<ContentPresenter.Content>
<ComboBox x:Name="MyComboBox" />
</ContentPresenter.Content>
</ContentPresenter>
Additionally, since this control gets the PreviewMouseDown event before the ComboBox does, you not only can use it to pre-process events before the ComboBox even sees them, but you can cut off the ComboBox entirely by setting the event arg's handled property to 'true.'
Works like a charm! No subclassing or other tricks needed and it only requires a lightweight control in the tree!
Notes
As some may have considered, technically you could attach the PreviewMouseDown event to any ancestor of your ComboBox, but you then may have to include logic in that handler to determine if you're actually clicking on the ComboBox vs something else.
By using an explicit ContentPresenter (an incredibly lightweight element that itself doesn't have any rendering logic. It simply hosts other elements), you now have a dedicated PreviewMouseDown handler just for this control. Plus, it makes it more portable should you need to move it around since the two items can travel together.
I have a couple of textboxes which are set with complex Databinding in WPF.
This works fine when just selecting items from a listbox in viewsource. (using Observable collection)
However, I am having an issue when I press a certain button I want the textboxes to become blank or contain a zero. So I set the .Text in C# to 0 or blank depending on the textbox. However it seems by doing that it also removes my bindings on this element. How can I get my bindings back in the original state through C#?
Xaml example
<TextBox Name="BandNrTextBox" Width="200" IsReadOnly="True">
<TextBox.Text>
<Binding Path="BandNr" UpdateSourceTrigger="PropertyChanged" Mode="OneWay">
</Binding>
</TextBox.Text>
</TextBox>
After that I set the BandNr element in C#
BandNrTextBox.Text = "0";
Now when I select another option from my listbox normally it should get the correct value through the bindings again however it seems the Bindings are gone cause nothing appears in my textBlock.
This isn't a proper solution IMO, as you should be handling the values directly to achieve what you want. Turning the bindings on/off seems counter productive. With that said setting the .Text property changes the binding to a direct value. If you need to rebind you would do so like this.
Binding b = new Binding();
b.Source = YourViewModel; //Whatever contains BandNr
b.Path = new PropertyPath("BandNr");
b.Mode = BindingMode.OneWay;
b.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
BindingOperations.SetBinding(BandNrTextBox, TextBox.TextProperty, b);
Also a heads up, you can simplify your XAML
<TextBox Name="BandNrTextBox" Width="200" IsReadOnly="True" Text="{Binding BandNr, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"/>
In Prism application I want to using validation.And I have implement the INotifyDataError interface in my ViewModel ,but I found that the validate solution don't be fired when the control is loaded first time.
Then I found the same Question like 'wpf Validation Binding not fired on First Load'
I Found a solution to solve the problem WPF don't fired the validation when first load the datacontext is that:
<TextBox Grid.ColumnSpan="2" Grid.Row="1" x:Name="textBoxFolder" Margin="2,4">
<TextBox.Text>
<Binding Path="this.MovieFolder" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<!-- Validation rule set to run when binding target is updated. -->
<Rules:MandatoryInputRule ValidatesOnTargetUpdated="True" />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
as you see,ValidatesOnTargetUpdated="True" is the key point ,this property will make WPF fired the validation when the datacontext load first time.
But I think that's a ugly solution. I need to add a Binding.ValidationRules for every control I want to validate.
Is there a good way to solve the problem.
OK I've solved it: You force the validation when the element got bound with a simple property - ValidatesOnTargetUpdated:
<rules:MyValidationRule ValidatesOnTargetUpdated="True" ValidationType="notnull"/>
I'd like to use the ItemsSource property from a particular element as one of the bindings in another element's MultiBinding. Here's what I have so far:
<Label>
<Label.Content>
<MultiBinding Converter="{converters:myMultiValueConverter}">
<Binding Path="PageIndex" />
<Binding ElementName="anotherElement" Path="ItemsSource"/>
</MultiBinding>
</Label.Content>
</Label>
This works once (when the ItemsSource is initially set), but the binding fails to update when the ObservableCollection bound to the original element's ItemsSource property has items added or removed. Is this kind of binding possible?
Add a dummy binding (- you don't need the value -) like this to force the MultiBinding to be reevaluated:
<Binding ElementName="anotherElement" Path="ItemsSource.Count"/>
Edit: Just noticed a flaw: If you move items that would not register if that does not change the Count property in-between, maybe this is relevant for you. In that case you could bind to your own dummy for which you can fire change notifications upon CollectionChanged (not all that clean in any case though).
You might want to consider HighCore's suggestion, a get-only property that returns the calculated value for which you manually fire PropertyChanged in all places that it depends on is usually quite convenient.
The Problem:
I am creating user control that handles data conversions (via a converter/validation rule). This works 100% as desired, but the validation only fires when the control is bound to something, which is not always the case.
Is there a way to force validation even if the control is not bound? OR is there a way to set up basically a dummy binding. (The solution needs to be done in code so that the end result is a drag and drop user control with no xaml customization needed by the programmer.
Thanks in advance for any advice.
EDIT : Really the code in question is this:
Binding TextBinding = BindingOperations.GetBinding(this, TextBox.TextProperty);
TextBinding.ValidationRules.Add(MyValidationRule);
This is how I am assigning my validation rule, but it will only work if the TextBinding is not null. So I either need a dummy binding for my TextBox, or another way to add the validation rule.
Sounds to me like what you want to be doing is defining a dependency property on your user control which the XAML in your user control binds to. This binding would incorporate your validation rule. Consumers of your user control would then bind the property on your user control to whatever they want.
It's really hard to be more specific than that without your exact use case, but consider this:
CoolUserControl.xaml.cs:
public class CoolUserControl : UserControl
{
public static readonly DependencyProperty CoolProperty = ...;
public string Cool
{
// get / set
}
}
CoolUserControl.xaml:
<UserControl x:Name="root" ...>
<TextBox>
<TextBox.Text>
<Binding Path="Cool" ElementName="root">
<Binding.ValidationRules>
<!-- your rule here -->
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</UserControl>
SomeConsumer.xaml:
<local:CoolUserControl Cool="{Binding SomePropertyOnMyViewModel}"/>