WPF хостинг WinForm, Tab Navigation проблемы - PullRequest
12 голосов
/ 02 марта 2011

У меня возникли проблемы при размещении формы 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;

Когда я сейчас запускаю приложение:

  • Ничего не сфокусировано (хорошо)
  • Я нажимаю на первый TextBox в WindowsFormsHost, он получает фокус (ок)
  • Я нажимаю Tab, фокус переходит на 2 TextBox в WindowsFormsHost (ок)
  • Я снова нажимаю клавишу Tab, фокус возвращается к 1-му TextBox в WindowsFormsHost ( не ок; должно было остаться WindowsFormsHost и дать фокус текстовому полю в нижней части окна WPF )
  • Я нажимаю на текстовое поле в wpf (после и после WindowsFormsHost), оно получает фокус (хорошо)
  • Я нажимаю клавишу Tab, фокус переходит к 1-му текстовому полю в WindowsFormsHost - так, как оно должно переходить к началу после конца. Так что это тоже нормально
  • Я снова щелкаю текстовое поле wpf и нажимаю shift + tab, фокус переходит на 2-е текстовое поле в WindowsFormsHost (ок)
  • Я нажимаю клавишу Tab, фокус переходит на 1-е текстовое поле в WindowsFormsHost (идет в начало в WFH) ( не ок)

Как заставить фокус вести себя так, как если бы у меня были только элементы управления одного типа? В данном случае это означает порядок табуляции WFH-1st-Textbox, WFH-2nd-Textbox, WPF-Textbox.

Ответы [ 4 ]

7 голосов
/ 14 мая 2011

В соответствии с найденными мною статьями этого, кажется, невозможно достичь. Согласно записи в блоге MSDN (раздел Hwnds) элементы управления Windows Forms всегда находятся над элементами управления WPF в иерархии. В статье MSDN (раздел Получение сообщений из цикла сообщений WPF) говорится, что события, происходящие в элементе 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.

4 голосов
/ 24 ноября 2013

Вот как я это реализовал:

Я создал элемент управления, который наследуется от 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));              
        }                 
    } 
}

отлично работал для меня

, используя тот же подход, который вы можететакже сделайте так, чтобы ваш элемент управления Windows имел необходимые привязки данных 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>
4 голосов
/ 14 мая 2011

Вы можете сделать это с помощью небольшого трюка. Предположим, что ваша форма 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);
}

Это должно сделать работу.

1 голос
/ 12 сентября 2018

Просто добавьте это в App.xaml.cs:

System.Windows.Forms.Integration.WindowsFormsHost.EnableWindowsFormsInterop();

Ссылка: WindowsFormsIntegration.dll

...