give focus to textbox when control is shown - wpf

There is a custom UserControl which contains a TextBox. How do I give focus to the textbox when UserControl is shown? I've tried calling TextBox.Focus on UserControl.IsVisibleChanged and TextBox.IsVisibleChanged event, but that didn't help. What else can I try?
It seems that something causes TextBox to loose focus. The approach that I've mentioned normally works. How can I find out what causes the TextBox to loose focus? I've tried listening to TextBox.LostFocus event, but it's parameters don't contain much valuable information and I also don't see previous methods in the stack trace.
The code:
void TextBox1_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (this.TextBox1.IsVisible)
this.TextBox1.Focus();
}
As I've said before, it works if I use same code on a similar scenario in a test project, but it doesn't work in my application (the application is big and I am basically fixing bugs in it, and this one is amongst them). So I think that the problem isn't that focus is set improperly (as I've thought when I was opening this question), I think that the problem is that something else resets the focus. I am trying to find what it is here: Find out why textbox has lost focus .

I'm not exactly sure why it doesnt come back on visibility changed... however when I did TextBox1.Focus() in IsEnabledChanged it worked like a charm.

Use the UC as the bridge, when you let the UC shown, then call some function to focus the TextBox.

Related

Databinding falls behind event notification - discussion

Found an interesting problem that I first found in WinForms, and found again in Silverlight, and more than likely WPF as well when it comes to databinding.
I have a tab control with several tabs. As users click across the tabs, each time should be valid before allowing the user to switch from the tab.
For example, user is in a text box which is updated. Binding of text boxes is not flushed until the control loses focus. Loss of focus occurs when the cursor is moved from the control, and focus is given to another control.
In this scenario, the user tabs into a control (let's use text box for this example), and updates the text box. At this point the databinding has not flushed the control, and hence the VM has not yet seen the change. The user then uses their mouse to click the next tab of the control.
At this point things get interesting. I used the PreviewSelectionChanged (Telerik RadTabControl), as I want to check things out before the jump to the next tab occurs, and it also gives me the ability to cancel the event.
However, when I look at the VM, in this event, it still does not have the updated data. I see the VM is clean, and go ahead and allow the jump to the next tab.
As soon as this event is over however, the databindings flush, and the VM gets updated. what now? The events are out of sync! When the mouse was used to click the next tab, the textbox should have lost focus, flushed it's bindings, before the Preview of the Tab click! It's to late to jump back and say oops we didn't catch that in time!
I think I found an interesting work around to this issue - but I'm not 100% sure it will work 100% of the time. I cancel the current event, but then I use the Dispatcher and create a delegate pointing to another method with the same signature as the current event. The Dispatcher will add this message to the message pump, which by this time will now (hopefully?) be behind the messages of the VM updating...
My two questions are:
1) I'm assuming that the textbox control either didn't flush when the mouse left the control, or the process that was fired was too slow and hence the preview message was on the pump before the databinding - either way I see this to be a major issue.
2) Is the workaround a good solution?
Ok, first to answer question 1:
Just because the mouse left the textbox area, doesn't mean that the textbox lost focus. It only loses focus once something else gets focus. For example, if you moved the mouse out of the textbox and click on some other control on your page (it can be anything from a scroll viewer to another textbox, etc.) then your textbox will lose focus.
Now, based on that, the events do not happen in the wrong order. What happens is; your click event on the other tab triggers both the textbox to lose focus (and the data binding to take place) and the move to the next frame, and based on that, you basically get a race condition where the moving to the next tab happens before the databinding takes place.
About question 2:
What you can do is, set the UpdateSourceTrigger to Explicit, you will however be forced to then have some kind of text_changed event and manually update the binding.
You can read more about that here. It might not be the most complete explanation but is a good place to start.
On the other hand, you can associate some events to the textbox and force the textbox to lose focus on those events (e.g. mouse out).
Just an idea: Why not do everything in the VM's PropertyChanged event?
protected override void OnThisViewModelPropertyChanged(object sender, PropertyChangedEventArgs e) {
if(e.PropertyName == "WhateverProperty") {
//Do your magic here for whatever you want to set
}
}
Have your TabItems bound to a collection that will control is being disabled or not.
<sdk:TabControl>
<sdk:TabItem IsEnabled="{Binding SomeProperty, Converter={AmIDisabledOrWhatConverter}}" />
</sdk:TabControl>
That way, everything is triggered whenever a property is chaned in the vm. No more timing issues since everything is on the vm.
Just my two cents.
There's a design defect here, and you're trying to work around the defect instead of fixing it. You shouldn't have to figure out how to cancel the Click event on the tab. The tab shouldn't be processing Click events in the first place.
Generally speaking, if it's not legal for the user to click on a control, the control shouldn't be enabled. The tab should be disabled until the state of the view model is valid.
Your view model should be exposing a command for navigating to the next tab, and the tab should be bound to the command. The command's CanExecute method should only return true when the state of the view model on the current tab is valid.
This doesn't fix your other problem, which is that Silverlight doesn't support UpdateSourceTrigger="PropertyChanged" out of the box. But that's a solved problem (here is one example).
Note that if you implement commands to handle this wizard-like navigation in your application, you can, down the road, change the view to use something other than a tab control (e.g. to use navigation buttons like an actual wizard, or something like Telerik's PanelBar) without having to screw around with event handlers.
Change your bindings to include UpdateSourceTrigger="PropertyChanged".
This will ensure that your data sources are updated on every key stroke, not just LostFocus.
MyOwnTextBox()
{
this.TextChanged += (s, e) => UpdateText();
}
private void UpdateText()
{
BindingExpression be = GetBindingExpression(TextProperty);
if (be != null && be.ParentBinding.Mode == BindingModes.TwoWay)
{
be.UpdateSource();
}
}
I am using this class it updates my binding on the fly, however there is issue with empty string and null values, if your destination property is nullable string then this will update empty string in your destination property. You can get workaround by using some sort of string converter which will use null instead of empty string in case of nullable strings.

Setting focus to a textbox on load of Windows Phone 7 app

I have a TwoWay data-bound textbox inside my page. I want to do something pretty simple, but I can't for the life of me work out how to do it. When the page loads, I want to set the focus to the textbox if it doesn't have any text in it.
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
if (tbSearch.Text == "")
tbSearch.Focus();
}
This doesn't work. It doesn't fail, it just doesn't do anything. Am I doing something strange wrong? I've also tried putting this in the constructor, to no avail.
Chris
Does doing this from the pages Loaded event work for your requirements?
It will work from there.

