当前位置:首页 > 技术知识 > 正文内容

WPF 在 Windows 实现任务栏缩略图

WPF 在 Windows 实现任务栏缩略图

控件名:SetIconicThumbnail

作 者:WPFDevelopersOrg - 驚鏵&赵嘉

原文链接[1]:https://github.com/WPFDevelopersOrg/WPFDevelopers

码云链接[2]:https://gitee.com/WPFDevelopersOrg/WPFDevelopers

  • 框架支持.NET4 至 .NET8
  • Visual Studio 2022;

Windows Vista 系统上,微软首次推出任务栏缩略图预览功能。

Windows 任务栏中,当我们将窗口最小化并将鼠标悬停在图标上,会显示一个窗口预览缩略图。通过 Windows DWM(Desktop Window Manager)[3]提供的 API,我们可以自定义这个缩略图的内容,实现类似音乐播放器当前播放的音乐封面图。

1. 修改 WindowHelpers.cs
  • 先检查 window 是否为 imagePath 是否为空。
  • 使用 WindowInteropHelper 获取 window 的句柄 (hwnd);
  • 注册窗口消息钩子,监听 DWM 消息;
  • window.ShowInTaskbar = true;设置 Window 有任务栏图标,否则无法显示预览缩略图。
public static void SetIconicThumbnail(this Window window, string imagePath)
{
if (window == || string.IsOrWhiteSpace(imagePath)) return;
if (!File.Exists(imagePath)) return;
IntPtr hwnd = new WindowInteropHelper(window).Handle;
int size = Marshal.SizeOf(typeof(int));
IntPtr pBool = Marshal.AllocHGlobal(size);
try
{
Marshal.WriteInt32(pBool, 1);
Win32.DwmSetWindowAttribute(hwnd, DwmWindowAttributes.FORCE_ICONIC_REPRESENTATION, pBool, size);
Win32.DwmSetWindowAttribute(hwnd, DwmWindowAttributes.HAS_ICONIC_BITMAP, pBool, size);
}
finally
{
Marshal.FreeHGlobal(pBool);
}
var source = HwndSource.FromHwnd(hwnd);
if (source != )
{
source.AddHook(new HwndSourceHook((IntPtr hwnd2, int msg, IntPtr wParam, IntPtr lParam, refbool handled) =>
{
if (msg == WindowsMessageCodes.WM_DWMSENDICONICTHUMBNAIL)
{
int width = ((int)((((long)lParam) >> 16) & 0xFFFF));
int height = ((int)((long)lParam & 0xFFFF));
try
{
using (var bmp = new Bitmap(imagePath))
using (var resized = new Bitmap(bmp, new Size(width, height)))
{
IntPtr hBitmap = resized.GetHbitmap();
Win32.DwmSetIconicThumbnail(hwnd2, hBitmap, (int)DwmWindowAttributes.None);
Win32.DeleteObject(hBitmap);
}
}
catch (Exception ex)
{
thrownew Exception($"DwmSetIconicThumbnail error :{ex.Message}!");
}

handled = true;
}

return IntPtr.Zero;
}));
}
Win32.DwmInvalidateIconicBitmaps(hwnd);
window.ShowInTaskbar = true;
}

2. 修改 Win32.cs
  • DwmSetWindowAttribute 设置打开缩略图显示图片;
  • DwmSetIconicThumbnail 设置自定义缩略图给任务栏;
  • DwmInvalidateIconicBitmaps 通知 DWM 当前的缩略图无效,重新请求;
  • WM_DWMSENDICONICTHUMBNAILDWM 向窗口发送消息,请求新的缩略图;
  • DeleteObject()手动释放 GDI ,避免内存泄漏;
[DllImport(Gdi32)]
public static extern bool DeleteObject(IntPtr hObject);

[DllImport(DwmApi)]
public static extern int DwmSetIconicThumbnail(IntPtr hwnd, IntPtr hbmp, int dwSITFlags);

[DllImport(DwmApi)]
public static extern int DwmSetWindowAttribute(IntPtr hwnd, DwmWindowAttributes dwAttribute, IntPtr pvAttribute, int cbAttribute);

[DllImport(DwmApi)]
public static extern int DwmInvalidateIconicBitmaps(IntPtr hwnd);

internalclassWindowsMessageCodes
{
publicconstint WM_DWMSENDICONICTHUMBNAIL = 0x0323;
}

[Flags]
publicenum DwmWindowAttributes : uint
{
None = 0,
DISPLAYFRAME = 1,
FORCE_ICONIC_REPRESENTATION = 7,
HAS_ICONIC_BITMAP = 10
}
3. 新增 IconicThumbnailWindowExample.xaml
  • 新增两个按钮切换图片,BtnPrevious_ClickBtnNext_Click
  • Image 控件用于显示任务栏的缩略图。
<wd:Window
x:Class="WPFDevelopers.Sample.ExampleViews.IconicThumbnailWindowExample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converts="clr-namespace:WPFDevelopers.Sample.Converts"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WPFDevelopers.Sample.ExampleViews"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:model="clr-namespace:WPFDevelopers.Sample.Models"
xmlns:wd="https://github.com/WPFDevelopersOrg/WPFDevelopers"
Title="IconicThumbnailWindowExample"
Width="600"
Height="450"
Icon="pack://application:,,,/WPFDevelopers.Samples;component/WPFDevelopers.ico"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="7*" />
<RowDefinition Height="3*" />
</Grid.RowDefinitions>
<Image
x:Name="ImagePreview"
Width="200"
Height="200"
VerticalAlignment="Bottom" />

