WPF-хостинг WinForm, вкладка "Навигация"
У меня возникли проблемы при размещении формы WinForms в рамках WindowsFormsHost
и навигации по вкладкам. Чтобы решить, я сделал этот простой пример:
- Созданный WPF
Window
(начальная точка приложения)
- Созданы WinForms
Form
с двумя TextBox
на нем
- Окно WPF: добавлено
WindowsFormsHost
к нему
- Окно WPF: добавлен обработчик
OnLoaded
- Окно WPF: добавлено
TextBox
, расположенное под WindowsFormsHost
В обработчике OnLoaded
я получил:
System.Windows.Forms.Form f = new WinFormsForm();
f.TopLevel = false;
f.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.windowsFormsHost1.Child = f;
Когда я сейчас запустил приложение:
- Ничего не сфокусировано (ok)
- Я нажимаю на первый
TextBox
в WindowsFormsHost
, он получает фокус (ok)
- Я нажимаю вкладку, фокус переходит на 2-й
TextBox
в WindowsFormsHost
(ok)
- Я снова нажимаю вкладку, фокус возвращается к 1-му
TextBox
в WindowsFormsHost
( не ok; должен оставить WindowsFormsHost
и задать фокус в текстовое поле внизу WPF окно)
- Я нажимаю на текстовое поле в wpf (помещается после и под
WindowsFormsHost
), он получает фокус (ok)
- Я нажимаю вкладку, фокус переходит в 1-ое текстовое поле в
WindowsFormsHost
- так как он должен начинаться после завершения. Так что это тоже нормально.
- Я снова нажимаю текстовое поле wpf и нажимаю shift + tab, фокус переходит во 2-й текстовый блок в
WindowsFormsHost
(ok)
- Я нажимаю вкладку, фокус переходит к 1-му текстовому полю в
WindowsFormsHost
(идет в начало в WFH) ( не ok)
Как заставить фокус вести себя, как если бы у меня были только элементы управления одного типа? В этом случае указывается порядок вкладок WFH-1st-Textbox, WFH-2nd-Textbox, WPF-Textbox.
Ответы
Ответ 1
Согласно статьям, которые я нашел, это кажется невозможным. Согласно записи в блоге MSDN (раздел Hwnds) элементы управления Windows Forms всегда находятся поверх элементов управления WPF в иерархии. В статье MSDN (раздел "Получение сообщений из WPF Message Loop" ) указано, что события, происходящие в элементе WindowsFormsHost, будут обработаны до того, как WPF даже узнает о них.
Итак, я предполагаю, что событие, запущенное нажатием клавиши TAB, обрабатывается элементом WindowsFormsHost (что приводит к фокусу другого текстового поля). В закрывающемся окне WPF событие никогда не встретится, потому что "оно уже обработано". С другой стороны, когда вы нажимаете клавишу TAB в текстовом поле WPF, WPF обрабатывает само событие, и цепочка управления обрабатывается нормально. При этом фокус попадет в текстовое поле в элементе WindowsFormsHost, и оттуда вы не сможете оставить его с клавиатуры.
Я знаю, что это не поможет вашей текущей проблеме, но я надеюсь, что это объяснит некоторые вещи.
ДОПОЛНЕНИЕ
Если вы не зависите от использования элемента управления формой, вы можете изменить его на пользовательский контроль WinForms с теми же элементами управления в нем. После этого вы измените инициализацию элемента WindowsFormsHost следующим образом:
System.Windows.Forms.UserControl control = new WinFormUC();
windowsFormsHost1.Child = control;
Класс WinFormUC - это пользовательский элемент управления WinForms, содержащий указанные текстовые поля. В моем тесте нажатие клавиши TAB сфокусировало текстовые поля один за другим независимо от того, является ли это Winforms или текстовым полем WPF.
Ответ 2
Вы можете сделать это с помощью небольшого трюка. Предположим, что ваша форма wpf с хостом выглядит следующим образом:
<StackPanel>
<TextBox LostFocus="TextBox_LostFocus" />
<wf:WindowsFormsHost Name="host" />
<TextBox/>
</StackPanel>
В событии LostFocus первого текстового поля вы устанавливаете фокус на первую кнопку на winform. Таким образом, вы убедитесь, что фокус всегда начинается с первой кнопки.
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
Form1 f = (Form1)host.Child;
f.EnableTabStops(true);
}
В winform вам нужно закодировать EnableTabStops следующим образом:
public void EnableTabStops(bool IsEnabled)
{
this.button1.TabStop = IsEnabled;
this.button2.TabStop = IsEnabled;
if (IsEnabled) button1.Focus();
}
Затем вы вставляете кнопки winform. После ввода последней кнопки на winform вы отключите/удалите все вкладки, чтобы следующая вкладка могла перейти только в свою родительскую форму wpf, например:
private void button2_Enter(object sender, EventArgs e)
{
EnableTabStops(false);
}
Это должно выполнить эту работу.
Ответ 3
Так я реализовал это:
Я создал элемент управления, который наследуется от WindowsFormsHost
public class MyWpfControl: WindowsFormsHost
{
private MyWindowsFormsControl _winControl = new MyWindowsFormsControl ();
public MyWpfControl()
{
_winControl.KeyDown += _winControl_KeyDown;
}
void _winControl_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Tab && e.Shift)
{
MoveFocus(new TraversalRequest(FocusNavigationDirection.Previous));
}
else if (e.KeyCode == Keys.Tab)
{
MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
}
}
отлично работал у меня
используя тот же подход, вы также можете настроить управление окнами на необходимые привязки данных wpf:
public static readonly RoutedEvent SelectionChangedEvent = EventManager.RegisterRoutedEvent("SelectionChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyWpfControl));
public event RoutedEventHandler SelectionChanged
{
add { AddHandler(SelectionChangedEvent, value); }
remove { RemoveHandler(SelectionChangedEvent, value); }
}
void RaiseSelectionChangedEvent()
{
var newEventArgs = new RoutedEventArgs(SelectionChangedEvent);
RaiseEvent(newEventArgs);
}
private void InitDependencyProperties()
{
_winControl.EditValueChanged += (sender, e) =>
{
SetValue(SelectedValueProperty, _winControl.EditValue);
if (!_disabledSelectionChangedEvent)
{
RaiseSelectionChangedEvent();
}
};
}
public static readonly DependencyProperty SelectedValueProperty = DependencyProperty.Register("SelectedValue", typeof(string), typeof(MyWpfControl),
new PropertyMetadata("",
(d, e) =>
{
var myControl = d as MyWpfControl;
if (myControl != null && myControl._brokersCombo != null)
{
var val = myControl.GetValue(e.Property) ?? string.Empty;
myControl._winControl.EditValue = val;
}
}, null));
Вот XAML:
<u:MyWpfControl x:Name="myWpfControl" Margin="5,0,0,0" DataSource="{Binding BindingData, UpdateSourceTrigger=PropertyChanged}" SelectedValue="{Binding SelectedPropertyNameOnViewModel, Mode=TwoWay}">
</u:MyWpfControl>