Reputation: 35117
I've created a custom grid control with editable text blocks in each cell. The edit is triggered by a double click like so:
void TextBlock_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (e.ClickCount == 2) Edit();
}
public void Edit()
{
TextBox.Text = TextBlock.Text;
TextBlock.Visibility = System.Windows.Visibility.Collapsed;
TextBox.Visibility = System.Windows.Visibility.Visible;
Dispatcher.BeginInvoke((ThreadStart)delegate
{
TextBox.Focus();
TextBox.SelectAll();
});
}
This part works as expected.
I wanted to give the user the ability to tab to the next cell simply by pushing tab to advance or shift tab to go back. I added an event which is triggered like so:
void TextBox_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == System.Windows.Input.Key.Tab)
{
e.Handled = true;
OnTab(System.Windows.Input.Keyboard.IsKeyDown(System.Windows.Input.Key.LeftShift) || System.Windows.Input.Keyboard.IsKeyDown(System.Windows.Input.Key.RightShift));
}
}
The outer grid handles this event and calls the Edit()
method on the next or previous editable cell. This almost works as expected as long as I don't actually type in the text box. For some reason if I've typed anything the LostFocus event fires not only for the current cell (which is expected after Edit()
calls Focus()
on the Textbox) but for the next cell as well. Here's the code for the LostFocus event. (Though I'm pretty certain it's not relevant.)
void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
bool TextChanged = TextBox.Text != TextBlock.Text;
TextBlock.Text = TextBox.Text;
TextBlock.Visibility = System.Windows.Visibility.Visible;
TextBox.Visibility = System.Windows.Visibility.Collapsed;
if (TextChanged) OnTextChanged();
}
I could create some sort of fancy gate to get the textbox to ignore this event but I'm more curious as to why it's even firing, or at least how I could go about finding an answer.
Thanks.
EDIT:
So apparently even with some gating I'm still hosed. I added a boolean called Editing
and altered the Edit()
method to work like this:
public void Edit()
{
TextBox.Text = TextBlock.Text;
TextBlock.Visibility = System.Windows.Visibility.Collapsed;
TextBox.Visibility = System.Windows.Visibility.Visible;
Thread Task = new Thread(() =>
{
Editing = true;
Dispatcher.BeginInvoke((ThreadStart)delegate
{
System.Windows.Input.Keyboard.Focus(TextBox);
TextBox.SelectAll();
EditGate.Set();
});
EditGate.WaitOne();
Editing = false;
});
Task.Start();
}
And then changed the LostFocus handler:
void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
if (Editing) return;
// ... Some more code.
}
Unfortunately this doesn't work either. Things end up going like so:
I'm not sure why the LostFocus event is firing after everything else. It's almost like it knows what I'm trying to do. :P
Upvotes: 0
Views: 2449
Reputation: 43616
I think it may be due to KeyboardFocus
, in WPF you can set "Logical" focus and KeyboardFocus
its just a guess but when you type in the TextBox is aquiring the KeyBoardFocus
which could change the state of the logical focus.
Its a bit hard for me to test but try setting the Keyboard focus in the Edit()
method and see what happens.
public void Edit()
{
TextBox.Text = TextBlock.Text;
TextBlock.Visibility = System.Windows.Visibility.Collapsed;
TextBox.Visibility = System.Windows.Visibility.Visible;
Dispatcher.BeginInvoke((ThreadStart)delegate
{
Keyboard.Focus(TextBox);
TextBox.SelectAll();
});
}
I have always had issue manipulating WPF focus from code behind, I guess its because thats not how its designed to work, it may be better to use Xaml triggers to sort the focus (if possible)
Perhaps the FocusManager class may be able to help, here you can add remove the focus handlers of the current/next item,
FocusManager.RemoveLostFocusHandler(TextBox, TextBox_LostFocus);
FocusManager.AddLostFocusHandler(TextBox, TextBox_LostFocus);
Upvotes: 1