WPF 的 UI 逻辑只在同一个线程中,这是学习 WPF 开发中大家几乎都会学习到的经验。如果希望做不同线程的 UI,大家也会想到使用另一个窗口来实现,让每个窗口拥有自己的 UI 线程。然而,就不能让同一个窗口内部使用多个 UI 线程吗?

阅读本文将收获一份 Win32 函数 SetParent 及相关函数的使用方法。

WPF 同一个窗口中跨线程访问 UI 有多种方法:

前者使用的是 WPF 原生方式,做出来的跨线程 UI 可以和原来的 UI 相互重叠遮挡。后者使用的是 Win32 的方式,实际效果非常类似 WindowsFormsHost,新线程中的 UI 在原来的所有 WPF 控件上面遮挡。另外,后者不止可以是跨线程,还可以跨进程。

准备必要的 Win32 函数

完成基本功能所需的 Win32 函数是非常少的,只有 SetParent 和 MoveWindow。

[DllImport("user32.dll")]

public static extern bool SetParent(IntPtr hWnd, IntPtr hWndNewParent);

[DllImport("user32.dll", SetLastError = true)]

public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);

SetParent 用于指定传统的窗口父子关系。有多传统呢?呃……就是 Windows 自诞生以来的那种传统。在传统的 Win32 应用程序中,每一个控件都有自己的窗口句柄,它们之间通过 SetParent 进行连接;可以说一个 Button 就是一个窗口。而我们现在使用 SetParent 其实就是在使用传统 Win32 程序中的控件的机制。

MoveWindow 用于指定窗口相对于其父级的位置,我们使用这个函数来决定新嵌入的窗口在原来界面中的位置。

启动后台 UI 线程

启动一个后台的 WPF UI 线程网上有不少线程的方法,但大体思路是一样的。我之前在 如何实现一个可以用 await 异步等待的 Awaiter 一文中写了一个利用 async/await 做的更高级的版本。

为了继续本文,我将上文中的核心文件抽出来做成了 GitHubGist,访问 Custom awaiter with background UI thread 下载那三个文件并放入到自己的项目中。

AwaiterInterfaces.cs 为实现 async/await 机制准备的一些接口,虽然事实上可以不需要,不过加上可以防逗比。

DispatcherAsyncOperation.cs 这是我自己实现的自定义 awaiter,可以利用 awaiter 的回调函数机制规避线程同步锁的使用。

UIDispatcher.cs 用于创建后台 UI 线程的类型,这个文件包含本文需要使用的核心类,使用到了上面两个文件。

在使用了上面的三个文件的情况下,创建一个后台 UI 线程并获得用于执行代码的 Dispatcher 只需要一句话:

// 传入的参数是线程的名称,也可以不用传。

var dispatcher = await UIDispatcher.RunNewAsync("Background UI");

在得到了后台 UI 线程 Dispatcher 的情况下,无论做什么后台线程的 UI 操作,只需要调用 dispatcher.InvokeAsync 即可。

我们使用下面的句子创建一个后台线程的窗口并显示出来:

var backgroundWindow = await dispatcher.InvokeAsync(() =>

{

var window = new Window();

window.SourceInitialized += OnSourceInitialized;

window.Show();

return window;

});

在代码中,我们监听了 SourceInitialized 事件。这是 WPF 窗口刚刚获得 Windows 窗口句柄的时机,在此事件中,我们可以最早地拿到窗口句柄以便进行 Win32 函数调用。

private void OnSourceInitialized(object sender, EventArgs e)

{

// 在这里可以获取到窗口句柄。

}

嵌入窗口

为了比较容易写出嵌入窗口的代码,我将核心部分代码贴出来:

class ParentWindow : Window

{

public ParentWindow()

{

InitializeComponent();

Loaded += OnLoaded;

}

private async void OnLoaded(object sender, RoutedEventArgs e)

{

// 获取父窗口的窗口句柄。

var hwnd = (HwndSource) PresentationSource.FromVisual(this);

_parentHwnd = hwnd;

// 在后台线程创建子窗口。

var dispatcher = await UIDispatcher.RunNewAsync("Background UI");

await dispatcher.InvokeAsync(() =>

{

var window = new Window();

window.SourceInitialized += OnSourceInitialized;

window.Show();

});

}

private void OnSourceInitialized(object sender, EventArgs e)

{

var childHandle = new WindowInteropHelper((Window) sender).Handle;

SetParent(childHandle, _parentHwnd.Handle);

MoveWindow(childHandle, 0, 0, 300, 300, true);

}

private HwndSource _parentHwnd;

[DllImport("user32.dll")]

public static extern bool SetParent(IntPtr hWnd, IntPtr hWndNewParent);

[DllImport("user32.dll", SetLastError = true)]

public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);

}

具体执行嵌入窗口的是这一段:

private void OnSourceInitialized(object sender, EventArgs e)

{

var childHandle = new WindowInteropHelper((Window) sender).Handle;

SetParent(childHandle, _parentHwnd.Handle);

MoveWindow(childHandle, 0, 0, 300, 300, true);

}