Make WPF TextBox or other focusable controls transparent to certain key presses (i.e. pass-through, not swallow)

I have a ListBox with a custom data template which contains a CheckBox, a TextBlock and a TextBox. Normally when you select an item in a ListBox, the underlying ListBoxItem is actually what has the focus and as such, it responds to the up and down keys. Additionally, if the CheckBox has focus, since it doesn't do anything with the up and down keys itself, it just happily ignores them and they're handled by the underlying ListBoxItem as well. All's good so far.
However, a TextBox has its own processing rules for the up and down keys, namingly moving the caret up or down a line in text, which doesn't apply here because in this case it's a single line (it's a number actually.) As such, if the TextBox has focus, the up and down keys break the navigation of the ListBox's selection, nor do they really help with editing.
Now while I could handle the PreviewKeyDownEvent (which I do below, but for different reasons) and manually process the behaviors depending on the pressed keys, that's a very specific solution and requires the control to have knowledge of its container's behavior.
In a perfect world (and in pseudocode), I'd like to just say MyTextBox.KeysToIgnore(Up, Down) or something similar and have it do just that... ignore those keys as if it wasn't even there. (Again, not swallow, but ignore so they pass through.)
But until then, here's what I've come up with, which seems to work, but just looks so 'hacky' to me...
private void PreviewKeyDownHandler(object sender, KeyEventArgs e) {
switch (e.Key){
case Key.Up:
case Key.Down:
case Key.OtherKeyToIgnore
case Key.AndAnother
e.Handled = true;
FrameworkElement target = VisualTreeHelper.GetParent(
e.Source as DependencyObject) as FrameworkElement;
target.RaiseEvent(
new KeyEventArgs(
e.KeyboardDevice,
PresentationSource.FromVisual(target),
0,
e.Key
){
RoutedEvent=Keyboard.KeyDownEvent
}
);
break;
}
}
This also has the added negative of not sending the PreviewKeyDown event to the target. Now I could work around that and fake it by sending that event first, then looking at the e.Handled before sending the actual KeyDown message, which makes sense, but then I hit another wall with the PreviewKeyUp and KeyUp events since thanks to setting e.Handled above, I never get the real 'key up' events to know when to send the fake ones. Plus I'm pretty sure I'd also be breaking the direction of the PreviewKeyxxx messages since they bubble the opposite direction from the regular non non-preview versions. (Maybe that's handled internally but I don't think so.)
Like I said... hacky, hacky, hacky!
But it does work so there's that. And I can implement this via Attached Behaviors which is why I even went this route. (In the attached behaviors implementation, it's not a case statement but rather a check against a collection of keys that I specify in XAML.) I just don't like the idea of losing all the other behaviors that I want.
Again, I just want to say 'Hey TextBox... when you see the Up or Down keys being pressed, STFU ya b*stard!!' and otherwise make it key-transparent.
Thoughts anyone?
Man... not even one comment, let alone an answer in almost a month! Oh well... guess my 'hacky' solution above is the way to do this so I'm marking this as the answer.
Yeh, TextBox key event handling sure can give you a headache.
The trouble is that it is impossible to determine call order of callbacks, registered via EventManager.RegisterClassEventHandler. I.e. your callback gets called on unhandled event, then event is handled, and thats it...
I found a way to "unhandle" key events by subclassing TextBox and by calling "AddEventHandler(KeyDownEvent, callback, true)" in constructor. Then set e.Handled = false by circumstances. Seems like callback gets called after event is processed by TextBox.
It is very not nice to have instance of delegate per instance of TextBox, rather than to have one delegate instance per class, but i can't see any other way to workaround.

