进程间通信,WPF中Popup上的textbox无法切换到中文输入法

As Marco Zhou has said in the msdn forum ():
Windows will only send WM_IME_SETCONTEXT message to the active window, Popup by default is designed to be shown with WM_EX_NOACTIVE which means that it doesn't in active state when displaying, that's why IME could not work correctly in this regard, to workaround this issue, you could try set the Popup as the active window using win32 SetActiveWindow() API.

/// <summary>
/// 解决StayOpen=true时,永远置顶问题的Popup控件
/// </summary>
public class EasiNotePopup : Popup
必威官方最新下载,{
public static readonly DependencyProperty IsTopmostProperty = DependencyProperty.Register("IsTopmost", typeof(bool), typeof(EasiNotePopup), new FrameworkPropertyMetadata(false, OnIsTopmostChanged));

转自Net-Spider原文 c# 进程间通信

So the workaround is use MyPopup instead of Popup:

private bool? _appliedTopMost;
private bool _alreadyLoaded;
private Window _parentWindow;

 

public class MyPopup : Popup
{
    [DllImport("user32.dll")]
    static extern IntPtr SetActiveWindow(IntPtr hWnd);

public bool IsTopmost
{
get { return (bool)GetValue(IsTopmostProperty); }
set { SetValue(IsTopmostProperty, value); }
}

一、进程间通讯的方式

    static MyPopup()
    {
        EventManager.RegisterClassHandler(
            typeof(MyPopup),
            Popup.PreviewGotKeyboardFocusEvent,
            new KeyboardFocusChangedEventHandler(OnPreviewGotKeyboardFocus),
            true);
    }

/// <summary>
/// ctor
/// </summary>
public EasiNotePopup()
{
Loaded += OnPopupLoaded;
Unloaded += OnPopupUnloaded;
}

进程间通讯的方式有很多,常用的有共享内存(内存映射文件、共享内存DLL、剪切板等)、命名管道和匿名管道、发送消息等几种方法来直接完成,另外还可以通过socket口、配置文件和注册表等来间接实现进程间数据通讯任务。以上这几种方法各有优缺点,具体到在进程间进行大数据量数据的快速交换问题上,则可以排除使用配置文件和注册表的方法;另外,由于管道和socket套接字的使用需要有网卡的支持,因此也可以不予考虑。这样,可供选择的通讯方式只剩下共享内存和发送消息两种。

    private static void OnPreviewGotKeyboardFocus(Object sender, KeyboardFocusChangedEventArgs e)
    {
        var textBox = e.NewFocus as TextBoxBase;
        if (textBox != null)
        {
            var hwndSource = PresentationSource.FromVisual(textBox) as HwndSource;
必威体育手机端,            if (hwndSource != null)
            {
                SetActiveWindow(hwndSource.Handle);
            }
        }
    }
}

void OnPopupLoaded(object sender, RoutedEventArgs e)
{
if (_alreadyLoaded)
return;

二、发送消息实现进程间通讯前准备

官方解决方案

_alreadyLoaded = true;

下面的例子用到一个windows api 32函数

使用的话自己建一个类代码如上,控件内local:MyPopup 等同于Popup,亲测好使,实用。

if (Child != null)
{
Child.AddHandler(PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(OnChildPreviewMouseLeftButtonDown), true);
}

[DllImport("User32.dll", EntryPoint = "SendMessage")]
private static extern int SendMessage(IntPtr wnd,int msg,IntPtr wP,IntPtr lP);

参考:

_parentWindow = Window.GetWindow(this);

要有此函数,需要添加using System.Runtime.InteropServices;命名空间

if (_parentWindow == null)
return;

此方法各个参数表示的意义

_parentWindow.Activated += OnParentWindowActivated;
_parentWindow.Deactivated += OnParentWindowDeactivated;
}

wnd:接收消息的窗口的句柄。如果此参数为HWND_BROADCAST,则消息将被发送到系统中所有顶层窗口,包括无效或不可见的非自身拥有的窗口、被覆盖的窗口和弹出式窗口,但消息不被发送到子窗口。

private void OnPopupUnloaded(object sender, RoutedEventArgs e)
{
if (_parentWindow == null)
return;
_parentWindow.Activated -= OnParentWindowActivated;
_parentWindow.Deactivated -= OnParentWindowDeactivated;
}

msg:指定被发送的消息类型。

void OnParentWindowActivated(object sender, EventArgs e)
{
SetTopmostState(true);
}

wP:消息内容。

void OnParentWindowDeactivated(object sender, EventArgs e)
{
Debug.WriteLine("Parent Window Deactivated");

lP:指定附加的消息指定信息。

if (IsTopmost == false)
{
SetTopmostState(IsTopmost);
}
}

用api参考手册查看SendMessage用法时,参考手册则提示

void OnChildPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{

SendMessage与PostMessage之间的区别:SendMessage和PostMessage,这两个函数虽然功能非常相似,都是负责向指定的窗口发送消息,

SetTopmostState(true);

SendMessage() 函数发出消息后一直等到接收方的消息响应函数处理完之后才能返回,并能够得到返回值,在此期间发送方程序将被阻塞,SendMessage() 后面的语句不能被继续执行,即是说此方法是同步的。

if (!_parentWindow.IsActive && IsTopmost == false)
{
_parentWindow.Activate();
}
}

PostMessage() 函数在发出消息后马上返回,其后语句能够被立即执行,但是无法获取接收方的消息处理返回值,即是说此方法是异步的。

private static void OnIsTopmostChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var thisobj = (EasiNotePopup)obj;

 

thisobj.SetTopmostState(thisobj.IsTopmost);
}

