WPF托管WinForm,Tab导航问题

问题描述 投票:12回答:4

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

  • 创建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键,焦点转到TextBox的第二个WindowsFormsHost(ok)
  • 我再次按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键顺序。

.net wpf winforms windowsformshost tab-ordering
4个回答
7
投票

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

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

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


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

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

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


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);
}

这应该做的工作。


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>

1
投票

只需在App.xaml.cs中添加:

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

参考:WindowsFormsIntegration.dll

© www.soinside.com 2019 - 2024. All rights reserved.