Explicit Binding UpdateSource() Doesn't Work Until Mouse Hovers Over TextBox - wpf

So I'm calling the UpdateSource() method on Text property of TextBox in code behind. The ErrorTemplate should come up, but it doesn't until I move my mouse over the TextBox.
Or maybe it does but doesn't get repainted? Any ideas how to fix this to update GUI instantly?
EDIT:
It is PropertyChanged. The problem is not with updating source. The problem is that when source updates, it causes validation and the ErrorTemplate should come up, but it doesn't until I move my mouse over validated TextBox.
EDIT:
Appearently it does update when I move my mouse over some other GUI elements as well (like radio button), which doesn't have anything to do with validation. This is definitely an issue of repainting or binding validation error check trigger. How can I trigger that in code behind?

Binding to Text property of 'TextBox' control is quite specific. UpdateSourceTrigger is set to "LostFocus" by default. It is for improving performance. Try to change it to PropertyChanged.

You may be able to get Validation_Error to fire.
<TextBox Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" x:Name="fieldValue" BorderBrush="SteelBlue" BorderThickness="2" TextWrapping="Wrap"
Text="{Binding Path=DF.FieldValue, Mode=TwoWay, ValidatesOnExceptions=True, NotifyOnValidationError=True, UpdateSourceTrigger=Explicit}"
Validation.Error="Validataion_Error"
LostFocus="fieldValue_LostFocus" KeyUp="fieldValue_KeyUp"/>
private void Validataion_Error(object sender, ValidationErrorEventArgs e)
{
if (e.Action == ValidationErrorEventAction.Added)
{
MessageBox.Show(e.Error.ErrorContent.ToString(), "Fatal Update Error");
}
}
It may be that since that event is on the TextBox is only fires when the TextBox has focus. You may need to throw a custom event and handle it at the Page/Window.

Related

How to override keybinding in a control in wpf?

I have the following code which uses Microsoft's WPFToolkit AutoCompleteBox. I have tried adding an input binding inside it
xmlns:tk="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Input.Toolkit"
<tk:AutoCompleteBox IsTextCompletionEnabled="True" FilterMode="Contains" ItemsSource="{Binding DistinctItemNames, Mode=OneWay}"
SelectedItem="{Binding SelectedItemName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
x:Name="searchBox" Width="300" Height="23" VerticalContentAlignment="Center" >
<tk:AutoCompleteBox.InputBindings>
<KeyBinding Key="Return" Command="{Binding ShowSelectedItemsCommand}"/>
<tk:AutoCompleteBox.InputBindings>
</tk:AutoCompleteBox>
However, it doesn't work. I expect that the control itself handles the 'Enter' or 'Return' key so how can I override its default function?
I have also put that keybinding directly under the 'UserControl.InputBindings' and it also did not work. I hate to use Code Behind to handle the command logic.
You could try to handle the PreviewKeyDown event, either directly in the code-behind of the view:
private void AutoCompleteBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
AutoCompleteBox box = sender as AutoCompleteBox;
dynamic viewModel = box.DataContext;
viewModel.ShowSelectedItemsCommand.Execute(null);
}
}
...or by wrapping it in an attached behaviour: https://www.codeproject.com/Articles/28959/Introduction-to-Attached-Behaviors-in-WPF.
Neither approach breaks the MVVM pattern. In the first case you are just invoking the exact same view model command from the exact same view. But if you really "hate to use code-behind" for some strange reason, then create an attached behaviour.
Try having a look at this post: ReactiveCommand pass Command Parameter.
It uses Reactivity to accomplish the same thing you want.
Then, you can process the key received via KeyCode, and check if it is the key you want.

How to override PreviewKeyDown on a TextBox?