最终显示时会将后台线程的子窗口显示到父窗口的 (0, 0, 300, 300) 的位置和大小。可以试试在主线程写一个 Thread.Sleep(5000),在卡顿的事件内,你依然可以拖动子窗口的标题栏进行拖拽。

当然,如果你认为外面那一圈窗口的非客户区太丑了,使用普通设置窗口属性的方法去掉即可:

await dispatcher.InvokeAsync(() =>

{

var window = new Window

{

BorderBrush = Brushes.DodgerBlue,

BorderThickness = new Thickness(8),

Background = Brushes.Teal,

WindowStyle = WindowStyle.None,

ResizeMode = ResizeMode.NoResize,

Content = new TextBlock

{

Text = "walterlv.github.io",

HorizontalAlignment = HorizontalAlignment.Center,

VerticalAlignment = VerticalAlignment.Center,

Foreground = Brushes.White,

FontSize = 24,

}

};

window.SourceInitialized += OnSourceInitialized;

window.Show();

});

WPF 同一窗口内的多线程 UI(VisualTarget)

WPF 的 UI 逻辑只在同一个线程中,这是学习 WPF 开发中大家几乎都会学习到的经验.如果希望做不同线程的 UI,大家也会想到使用另一个窗口来实现,让每个窗口拥有自己的 UI 线程.然而,就不能让 ...

C#关闭一个窗口的同时打开另一个窗口

在.net的WinForm程序中,如果是直接起动的Form作为主窗口,那么这个主窗口是不能关闭的,因为它维护了一个Windows消息循环,它一旦关闭了就等于声明整个应用程序结束,所以新打开的窗口也就被 ...

IDEA一个窗口打开多个项目

首先IDEA没有Eclipse的Workspace的概念,且IDEA推荐是一个窗口对应着一个Project. 然后经过研究你会发现IDEA其实是由一个主进程来维护这些窗口的,所以即使你开了很多个窗口, ...

拒绝卡顿——在WPF中使用多线程更新UI

原文:拒绝卡顿--在WPF中使用多线程更新UI 有经验的程序员们都知道:不能在UI线程上进行耗时操作,那样会造成界面卡顿,如下就是一个简单的示例: public partial class MainW ...

C# Wpf异步修改UI,多线程修改UI(二)

1.使用定时器异步修改 这是相对比较简单的方法 在Wpf中定时器使用DiapatcherTimer,不使用Timer原因: 在一个应用程序中,Timer会重复生成time事件,而DispatcherT ...

WPF里面多线程访问UI线程、主线程的控件

如果出现以下错误:调用线程无法访问此对象,因为另一个线程拥有该对象. 你就碰到多线程访问UI线程.主线程的控件的问题了. 先占位.

C++程序员面试题目总结(涉及C++基础、多线程多进程、网络编程、数据结构与算法)

说明:C++程序员面试题目总结(涉及C++基础知识.多线程多进程.TCP/IP网络编程.Linux操作.数据结构与算法) 内容来自作者看过的帖子或者看过的文章,个人整理自互联网,如有侵权,请联系作者 ...

Android多线程更新UI的方式

Android下,对于耗时的操作要放到子线程中,要不然会残生ANR,本次我们就来学习一下Android多线程更新UI的方式. 首先我们来认识一下anr: anr:application not rep ...

Python多线程多进程那些事儿看这篇就够了~~

自己以前也写过多线程,发现都是零零碎碎,这篇写写详细点,填一下GIL和Python多线程多进程的坑~ 总结下GIL的坑和python多线程多进程分别应用场景(IO密集.计算密集)以及具体实现的代码模块 ...

随机推荐

编写高质量代码:改善Java程序的151个建议(第1章:JAVA开发中通用的方法和准则___建议16~20)

建议16:易变业务使用脚本语言编写 Java世界一直在遭受着异种语言的入侵,比如PHP,Ruby,Groovy.Javascript等,这些入侵者都有一个共同特征:全是同一类语言-----脚本语言,它 ...

ASP.NET生成WORD文档,服务器部署注意事项

网上转的,留查备用,我服务器装的office2007所以修改的是Microsoft Office word97 - 2003 文档这一个. ASP.NET生成WORD文档服务器部署注意事项 1.Asp ...

pt-query-digest使用介绍【转】

本文来自:http://isadba.com/?p=651 一.pt-query-digest参数介绍. pt-query-digest --user=anemometer --password=an ...

Rotate List || LeetCode

