在winform中托管的WPF控件中,Wierd进入/离开/验证行为 [英] Wierd enter/leave/validating behavior in a WPF control hosted within a winform

查看:82
本文介绍了在winform中托管的WPF控件中,Wierd进入/离开/验证行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个WinForms应用程序,它正在部分迁移到WPF,因此混合了Winforms和WPF。



问题: 在通过ElementHost托管的WPF控件的win窗体上,当焦点从win窗体文本框转换为WPF控件(比如文本框)时,win的验证事件形成文本调用box以使验证失败( e.Cancel = true ),然后将 Enter,Leave 验证事件继续在WinForms文本框上重复调用。请注意,我在验证事件中调用 Winforms消息框,如果用户按下取消按钮,则验证事件将设置为已取消。在我的代码示例中,用户每次都按下取消,理想情况下焦点应该返回到原始的winforms文本框。



我尝试了以下内容:



我尝试过:



代码如下:



Winforms

I am working on a WinForms app which is being migrated in parts to WPF so there is a mix of Winforms and WPF.

The problem: On a win form with a WPF control hosted via ElementHost, When focus shifts from a win forms text box to a WPF control (say text box), the Validating event of the win forms text box gets called such that the validation fails (e.Cancel = true), then instead of the focus being set to the win forms text box, the Enter, Leave and Validating event keeps on calling repeatedly on the WinForms text box. Note that i am calling a Winforms message box in the validating event and if the user presses Cancel button, the validating event is set to cancelled. In my code sample, the user presses Cancel each time and ideally the focus should go back to the originating winforms text box.

I have tried the following:

What I have tried:

The code looks like this:

Winforms

private void WindowsFormTestLoad(object sender, EventArgs e)
 {
     // Host WPF UserControl in winforms
     UserControl1 ctrl = new UserControl1();
     host = new ElementHost { Dock = DockStyle.Left , Child = ctrl};
     this.panel1.Controls.Add(host);
 }

 // Textbox1 events
 private void TextBox1Enter(object sender, EventArgs e)
 {
     Debug.WriteLine("Enter: text box 1");
 }

 private void TextBox1Leave(object sender, EventArgs e)
 {
     Debug.WriteLine("Leave: text box 1");
 }

 // Show a message box with OK and Cancel buttons.
 // Clicking on Cancel simulates a validation failure (This is what happens in my application).
 private void TextBox1Validating(object sender, CancelEventArgs e)
 {
     Debug.WriteLine("Validating: text box 1");

     var dialog = System.Windows.Forms.MessageBox.Show("blah blah", "caption", MessageBoxButtons.OKCancel);
     if (dialog == DialogResult.Cancel)
     {
         e.Cancel = true;
     }
 }





WPF控制



WPF Control

   <UserControl x:Class="WpfWinformInterop.UserControl1"

         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 

         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

         mc:Ignorable="d" 

         d:DesignHeight="300" d:DesignWidth="300">
<StackPanel>
    <TextBox Name="txt1"></TextBox>
</StackPanel>





当我尝试将焦点转移到Winform控件(比如文本框)时,Validating事件只被触发一次,焦点会回到原始文本框。



切换到win表单文本框时的调试输出(文本方框1到文本框2)是:离开:文本框1



验证:文本框1



输入:文本框1



当我尝试将焦点从win形式移动到WPF时,win form的文本框中的验证事件将继续触发。 br />


切换到WPF控件时的调试输出是:



离开:文本框1



验证:文本框1



输入:文本框1



离开:文本框1



验证:文本框1



输入:文本框1



离开:文本框1



验证:文本框1



像这样继续......



我无法理解为什么WPF行为是不同的,可以做些什么使它的行为类似于win form版本。任何帮助将不胜感激。



When I try to shift focus to a Winform control (say text box), the Validating event is fired only once and focus gets back to the original text box.

The debug output when switching to a win forms text box (text box 1 to text box 2) is: Leave: text box 1

