2011-03-02 85 views
12

當在WindowsFormsHost和標籤導航中託管一個WinForms表單時,我遇到了問題。爲了解決我做了這個簡單的例子:WPF託管一個WinForm,標籤導航問題

  • 創建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)
  • 我在WindowsFormsHost第一TextBox點擊,它得到的焦點(好)
  • 我按下標籤,重點轉到第TextBoxWindowsFormsHost(好)
  • 我再次按標籤,重點回到TextBoxWindowsFormsHost不是 ok;應該離開WindowsFormsHost,並在WPF窗口的底部)
  • 我點擊在WPF文本框中賦予焦點文本框(後下WindowsFormsHost放置),它獲得焦點(OK)
  • 我按標籤,重點放在WindowsFormsHost的第1個文本框中 - 因爲它應該在結束後開始。因此,這是確定的太
  • 我再次點擊WPF文本框,然後按Shift + Tab,焦點轉移到第二個文本框在WindowsFormsHost(OK)
  • 我按Tab鍵,焦點轉到第一個文本框,在WindowsFormsHost(去年初在WFH )(不是 ok)

如何讓焦點的行爲就像我只有一種類型的控件一樣?在這種情況下,表示WFH-1st-Textbox,WFH-2nd-Textbox,WPF-Textbox的Tab鍵順序。

+0

在真正的項目導致我這個最小化的問題,情況甚至有點不同。有所有WPF控件(包括WindowsFormsHost)之間的Tab鍵切換。但是WindowsFormsHost中的Tab鍵並沒有轉到WindowsFormsHost中的其他WinForms控件之一,它只是讓WindowsFormsHost繼續使用下一個WPF控件。 – ZoolWay 2011-03-03 10:39:44

回答

7

根據我發現的文章,這似乎不可能完成。根據MSDN Blog Entry(Hwnds節),Windows窗體控件始終位於層次結構中的WPF控件之上。 MSDN article(從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鍵都會將文本框集中在一起。

+0

可能有辦法教WinForms部分中的第一個和最後一個控件的行爲有所不同嗎?像「釋放重點更高的控制」? – ZoolWay 2011-05-14 11:03:19

+0

你的附錄聽起來很有趣。簡而言之:如果嵌入UserControl而不是表單,它的工作原理應該如此? – ZoolWay 2011-05-14 14:51:49

+0

正確。至少對於我的小例子來說。 – Osiris76 2011-05-14 15:45:41

4

你可以用一個小技巧做到這一點。假設一臺主機的WPF形式如下:

<StackPanel> 
    <TextBox LostFocus="TextBox_LostFocus" /> 
    <wf:WindowsFormsHost Name="host" /> 
    <TextBox/> 
</StackPanel> 

在您將焦點設置對WinForm的第一個按鈕的第一個文本框的LostFocus事件。通過這種方式,您可以確保始終從第一個按鈕開始對焦。

private void TextBox_LostFocus(object sender, RoutedEventArgs e) 
{ 
    Form1 f = (Form1)host.Child; 
    f.EnableTabStops(true); 
} 

在你有如下的代碼EnableTabStops在WinForm:

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

這應該做的工作。

+0

星期一我會試一試。 – ZoolWay 2011-05-14 14:53:21

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

完全爲我工作

使用相同的方法,你也可以讓你的窗口控制的控制有需要的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> 
+0

這看起來很有趣,我將不得不嘗試一下! – ZoolWay 2013-11-25 09:41:34