/** * Definition for singly-linked list. * struct ListNode { * int val; * struct ListNode *next; * } ...

JQ 全选、全不选

$(document).ready(function() { $("#isalldebt").click(function() { if ($(this).attr("c ...

海量数据的二度人脉挖掘算法(Hadoop 实现)

最近做了一个项目,要求找出二度人脉的一些关系,就好似新浪微博的“你可能感兴趣的人” 中,间接关注推荐:简单描述:即你关注的人中有N个人同时都关注了 XXX . 在程序的实现上,其实我们要找的是:若 U ...

Android开发重修

Android程序开发的重新学习 #####Mars课程学习笔记20140926 1.Service主要用于完成耗时较长的操作,没有图形化界面. 2.Content Provider数据的提供者,是A ...

Route-map简介

Normal 0 7.8 磅 0 2 false false false EN-US ZH-CN X-NONE MicrosoftInternetExplorer4 /* Style Definiti ...

HDU 4185 Oil Skimming 【最大匹配】

题目大意: 给你一张图,图中有 '*' , '.' 两点,现在每次覆盖相邻的两个 '#' ,问最多能够覆盖几次. 解题分析: 无向图二分匹配的模板题,每个'#'点与周围四个方 ...

linux 进程间界面嵌套,WPF 同一窗口内的多线程/多进程 UI(使用 SetParent 嵌入另一个窗口)...相关推荐

  1. linux共享内存示例,linux 进程间共享内存示例

    写入端: #include #include #include #include #include using namespace std; struct MappingDataType { int ...

  2. msgget();msgsnd();msgrcv();msgctl(); 消息队列 Linux进程间的通信方式之消息队列

    Linux进程间的通信方式 ----消息队列. 消息队列和共享内存类似 消息队列它允许一个或多个进程向它写消息,一个或多个进程向它写读消息. 消息队列存在于系统内核中,消息的数量受系统限制. 我们来看 ...

  3. Linux 进程间通讯(IPC)方式 ------- 共享内存

    Linux 进程间通讯(IPC)方式有以下几种: 1->管道(pipe)和有名管道(fifo). 2->消息队列 3->共享内存 4->信号量 5->信号(signal) ...

  4. Linux 进程间通讯方式 pipe()函数

    Linux 进程间通讯方式有以下几种: 1->管道(pipe)和有名管道(fifo). 2->消息队列 3->共享内存 4->信号量 5->信号(signal) 6-&g ...

  5. linux进程间信号量

    linux进程间信号量 Linux 提供的各种系统调用来实现一个具有两种状态的信号量(binary semaphore). 分配和释放 和用于分配.释放共享内存的 shmget 和 shmctl 类似 ...

  6. linux无名管道实验代码,Linux 进程间通讯之创建无名管道和读写无名管道

    Linux进程间通讯的方式: 1. 管道(pipe)和有名管道(FIFO). 2. 信号(signal) 3. 消息队列 4. 共享内存 5. 信号量 6. 套接字(socket) 管道通讯: 无名管 ...

  7. 深刻理解 Linux 进程间七大通信(IPC)

    前言 网络编程是 Linux C/C++的面试重点,今天我就来聊一聊进程间通信的问题,文章末尾列出了参考资料,希望帮助到大家. 篇幅有点长,希望大家耐心阅读. Linux 下的进程通信手段基本上是从 ...

  8. Linux进程间通讯

    最初Unix IPC包括:管道.FIFO.信号: System V IPC包括:System V消息队列.System V信号灯.System V共享内存区: Posix IPC包括: Posix消息 ...

  9. linux进程间通讯-信号

    文章目录 进程间通信功能 信号 信号的概念 产生信号的方式 信号的默认(缺省)处理方式 进程接收到信号后的处理方式 kill函数 alarm函数 raise函数 abort函数 pause函数 sig ...

最新文章

  1. linux驱动:i2c驱动(一)
  2. 集线器、路由器与交换机
  3. Photon Server伺服务器在LoadBalancing的基础上扩展登陆服务
  4. java finally的作用_java中finally关键字的特点和作用是什么
  5. python中文乱码 def decode-python处理一些乱码的中文文本时decode('utf-8')报错的处理...
  6. 测试使用wiz来发布blog
  7. 计算机win10启动慢,Win10 开机慢/Win10启动慢的常见原因
  8. datax 不识别字段过滤_静电式空气过滤器有什么特点 静电式空气过滤器特点介绍【详解】...
  9. linux 安装org2pg_Ora2Pg的安装和使用
  10. php可以写无缝轮播图吗,怎样用css实现无缝轮播图切换?
  11. 163邮箱满了怎么办?无限容量,超大附件带你畅游文件的海洋
  12. java 分词获取词性_Ansj中文分词Java开发词性分类
  13. 统计检验 单尾还是双尾
  14. 梆梆爱加密java反调试绕过
  15. [系统与控制]力场盾系统升级原因
  16. [ExtJS] 颜色选择器2.0
  17. python 修改excel 路径_python更改已存在excel文件的方法
  18. Matlab快速创建矩阵的方法(创建特殊矩阵)
  19. Infrastructure 知识: dnf对module的处理
  20. 笔记本固态盘数据丢失怎么办?笔记本固态盘怎么恢复数据

热门文章

  1. X11 Wayland 及 Mir 比较
  2. 设置老版版谷歌浏览器自动启用flash
  3. 表格维护生成器-部分字段不能修改或不能看见
  4. 柳传志:古稀人生最后一个大弯
  5. 华为策略路由加等价路由_思科华为路由器如何利用route-map配置双wan口策略路由...
  6. 知网CAJ转为PDF下载
  7. css button按钮点击或者划过背景色填充
  8. 游戏后台之内存管理篇
  9. Sass系统技术选型笔记(2)JBPM
  10. 电机控制系统php,步进电机调速控制系统资料(原理图+单片机源码)