WPF - Why isn't Keyboard.Focus() working?

have a TextBox item (MyTextBox) on a TabItem control. I have code that looks as follows:
MyTextBox.Focus();
Keyboard.Focus(MyTextBox);
When I run this code through the debugger I see the following after the lines are executed:
MyTextBox.IsFocused = true
MyTextBox.IsKeyboardFocused = false
Can anyone tell me why the textbox isn't receiving keyboard focus? It's just a standard TextBox control that is enabled.
When you try to set Focus to an element besides the things enumerated above by our coleague, you must also know that WPF does not allow cross threaded operations.
In some cases this exception is not raised like in the Focus method call case. What I've done to fix this issue is to call all the code that involves Keyboards focus in an action.
This action is ran inside the control dispatcher to make sure that my code is not being executed from another thread than the UI thread (e.g. timer event or an event raised from another thread):
[UIElement].Dispatcher.BeginInvoke(
new Action(
delegate{
/// put your Focus code here
}
)
);
MyTextBox.IsKeyboardFocused is false because you are looking at it under debugger and the keyboard focus is probably in your Visual Studio... Try debugging focus without breakpoints (e.g. Debug.Write or trace brakepoints) to see actual values of MyTextBox.IsKeyboardFocused in runtime.
Also notice that Focus() method returns boolean value that indicates whether focus was successfully set. Does it return False in your case? If yes, I would suggest stepping into Focus() method in order to find out what is wrong.
3 important properties must be true: IsVisible="True", Focusable="True". IsEnabled="True".
To be focusable, Focusable and IsEnabled must both be true.
http://msdn.microsoft.com/en-us/library/system.windows.uielement.focus.aspx
The accepted answer here does not solve the problem of textboxes who dont gain focus, no matter what the debugger tells you. If you have and can write to your textbox, then you have it keyboard-focused.
I found this here solving the problem (and actually gaining focus, not just settings the values so it looks like focus in the debugger), it comes very close to Pavlov's answer but with the "Focus code" : Keyboard.Focus does not work on text box in WPF
This worked for me (had to do UpdateLayout, otherwise Focus() didn't work immediately after changing tab from script)
tabControl.SelectedIndex = 2;
this.UpdateLayout();
txtMyTextBox.Focus();
It's important where your first two lines of code are executed.
If they are in an event handler that relates to the user pressing a key, using the mouse, altering the visibility of a control, or otherwise taking an action that might have an impact on focus, I find manually calling Focus() often doesn't work.
My theory is that internally, WPF operates as follows:
User or code takes action which could have an impact on focus, e.g. a TextBox control becomes enabled inside a focus scope which previously had no focusable control.
WPF notifies various event handlers, including yours which calls Focus().
WPF updates focus based on the state changes in step 1. This overrides whatever you did in step 2.
That is why this answer suggests to call your Focus() in a queued callback which will be executed after step 3.
Side note: you don't need to call both UIElement.Focus and Keyboard.Focus since the first includes the second (at least if you trust the Microsoft docs).
In conclusion, replace your first two lines of code with this:
// using System.Windows.Threading;
Dispatcher.BeginInvoke(DispatcherPriority.Input, MyTextBox.Focus);

WPF Focus In Tab Control Content When New Tab is Created

I've done a lot of searching on SO and google around this problem, but can't seem to find anything else to try.
I have a MainView (window) that contains a tab control. The tab control binds to an ObservableCollection of ChildViews (user controls). The MainView's ViewModel has a method that allows adding to the collection of ChildViews, which then creates a new tab. When a new tab is created, it becomes the active tab, and this works fine. This method on the MainView is called from another ViewModel (OtherViewModel).
What I am trying to do is set the keyboard focus to the first control on the tab (an AutoCompleteBox from WPFToolkit*) when a new tab is created. I also need to set the focus the same way, but WITHOUT creating a new tab (so set the focus on the currently active tab).
(*Note that there seem to be some focus problems with the AutoCompleteBox--even if it does have focus you need to send a MoveNext() to it to get the cursor in its window. I have worked around this already).
So here's the problem. The focusing works when I don't create a new tab, but it doesn't work when I do create a new tab. Both functions use the same method to set focus, but the create logic first calls the method that creates a new tab and sets it to active. Code that sets the focus (in the ChildView's Codebehind):
IInputElement element1 = Keyboard.Focus(autoCompleteBox);
//plus code to deal with AutoCompleteBox as noted.
In either case, the Keyboard.FocusedElement starts out as the MainView. After a create, calling Keyboard.Focus seems to do nothing (focused element is still the MainView). Calling this without creating a tab correctly sets the keyboard focus to autoCompleteBox.
Any ideas?
Update:
Bender's suggestion half-worked.
So now in both cases, the focused element is correctly the AutoCompleteBox. What I then do is MoveNext(), which sets the focus to a TextBox. I have been assuming that this Textbox is internal to the AutoCompleteBox, as the focus was correctly set on screen when this happened. Now I'm not so sure. This is still the behavior I see when this code gets hit when NOT doing a create. After a create, MoveNext() sets the focus to an element back in my MainView.
The problem must still be along the lines of Bender's answer, where the state of the controls is not the same depending on whether a new tab was created or not. Any other thoughts?
Final Update
As noted, majocha's suggestion worked.
I wanted to update this in case anyone happened upon this same problem with the AutoCompleteBox. It appears that setting focus does not activate it in the UI--you need to do a MoveNext on it to move focus forward once to the control's internal Textbox. This is based on my debugging experience, which may not be 100% scientific. If I have time, I will attempt to create a small repro project and submit it to the WPFToolkit team.
You can try defering the focus change with
Dispatcher.BeginInvoke(MyChangeFocusAction, DispatcherPriority.ContextIdle);
It will get queued after layout and properties updates are done.
I don't think it's best practice, but it works for me.
The control must be visible to be focused, you may try to defer focusing by subscribing to the IsVisibleChanged event, something similar to the following should work:
public static void setFocusLate(this Control control)
{
DependencyPropertyChangedEventHandler handler = null;
handler = delegate
{
control.Focus();
control.IsVisibleChanged -= handler;
};
control.IsVisibleChanged += handler;
}

Resources