本文介绍如何使用 Windows 的 AppBar 相关 API 实现固定停靠在桌面上的特殊窗口。
停靠窗口
你可能并不明白停靠窗口是什么意思。
看下图,你可能使用过 OneNote 的停靠窗口功能。当打开一个新的 OneNote 停靠窗口之后,这个新的 OneNote 窗口将固定显示在桌面的右侧,其他的窗口就算最大化也只会占据剩余的空间。
OneNote 的这种功能可以让你在一边浏览网页或做其他事情的时候,以便能够做笔记。同时又不用担心其他窗口最大化的时候会占据记笔记的一部分空间。
这其实也是 Windows 任务栏所使用的方法。
OneNote 中给出的名称叫做“停靠窗口”,于是这可以代表微软希望用户对这个概念的理解名词。
只是,这个概念在 Windows API 中的名称叫做 AppBar。
AppBar
要做出停靠窗口的效果,最核心的 API 是 SHAppBarMessage
,用于发送 AppBar 消息给操作系统,以便让操作系统开始处理此窗口已形成一个 AppBar 窗口。也就是我们在用户交互上所说的“停靠窗口”。
虽然说要让一个窗口变成 AppBar 只需要一点点代码,但是要让整个停靠窗口工作得真的像一个停靠窗口,依然需要大量的辅助代码。所以我将其封装成了一个 DesktopAppBar
类,方便 WPF 程序来调用。
如何使用
以下使用,你需要先获取我封装的源码才可以编译通过:
你可以在 XAML 中使用:
<Window x:Class="Walterlv.Demo.DesktopDocking.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:dock="clr-namespace:Walterlv.Demo.DesktopDocking"
mc:Ignorable="d" Title="Walterlv 的停靠窗口" Height="450" Width="500"
dock:DesktopAppBar.AppBar="Right">
<StackPanel Background="#ffcd42">
<TextBlock FontSize="64" Margin="64" TextAlignment="Center" Text="walterlv 的停靠窗口" />
<Button Content="再停靠一个 - blog.walterlv.com" FontSize="32" Padding="32" Margin="32" Background="#f9d77b" BorderThickness="0"
Click="Button_Click"/>
</StackPanel>
</Window>
核心代码是其中的一处属性赋值 dock:DesktopAppBar.AppBar="Right"
,以及前面的命名空间声明 xmlns:dock="clr-namespace:Walterlv.Demo.DesktopDocking"
。
你也可以在 C# 代码中使用:
using System;
using System.Windows;
namespace Walterlv.Demo.DesktopDocking
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
DesktopAppBar.SetAppBar(this, AppBarEdge.Right);
}
}
}
使用以上代码中的任何一种方式,你就可以让你的窗口在右边停靠了。
从图中我们可以发现,我们的示例窗口停靠在了右边,其宽度就是我们在 XAML 中设置的窗口宽度(当然这是我封装的逻辑,而不是 AppBar 的原生逻辑)。
同时我们还能注意到,Visual Studio 的窗口是处于最大化的状态的——这是停靠窗口的最大优势——可以让其他窗口的工作区缩小,在最大化的时候不会覆盖到停靠窗口的内容。
另外,如果设置了第二个停靠窗口,那么第二个停靠窗口会挤下第一个窗口的位置。
[外链图片转存失败(img-OjZqTgeb-1564229607157)(https://i.loli.net/2019/07/27/5d3c3fbb9a9e364593.png)]
如何还原
Windows AppBar 的 API 有一个很不好的设定,如果进程退出了,那么 AppBar 所占用的空间 并不会还原!!!
不过不用担心,我在封装的代码里面加入了窗口关闭时还原空间的代码,如果你正常关闭窗口,那么停靠窗口占用的空间就会及时还原回来。
当然,你也可以适时调用下面的代码:
DesktopAppBar.SetAppBar(this, AppBarEdge.None);
附源码
由于源码一直在持续改进,所以本文中贴的源代码可能不是最新的。你可以在以下仓库找到这段源码的最新版本:
using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Interop;
// ReSharper disable IdentifierTypo
// ReSharper disable InconsistentNaming
// ReSharper disable EnumUnderlyingTypeIsInt
// ReSharper disable MemberCanBePrivate.Local
// ReSharper disable UnusedMember.Local
// ReSharper disable UnusedMember.Global
namespace Walterlv