I have a handler for the PreviewKeyDown event on a TextBox inside a control I made, which checks to see if the user has pressed the down key. The event handler correctly handles the key press when the control sits inside a layout container like a grid. If however, I place the control inside a DataGrid's DataGridTempalteColumn, the control does not do what I need it to do.
I think the issue is that because the PreviewKeyDown is on a Tunneling strategy, the host DataGrid gets to handle the down arrow key press before my control does. For the down arrow the DataGrid moves the focus to the next row. The DataGrid doesn't seem to be setting the IsHandled to true, because it the event eventually gets down to my control, but it does nevertheless do its own thing on the event, which breaks things for me.
The issue isn't really with the DataGrid, but with the fact that my control has a tunneling PreviewKeyDown event from the TextBox. I'm looking for a way to override this default event on the TextBox. Perhaps there's something I can do with attached behaviors? Maybe I need to inherit from the TextBox and then override? So far I've not found anything that indicates how to handle a situation like this.
Below is the original text for this question that didn't yield any answers
I'm having difficulty using a custom autocomplete text box I made as a DataTemplate in a DataGridTemplateColumn.
<DataGrid.Columns>
<DataGridTemplateColumn Header="Material" Width="300">
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<local:actextbox Text="{Binding Path=Description, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
MatchList="{Binding Path=DataContext.LaborTemplatesList, RelativeSource={RelativeSource AncestorType=UserControl, AncestorLevel=2}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Description}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
The actextbox class derives from user control and has event handlers to respond to certain key presses like so
private void myTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Down & myPopup.IsOpen == true)
{
myPopUpList.SelectedIndex = 0;
ListBoxItem lbi = (ListBoxItem)myPopUpList.ItemContainerGenerator.ContainerFromItem(myPopUpList.SelectedItem);
lbi.Focus();
e.Handled = true;
}
}
The intent is that when an autocomplete popup is displayed, pressing down and up allows the user to navigate its contents. This works as expected when the control is placed in a hierarchy of layout containers; however, when it is part of the cell in a datagrid the expected behaviour is lost. Looks like the previewKeyDown is used by the DataGrid to apply its own interpretation of the down or up arrows, and while it does not set the event as handled, by the time the event gets down to my control focus is lost, and different row is selected.
I've looked online all over, and wasn't able to find any clues on how to handle this. Certainly, I've seen controls inside DataGridTemplateColumns handle all sorts of inputs, but how they accomplish this is lost on me.
OK, following some advice to use Snoop I figured out what was happening. In fact the issue was not that the DataGrid was doing something with the PreviewKEyDown event, but that I was moving focus away from the data grid cell that was presently being edited triggering a CellEditEnding event. This resulted in the behaviour I was observing.

databinding and focus coordination

I have several controls including a DataGrid that I want to be disabled until there is a valid value in the first TextBox in the presentation. So I added a boolean property to bind to in the VM and bind to it in the xaml (below).
The binding works, but has the side effect of 'trapping' the user in the TextBox (MoneyToAllocate).
Presumably this is because the TB binding is LostFocus and there is no place for the focus to go and actually trigger the updates. What's a good way to fix this?
Cheers,
Berryl
ViewModel
public bool HasMoneyToAllocate { get { return MoneyToAllocate.Amount > 0; } }
public Money MoneyToAllocate {
get { return _moneyToAllocate; }
set {
if (value.Amount < 0) return;
_moneyToAllocate = new Money(value.Amount, SelectedCurrency);
NotifyPropertyChanged(() => HasMoneyToAllocate);
}
}
View
<TextBox Text="{Binding MoneyToAllocate, Converter={StaticResource moneyConverter}}" />
<DataGrid IsEnabled="{Binding HasMoneyToAllocate}" ...
EDIT
I should have added that I tried PropertyChanged for update but it gets a bit messy since the value of the text box needs to be formatted by the converter. Any other ideas?
FINAL EDIT
I wound up letting another control that previously wasn't a tab stop be a tab stop, so the text box had a place to go. Phil understood the problem best and gets the answer, even though the range of values the user can input (.001 to decimal.MaxValue) make an up-down impractical.
Use UpdateSourceTrigger=PropertyChanged
<TextBox
Text="{Binding MoneyToAllocate, UpdateSourceTrigger=PropertyChanged,
Converter={StaticResource moneyConverter}}" />
Then you have to use UpdateSourceTrigger=PropertyChanged
- if you use that binding you are using the value in the VM will not effected till the focus moves from the textBox
- but if you add UpdateSourceTrigger=PropertyChanged to your binding the VM property (MoneyToAllocate) will effected immediately (when the textBox.Text value changed)

AutoCompleteBox with TextChanged event not selecting properly

Hi I'm using an AutoCompleteBox like this
<!-- XAML Code -->
<sdk:AutoCompleteBox Grid.Row="2"
FilterMode="None"
ItemsSource="{Binding Customers}"
SelectedItem="{Binding Path=SelectedCustomer, Mode=TwoWay}"
Text="{Binding CustomerSearchString, Mode=TwoWay}"
ValueMemberBinding="{Binding Path=FullName}"
ValueMemberPath="FullName"
TextChanged="{ext:Invoke MethodName=Search, Source={Binding}}"/>
C# part:
// Search Method in the viewmodel
public void Search()
{
var customerOperation = _context.Load(_context.GetCustomerByNameQuery(CustomerSearchString));
customerOperation.Completed += (s, e) => Customers = new List<Customer>(customerOperation.Entities);
}
In my app to quick-search for customers for an fast and uncomplicated method to search. I get it to display everything correctly in the dropdown, and when I select with the mouse it works perfectly.
But when I press ArrowDown, you see the text come up for a split-second but then it reverts and puts the cursor back in the textbox instead of selecting the first entry down. I tried using the TextInput event, but that one won't fire.
How can I avoid this behaviour?
SOLUTION:
The problem was, that the TextChanged event got fired when the user selected an entry, creating some sort of race condition like behaviour where the Text got reset. The solution was to use the KeyUp event (don't use KeyDown, because the Text property won't be updated yet). This event doesn't get triggered when the user selects something, solving the problem.
Final code (ViewModel unchanged):
<!-- XAML Code -->
<sdk:AutoCompleteBox Grid.Row="2"
FilterMode="None"
ItemsSource="{Binding Customers}"
SelectedItem="{Binding Path=SelectedCustomer, Mode=TwoWay}"
Text="{Binding CustomerSearchString, Mode=TwoWay}"
ValueMemberBinding="{Binding Path=FullName}"
ValueMemberPath="FullName"
KeyUp="{ext:Invoke MethodName=Search, Source={Binding}}"/>
Thanks everyone!
Add a handler like this in code:
KeyEventHandler eventHandler = MyAutoCompleteBox_KeyDown;
MyAutoCompleteBox.AddHandler(KeyDownEvent, eventHandler, true);
I'm not understanding why you are using a TextChanged event...? What is that for? If you take that out, does it work? I use an autocomplete box in my project and I don't need a search method... all I do is just supply a list of objects to the autocompletebox and the it searches that list when the user types. I can select either by mouse or by up/down arrows. The only thing that I can think of is that each time you try to use the up/down arrow the text changes and fires off the search function and closes the selection option drop down...

