WPF托管WinForm,Tab导航问题

时间:2011-03-02 10:56:59

标签: .net wpf winforms windowsformshost tab-ordering

WindowsFormsHost和标签导航中托管WinForms表单时遇到了问题。要解决我已经做了这个简单的例子:

  • 创建了WPF Window(应用的起点)
  • 创建了两个Form的WinForms 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键,焦点转到TextBox中的第二个WindowsFormsHost(确定)
  • 我再次按Tab键,焦点会回到TextBox中的第WindowsFormsHost位(确定;应该离开WindowsFormsHost并将焦点放在文本框中在WPF窗口的底部)
  • 我点击了wpf中的文本框(放在WindowsFormsHost之后和之下),它得到了焦点(确定)
  • 我按Tab键,焦点转到WindowsFormsHost中的第一个文本框 - 因为它应该在结束后开始。所以这也没关系
  • 我再次点击wpf文本框并按shift + tab,焦点转到WindowsFormsHost中的第二个文本框(ok)
  • 我按Tab键,焦点转到WindowsFormsHost中的第一个文本框(以WFH开头)(确定)

如果我只有一种类型的控件,我如何使焦点行为?这意味着WFH-1st-Textbox,WFH-2nd-Textbox,WPF-Textbox的Tab键顺序。

4 个答案:

答案 0 :(得分:7)

根据我发现的文章,这似乎无法实现。根据{{​​3}}(Hwnds部分),Windows窗体控件始终位于层次结构中的WPF控件之上。 MSDN Blog Entry(从WPF消息循环获取消息部分)指出,在WPF甚至意识到它们之前,将处理WindowsFormsHost元素中发生的事件。

所以我假设通过按TAB键触发的事件由WindowsFormsHost元素处理(导致其他文本框的焦点)。在封闭的WPF窗口中,永远不会遇到该事件,因为“它已被处理”。另一方面,当您按下WPF文本框中的TAB键时,WPF正在处理事件本身并正常处理控制链。有了这个,焦点将转到WindowsFormsHost元素中的文本框,从那里你不能使用键盘离开它。

我知道这对你当前的问题没有帮助,但我希望它能解释一些事情。


<强>附录 如果您不依赖于使用表单控件,则可以将其更改为WinForms用户控件,其中包含相同的控件元素。之后,您可以通过以下方式更改WindowsFormsHost元素的初始化:

System.Windows.Forms.UserControl control = new WinFormUC();
windowsFormsHost1.Child = control;

类WinFormUC是我的WinForms用户控件,包含上面提到的文本框。在我的测试中,无论是Winforms还是WPF文本框,按下TAB键都会逐个聚焦文本框。

答案 1 :(得分:4)

你可以用一个小技巧来做到这一点。假设您的主机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上的最后一个按钮后,您禁用/删除所有tabstops,以便下一个选项卡只能跳转到其父wpf表单,如下所示:

private void button2_Enter(object sender, EventArgs e)
{
    EnableTabStops(false);
}

这应该可以胜任。

答案 2 :(得分:4)

这是我实现这个的方式:

我创建了一个继承自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>

答案 3 :(得分:1)

只需在App.xaml.cs中添加它即可:

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

参考:WindowsFormsIntegration.dll