<StackPanel
Grid.Row="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
wd:PanelHelper.Spacing="3"
Orientation="Horizontal">

<Button
x:Name="BtnPrevious"
Width="50"
Height="50"
Click="BtnPrevious_Click">

<wd:PathIcon Width="10" Kind="Previous" />
</Button>
<Button
x:Name="BtnNext"
Width="50"
Height="50"
Click="BtnNext_Click">

<wd:PathIcon Width="10" Kind="Next" />
</Button>
</StackPanel>
</Grid>
</wd:Window>
4. 新增 IconicThumbnailWindowExample.xaml.cs
  • BtnNext_ClickcurrentFileIndex = (currentFileIndex + 1) % fileList.Count; 通过代码实现图片循环切换:每次点击按钮时,currentFileIndex 会增加 1,如果超过了 fileList 的最大索引,回到第一个图片。。
  • BtnPrevious_ClickcurrentFileIndex = (currentFileIndex - 1 + fileList.Count) % fileList.Count; 通过代码实现图片循环切换:每次点击按钮时,currentFileIndex 会减少 1,并且如果当前索引小于 0,回到最后一张图片。
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows;
using System.Windows.Media.Imaging;
using WPFDevelopers.Helpers;

namespaceWPFDevelopers.Sample.ExampleViews
{
/// <summary>
/// IconicThumbnailWindowExample.xaml 的交互逻辑
/// </summary>
publicpartialclassIconicThumbnailWindowExample
{
private List<string> fileList = new List<string>();
privateint currentFileIndex = -1;
public IconicThumbnailWindowExample()
{
InitializeComponent();
Loaded += IconicThumbnailWindowExample_Loaded;
}

private void IconicThumbnailWindowExample_Loaded(object sender, RoutedEventArgs e)
{
fileList.Clear();
currentFileIndex = -1;
var directorys = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "IconicThumbnail");
if (!Directory.Exists(directorys)) return;
string[] files = Directory.GetFiles(directorys);
fileList.AddRange(files);
}

private void BtnPrevious_Click(object sender, RoutedEventArgs e)
{
if (fileList.Count == 0) return;
currentFileIndex = (currentFileIndex + 1) % fileList.Count;
var img = fileList[currentFileIndex];
ImagePreview.Source = new BitmapImage(new Uri(img));
this.SetIconicThumbnail(img);
}
private void BtnNext_Click(object sender, RoutedEventArgs e)
{
if (fileList.Count == 0) return;
currentFileIndex = (currentFileIndex - 1 + fileList.Count) % fileList.Count;
var img = fileList[currentFileIndex];
ImagePreview.Source = new BitmapImage(new Uri(img));
this.SetIconicThumbnail(img);
}
}
}




GitHub 源码地址[4]

Gitee 源码地址[5]

参考资料
[1]

原文链接: https://github.com/WPFDevelopersOrg/WPFDevelopers

[2]

码云链接: https://gitee.com/WPFDevelopersOrg/WPFDevelopers

[3]

Windows DWM(Desktop Window Manager): https://learn.microsoft.com/zh-cn/windows/win32/dwm/dwm-overview?redirectedfrom=MSDN

[4]

GitHub 源码地址: https://github.com/WPFDevelopersOrg/WPFDevelopers/blob/dev/src/WPFDevelopers.Shared/Core/Helpers/WindowHelpers.cs

[5]

Gitee 源码地址: https://gitee.com/WPFDevelopersOrg/WPFDevelopers/blob/dev/src/WPFDevelopers.Shared/Core/Helpers/WindowHelpers.cs

相关文章

第四章:产品设计(2.3)PRD写作 - 原型设计

2.3、原型设计(界面线框图)当我们逐渐清晰了产品的需求后,并梳理了产品的各个频道及页面,那么这一步就要开始验证这些想法的具体界面表现和方案的可行性了。原型设计是帮助我们更细致的思考,并做各项需求的评...

Windows 加密盘BitLocker爆锁屏绕过严重漏洞

BitLocker Windows内置现代设备级数据加密保护功能,BitLocker与Windows内核深度集成。有大量的企业和个人使用BitLocker加密自己关键数据,以防止数据泄密。BitLoc...

2018年度回顾:挖矿木马为什么会成为病毒木马黑产的中坚力量

一、概述根据腾讯御见威胁情报中心监测数据,2018年挖矿木马样本月产生数量在百万级别,且上半年呈现快速增长趋势,下半年上涨趋势有所减缓。由于挖矿的收益可以通过数字加密货币系统结算,使黑色产业变现链条十...

机器人需求驱动导航新SOTA,成功率提升15%!浙大&amp;vivo联手打造

CogDDN团队 投稿量子位|公众号QbitAI让机器人像人一样边看边理解,来自浙江大学和vivo人工智能实验室的研究团队带来了新进展。正如视频所展示的,机器人在复杂的室内环境中不仅能自主探索,还具备...

微软明年要停止SQL Server 2005的技术支持了

站长之家(Chinaz.com)12月28日消息据外媒消息称,微软将于明年停止为SQL Server 2005提供技术支持,即不再为其提供新的安全补丁、新功能、应用升级等服务。且表示在停止技术支持后,...

Android监听滚动视图(监听页面滚动)

Android UI Libs之Android-ObservableScrollView1. 说明Android-ObservableScrollView,顾名思义,Android上观察滚动的视图,可...