INotifyPropertyChanged problem

At first I want to say that sample below is oversimplification.
Suppose you have bound WPF control.
<Window Title="Window1" Height="300" Width="300">
<Grid>
<StackPanel>
<TextBox Text="{Binding Name}" Margin="10"/>
<Button HorizontalAlignment="Center"
Content="Click Me" Margin="5"
Padding="2" Click="OnButtonClick" />
</StackPanel>
</Grid>
</Window>
Window is bound to the Person class which implements INotifyPropertyChanged and has Name setter in form
public string Name
{
get { return _name; }
set
{
_name = "Some Name";
OnPropertyChanged("Name");
}
}
I.e. _name is assigned "Some Name" whenever user tries to change it from UI.
But this sample does not works. I changed name in TextBox to some value press tab forcing focus to move to the Button and value in TextBox remains unchanged although PropertyChanged event was triggered.
Could you please explain me why it happens? As I understand PropertyChanged event forces UI to reread values from properties and display them but in my example value in databound textbox is not updated.
Again. I understand that this is poor implementation of the property and but I want to repeat that this is oversimplification.
It is just a sample.
But anyway, PropertyChanged signals that property was changed and should be updated but it does not.
The PropertyChanged event is ignored by the TextBox because it is the initiator of the event.
Some clarification:
The TextBox (or the binding on the textbox) knows it is the initiator because it receives the PropertyChanged event in the same call. By doing an asynchronous call, the textbox (or binding) has no way to know that it is the initiator, so it will process the event as if someone else has updated it
If you add a 2nd textbox to your UI, you'll see that the 2nd TextBox does change when you edit the 1st, and the other way around.
The dummy converter workaround suggested by Heinzi (described here) doesn't work when binding's UpdateSourceTrigger is PropertyChanged. But what if this is what we need?
It seems that making the binding asynchrounous does the trick, e.g.:
SelectedIndex="{Binding SelectedIndex, IsAsync=True}"
As Bubblewrap already pointed out, this is by design -- the textbox assumes that if it sets a bound property to some value, the setter will not change the value. According to Microsoft, they won't change this behavior since this would break existing code.
If you want to change the value (yes, there are perfectly good reasons for doing that), you have to use a workaround, for example, by adding a dummy converter. There is a blog entry (not written by me) describing this technique in detail.
The reason is because you have hardcoded the 'Some Name' in the setter. When you changed the textBox value the setter is actually getting called and it again setting "Some Name" as the propertyValue so it doesnt seems to be changed in the UI.
Put _name = value and things will just work as you expected,
public string MyField
{
get { return _myField; }
set {
if (_myField == value)
return;
_myField = value;
OnPropertyChanged("MyField");
}
}
This is the proper implementation of the property.
When you change the property, make sure that the EXACT same instance of the object is binded to a control. Otherwise, the change will be notified but the control will never get it because the control is not binded properly.
Replacing setter in form
set
{
_name = "Some Name";
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.DataBind,
(SendOrPostCallback)delegate { OnPropertyChanged("Name"); },
null);
}
resolves the issue but it is still open. Why should I make async call instead of synchronous signaling that my property has been changed.
If I am not mistaken, the default binding behavior of the Text property on the TextBox is TwoWay, so this should work. You can force it to be TwoWay in the XAML like this:
<Window Title="Window1" Height="300" Width="300">
<Grid>
<StackPanel>
<TextBox Text="{Binding Name, Mode=TwoWay}" Margin="10"/>
<Button HorizontalAlignment="Center"
Content="Click Me" Margin="5"
Padding="2" Click="OnButtonClick" />
</StackPanel>
</Grid>
</Window>
Note the Mode=TwoWay in the Binding declaration.
If that doesn't work, then I suspect that an exception is being thrown in the code that fires the event, or assigns the property and you should look for that.
There seems to be a possibility that you are making the call to change the value on a thread that is not the UI thread. If this is the case, then you either have to marshal the call to fire the property changed event on the UI thread, or make the change to the value on the UI thread.
When an object is bound to a UI element, changes to the object which can affect the UI have to be made on the UI thread.

Resources