I've got a simple WPF dialog with these two controls:
<TextBox Text="{Binding MyText}"/>
<Button Command="{Binding MyCommand}" IsDefault="True"/>
Now, when I enter some text in the TextBox and click the button using the mouse, everything works like expected: the TextBox will set MyText and MyCommand is called.
But when I enter some text and hit enter to "click" the default button, it does not work. Since on hitting enter the focus does not leave the TextBox, the binding will not be refresh MyText. So when MyCommand is called (which works), MyText will contain old data.
How do I fix this in MVVM? In classic code-behind I probably just would call "MyButton.Focus()" in the MyCommand handler, but in MVVM the MyCommand handler does know nothing about the button.
So what now`?
Add the UpdateSourceTrigger to your TextBox with the value PropertyChanged. The default behavior of the Textbox is to update the source, when it´s lost focus.
<TextBox Text="{Binding MyText, UpdateSourceTrigger=PropertyChanged}"/>
Try this. This code moves focus on the button clicked. Thus binding completes before command processed.
public App()
{
EventManager.RegisterClassHandler(typeof(Button), Button.ClickEvent, new RoutedEventHandler(GenericButtonClickHandler));
}
void GenericButtonClickHandler(object sender, RoutedEventArgs e)
{
var button = sender as Button;
if (button == null)
return;
if (button.IsDefault)
button.Focus();
}
One Solution ist, to create your own Class OKButton that calls Me.Focus in the OnClick-Method. This will be called before the Click_Event and before any Command that is bound to the button. You just have to remember to use an OKButton instead of setting IsDefault=True
Public Class OKButton
Inherits System.Windows.Controls.Button
Public Sub New()
MyBase.New()
Me.Content = "OK"
Me.IsDefault = True
End Sub
Protected Overrides Sub OnClick()
Me.Focus()
MyBase.OnClick()
End Sub
End Class
Related
question edited to provide a better explanation
I am using a treeview consisting of a stackpanel with a textblock and a textbox inside. What I would like to achieve is the total selection of the text when the textbox appears.
The textblock disappears by double-clicking or selecting an option from a context menu, giving visibility to the textbox to rename the item.
I'd like to have the selectall on the MouseLeftButtonDown on the textblock and also on click on a context menu option.
My treeview is contained in the MainWindow and, the stackpanel (with the text block and the textbox) is in a different file and I dynamically add it to the tree view depending on the user's action.
When I click on the StackPanel the first click highlights it, on double-click it opens a page and, on the MouseLeftButtonDown (and on click in a contextmenu option) I change the visibility of the textblock with the textbox and here I want the selectall() event to get fired.
I tried the following code and it only works halfway:
private void mniRename_Click(object sender, RoutedEventArgs e)
{
prevSelected.MyTextBlock.Visibility = Visibility.Collapsed;
prevSelected.MyTextBox.Visibility = Visibility.Visible;
prevSelected.MyTextBox.Focus();
if (prevSelected.MyTextBox.IsFocused)
{
prevSelected.MyTextBox.SelectAll();
}
prevSelected.MyTextBox.Text = prevSelected.MyTextBlock.Text;
}
The issue is that the SelectAll() event doesn't work on the first click while the Focus() works, then on the following clicks everything works fine.
The code is always executed in the same way.
Does anyone have any idea why this happens?
No really sure what you want to achieve. but what you describe can be achieved by the following code:
XAML
<StackPanel Orientation="Horizontal">
<TextBox x:Name="MyTextBox" LostFocus="MyTextBox_OnLostFocus" Width="100"/>
<TextBlock x:Name="MyTextBlock" Text="{Binding ElementName=MyTextBox, Path=Text}" MouseLeftButtonDown="MyTextBlock_OnMouseLeftButtonDown"/>
</StackPanel>
C#
private void MyTextBox_OnLostFocus(object sender, RoutedEventArgs e)
{
MyTextBox.Visibility = Visibility.Hidden;
MyTextBlock.Visibility = Visibility.Visible;
}
private void MyTextBlock_OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
MyTextBlock.Visibility = Visibility.Collapsed;
MyTextBox.Visibility = Visibility.Visible;
MyTextBox.Focus();
MyTextBox.SelectAll();
}
I'm relatively new to WPF and I am struggling to manage the focus of an element at runtime.
I have a simple user control with a TextBox inside
<UserControl [...]
IsVisibleChanged="UserControl_IsVisibleChanged">
[...]
<TextBox x:Name="myTextBox" [...] />
</UserControl>
That I added on my WPF window
<ctrl:MyPanel
x:Name="myPanel"
Visibility="{Binding MyBooleanProperty}"
Panel.ZIndex="999" />
MyBooleanProperty is changing at runtime under some logic and the panel is showing up accordingly.
I need to have keyboard focus on myTextBox everytime myPanel becomes visible so user can enter data without using mouse, tab key or anything else.
Here's the logic on the event handler of IsVisibleChanged
private void UserControl_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (this.Visibility == Visibility.Visible)
{
myTextBox.Focus();
myTextBox.SelectAll();
}
}
This works, but if I click any button on the window before myPanel becomes visible then I cannot set focus in myTextBox.
I've tried many things, for example setting
Focusable="False"
on the buttons with no luck.
Thanks in advance for your help!
After a little more searching I found a workaround based on this answer by Rachel:
Dispatcher.BeginInvoke(DispatcherPriority.Input,
new Action(delegate () {
myTextBox.Focus();
Keyboard.Focus(myTextBox);
myTextBox.SelectAll();
}));
Delegating the focus action actually works.
I am working with UWP app using MVVM pattern, below is my code snippet to set the focus, but it not setting the focus to textbox.
XAML
<TextBox FontWeight="Bold" FontSize="32" Text="{Binding ProSeg1, Mode= TwoWay}" TextAlignment="Center" MaxLength="3"
x:Name="txtSeg" KeyUp="txtSeg_KeyUp"
Style="{StaticResource textboxTemplate}" Width="105" />
UserControl.xaml.cs
//this code is executed from constructor.
bool val = txtSeg.Focus( FocusState.Keyboard);
Variable val is always returned false. In another instance, the same code has used for another TextBox, but it is triggered by a Button event and it works fine.
Calling Focus in constructor is too early, as the control is not yet ready to receive focus and will always return false. You must wait until the user control or at least the TextBox is fully loaded. In the constructor attach the Loaded event:
this.Loaded += UserControl_Loaded;
And add the following event handler:
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
txtSeg.Focus(FocusState.Programmatic);
}
Also note that you should use FocusState.Programmatic as the Keyboard is reserved for when the control gets focus naturally using the Tab key whereas Programmatic is for setting focus in code.
You should check whether or not your XAML has been loaded. If you set the focus in the Window constructor the view won't be loaded so you will have to put your bool val = txtSeg.Focus( FocusState.Keyboard); in a Loaded event.
txtSeg won't be focusable until the window is loaded
You could attach to Window loaded or to Textbox Loaded to focus to your textbox
I have a situation when I want to detect when a TextBox, anywhere in the application, has been brought into focus by the user clicking on it with the mouse, or touch. I have "solved" this by adding a global event handler like this:
Application.Current.MainWindow.AddHandler(UIElement.MouseLeftButtonUpEvent, new MouseButtonEventHandler(txt_MouseLeftButtonUp), true);
...
void txt_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (e.OriginalSource is TextBox)
{
// Do somthing
}
}
However, if the user clicks the edges of a textbox instead of in the middle, quite often an hosting control (Grid, Border etc.) receives the mouse event and somehow passes this on to the contained TextBox, that will handle it and receive focus. This makes the approach above fruitless as the TextBox will not be the e.OriginalSource in this case and I have found no way of identifying that a TextBox was brought into focus by click.
Deriving TextBox and overriding OnMouseDown for instance, will catch this event and I guess this path could be explored to find a solution to the problem but that would require me to use a custom TextBox everywhere.
Anyone out there with a nice solution for this?
This is an example that will trigger the problem. By clicking the edges of the TextBoxes, the grid will handle the mouse event and focus will be passed on to the TextBox.
<Grid>
<Grid HorizontalAlignment="Center"
VerticalAlignment="Center"
Background="Red">
<TextBox>2323</TextBox>
</Grid>
<Grid Margin="200,0,0,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Background="Red" Focusable="False">
<TextBox>2323</TextBox>
</Grid>
</Grid>
The GotMouseCapture event seems to work:
AddHandler(UIElement.GotMouseCaptureEvent,
new MouseEventHandler(OnGotMouseCapture), true);
...
void OnGotMouseCapture(object sender, MouseEventArgs e)
{
if (e.OriginalSource is TextBox)
{
// ...
}
}
When I click on TextBox elements with the mouse this event handler is fired, however focus changes made via keyboard do not fire the event.
Simply handle the GotFocus event for each TextBox.
I have the following in my XAML:
<TextBox Name="TitleValue"
Text="{Binding ElementName=ListValue, Path=SelectedItem.Title, Mode=TwoWay}"
<TextBox Name="DescValue"
Text="{Binding ElementName=ListValue,
Path=SelectedItem.Description, Mode=TwoWay}"
When I enter a value in either TitleValue or DescValue and Tab or click on a different location in the window, the collection class properties are updated correctly. Then when I click the X to exit the window, the data in the collection class is saved correctly.
However, when I enter a value in either of those fields, then click X to exit the window, the data in the collection class is saved, but the value entered is not saved for that particular field I was editing when I clicked X.
I implemented a lose focus event on the fields and they work, even when the X is clicked. In those events I put the code:
private void Event_DescValue_LoseFocus(object sender, RoutedEventArgs e)
{
((Import)ListValue.SelectedItem).Description = ((TextBox)e.OriginalSource).Text;
}
But that didn't work either. the following does not work either:
Imports[ListValue.SelectedIndex].Description = ((TextBox)e.OriginalSource).Text;
How do I get the collection class Imports to update the selected class Import when X is clicked when in a field being edited?
It should work exactly like I had Tabbed off the field being edited or clicking elsewhere in the window.
Modify your binding's UpdateSourceTrigger to PropertyChanged.
Like so:
<TextBox Name="TitleValue" Text="{Binding ElementName=ListValue,
Path=SelectedItem.Title, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>