Validating: text box 1

Enter: text box 1

When i try to move focus from win form to WPF the validating event on win form's text box keeps on firing.

The debug output when switching to a WPF control is:

Leave: text box 1

Validating: text box 1

Enter: text box 1

Leave: text box 1

Validating: text box 1

Enter: text box 1

Leave: text box 1

Validating: text box 1

goes on and on like this ...

I fail to understand why WPF behavior is different and what can be done to make it behave similarly to the win forms version. Any help would be greatly appreciated.

推荐答案

正如您所意识到的,WPF中的LostFocus事件与Winforms中的事件略有不同。 (我会回过头来看到我到目前为止看到的最佳解释的链接:冒泡或隧道基本WPF事件 [ ^ ])

我发现有时你会弹出窗口,因为当鼠标离开窗口时没有失去焦点(或控制)由于各种原因。
相反,我们使用LostMouseCapture来触发所需的行为,也许你可以从这个想法中使用一些东西(对不起,它可能有点令人困惑,因为我们有一个列表弹出部分,而不是需要在此控件中验证的内容):



在UserControl xaml中添加此

The LostFocus events are a bit different in WPF than in Winforms as you realise. (I will get back with a link to the best explanation I've seen so far later : To bubble or tunnel basic WPF events[^] )
I've found that sometimes you get popups hanging because there is not a loss of focus when the mouse leaves the window (or the control) for various reasons.
Instead we have used the LostMouseCapture to trigger the desired behaviours, perhaps there is something you can use from this idea (sorry that it may be a bit confusing because we have a list popup part rather than something that needs to be validated in this control):

In the UserControl xaml add this
LostMouseCapture="thisControl_LostMouseCapture"



,在后面的代码中我们添加了这个


and in the code behind we've added this

private void thisControl_LostMouseCapture(object sender, MouseEventArgs e)
{
    if (Mouse.Captured != this)
    {
        if (e.OriginalSource == this)
        {
            if (Mouse.Captured == null || !IsDescendant(this, Mouse.Captured as DependencyObject))
            {
                ClosePopup();
                return;
            }
        }
        else if (IsDescendant(this, e.OriginalSource as DependencyObject))
        {
            if (PART_Popup.IsOpen && Mouse.Captured == null && GetCapture() == IntPtr.Zero)
            {
                Mouse.Capture(this, CaptureMode.SubTree);
                e.Handled = true;
                return;
            }
        }
        else
        {
            ClosePopup();
        }
    }
}

private void ClosePopup(bool killFocus = true)
{
    ResetListBoxSelection();
    PART_Popup.IsOpen = false;
    if(killFocus) Keyboard.Focus(null);
}
internal static bool IsDescendant(DependencyObject reference, DependencyObject node)
{
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(reference); i++)
    {
        var child = VisualTreeHelper.GetChild(reference, i);
        if (child == node) return true;
        if (IsDescendant(child, node)) return true;

        var popup = child as Popup;
        if(popup != null)
        {
            if (IsDescendant(popup.Child, node)) return true;
        }
    }
    return false;
}
public static Visual GetDescendantByType(Visual element, Type type)
{
    if (element == null)
    {
        return null;
    }
    if (element.GetType() == type)
    {
        return element;
    }
    Visual foundElement = null;
    if (element is FrameworkElement)
    {
        (element as FrameworkElement).ApplyTemplate();
    }
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
    {
        Visual visual = VisualTreeHelper.GetChild(element, i) as Visual;
        foundElement = GetDescendantByType(visual, type);
        if (foundElement != null)
        {
            break;
        }
    }
    return foundElement;
}


[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern IntPtr GetCapture();

private void PART_EditableTextBox_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
    var node = Keyboard.FocusedElement as DependencyObject;
    if (node == null) return;
    if (!IsDescendant(this, node)) ClosePopup(false);
}


这篇关于在winform中托管的WPF控件中,Wierd进入/离开/验证行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