三、发送消息实现进程间通讯具体步骤

protected override void OnOpened(EventArgs e)
{
SetTopmostState(IsTopmost);
base.OnOpened(e);
}

1.新建windows应用程序

private void SetTopmostState(bool isTop)
{
if (_appliedTopMost.HasValue && _appliedTopMost == isTop)
{
return;
}

(1)打开VS2008,新建一个“windows 应用程序”,主窗口为Form1,项目名称:ProcessCommunication
(2)在Form1上添加一个标签为textBox1的文本框,并为Form1添加KeyDown事件,当Form1接收到KewDown消息时,将接收到的数据显示在label1上。

if (Child == null)
return;

public Form1()
{
InitializeComponent();

var hwndSource = (PresentationSource.FromVisual(Child)) as HwndSource;

this.KeyDown+=new KeyEventHandler(Form1_KeyDown);

if (hwndSource == null)
return;
var hwnd = hwndSource.Handle;

}

RECT rect;

private void Form1_KeyDown(object sender, KeyEventArgs e)
{
this.textBox1.Text = Convert.ToString(e.KeyValue);
}
(3)编译运行,生成ProcessCommunication.exe

if (!GetWindowRect(hwnd, out rect))
return;

 

if (isTop)
{
SetWindowPos(hwnd, HWND_TOPMOST, rect.Left, rect.Top, (int)Width, (int)Height, TOPMOST_FLAGS);
}
else
{
// 重新激活Topmost,需要bottom->top->notop
SetWindowPos(hwnd, HWND_BOTTOM, rect.Left, rect.Top, (int)Width, (int)Height, TOPMOST_FLAGS);
SetWindowPos(hwnd, HWND_TOP, rect.Left, rect.Top, (int)Width, (int)Height, TOPMOST_FLAGS);
SetWindowPos(hwnd, HWND_NOTOPMOST, rect.Left, rect.Top, (int)Width, (int)Height, TOPMOST_FLAGS);
}

2.新建windows应用程序**

_appliedTopMost = isTop;
}

 

[StructLayout(LayoutKind.Sequential)]
public struct RECT

(1)打开VS2008,新建一个“windows 应用程序”,主窗口为Form1,项目名称:ProcessCommunication1,
并在Form1上添加一个按钮和一个文本框

{
public int Left;
public int Top;
public int Right;
public int Bottom;
}

namespace ProcessCommunication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
//Win32 API函数:
[DllImport("User32.dll", EntryPoint = "SendMessage")]
private static extern int SendMessage(IntPtr wnd,int msg,IntPtr wP,IntPtr lP);

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);

private void button1_Click(object sender, EventArgs e)
{
Process[] pros = Process.GetProcesses(); //获取本机所有进程
for (int i = 0; i < pros.Length; i++)
{
if (pros[i].ProcessName == "ProcessCommunication") //名称为ProcessCommunication的进程

[DllImport("user32.dll")]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X,
int Y, int cx, int cy, uint uFlags);

{
IntPtr hWnd = pros[i].MainWindowHandle; //获取ProcessCommunication.exe主窗口句柄
int data = Convert.ToInt32(this.textBox1.Text); //获取文本框数据
SendMessage(hWnd, 0x0100, (IntPtr)data, (IntPtr)0); //点击该按钮,以文本框数据为参数,向Form1发送WM_KEYDOWN消息
}

}

static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
static readonly IntPtr HWND_TOP = new IntPtr(0);
static readonly IntPtr HWND_BOTTOM = new IntPtr(1);

}
}

private const UInt32 SWP_NOSIZE = 0x0001;
const UInt32 SWP_NOMOVE = 0x0002;
const UInt32 SWP_NOZORDER = 0x0004;
const UInt32 SWP_NOREDRAW = 0x0008;
const UInt32 SWP_NOACTIVATE = 0x0010;

3.启动ProcessCommunication.exe可执行文件,弹出Form1窗体称为接受消息窗体。

const UInt32 SWP_FRAMECHANGED = 0x0020; /* The frame changed: send WM_NCCALCSIZE */
const UInt32 SWP_SHOWWINDOW = 0x0040;
const UInt32 SWP_HIDEWINDOW = 0x0080;
const UInt32 SWP_NOCOPYBITS = 0x0100;
const UInt32 SWP_NOOWNERZORDER = 0x0200; /* Don’t do owner Z ordering */
const UInt32 SWP_NOSENDCHANGING = 0x0400; /* Don’t send WM_WINDOWPOSCHANGING */

启动ProcessCommunication1.exe可执行文件,在弹出的窗体中的文本框中输入任意数字,点击button1按钮,接受消息窗体textBox1即显示该数字。

//很重要,窗口切换等需要将popup显示层级重新刷新
const UInt32 TOPMOST_FLAGS =
SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSENDCHANGING;
}

到此结束。

本文由必威官方最新下载发布于联系我们,转载请注明出处:进程间通信,WPF中Popup上的textbox无法切换到中文输入法

您可能还会对下面的文章感兴趣: