Подтвердить что ты не робот

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.

4b9b3361

Ответ 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>