我最近在我的 WPF 应用程序中添加了一个窗口,它可以作为“应用程序栏”停靠在桌面边缘。我用来做对接的代码来自这个stackoverflow 帖子。
该程序定义了与此窗口相关的三个用户设置。一个是窗口停靠的边缘,另外两个是Left
&Top
属性的值。这个想法是,当窗口关闭或程序关闭时,窗口将在程序重新启动时以相同的状态和位置重新打开。
我遇到的问题是,当程序打开时,窗口首先显示在屏幕上的随机位置(可能是创建窗口时由 Windows 分配给它的坐标),然后它移动到停靠位置。我见过的其他具有应用栏功能的程序,例如 Trillian,从一开始就被绘制在停靠位置。看到窗户这样移动有点令人不安。
这是窗口中的一些代码:
private void AppBarWindow_Activated( object sender, EventArgs e ) {
if ( Settings.Default.AppBarWindowEdge != ABEdge.None ) {
AppBarFunctions.SendShellActivated( this );
}
}
private void AppBarWindow_Closing( object sender, CancelEventArgs e ) {
Settings.Default.AppBarWindowLeft = Left;
Settings.Default.AppBarWindowTop = Top;
Settings.Default.Save();
AppBarFunctions.SetAppBar( this, ABEdge.None );
// Other, app specific code . . .
}
private void AppBarWindow_LocationChanged( object sender, EventArgs e ) {
if ( Settings.Default.AppBarWindowEdge != ABEdge.None ) {
AppBarFunctions.SendShellWindowPosChanged( this );
}
}
private void AppBarWindow_SourceInitialized( object sender, EventArgs e ) {
if ( Settings.Default.AppBarWindowEdge != ABEdge.None ) {
SizeWindow( Settings.Default.AppBarWindowEdge == ABEdge.None ? ABEdge.Left : ABEdge.None );
}
}
private void AppBarWindow_SizeChanged( object sender, SizeChangedEventArgs e ) {
if ( Settings.Default.AppBarWindowEdge != ABEdge.None ) {
AppBarFunctions.SendShellWindowPosChanged( this );
}
}
private void SizeWindow( ABEdge originalEdge ) {
// App specific code to compute the window's size . . .
if ( originalEdge != Settings.Default.AppBarWindowEdge ) {
AppBarFunctions.SetAppBar( this, Settings.Default.AppBarWindowEdge );
}
Settings.Default.AppBarWindowLeft = Left;
Settings.Default.AppBarWindowTop = Top;
Settings.Default.Save();
}
正如我在这篇文章中所读到的,我已经添加了SHAppBarrMessage
在窗口被激活或它的位置和大小发生变化时调用的函数。这些调用似乎对行为没有任何影响,所以我可能会删除它们。
我知道在显示窗口之前调用SourceInitialized
和Loading
事件,但在窗口句柄和布局和测量传递完成之后。但是,看起来窗口是在调用之前呈现的AppBarFunctions.SetAppBar
,这就是为什么我看到它出现然后移动到位的原因。
我还尝试通过将Left
和Top
属性设置为保存在窗口构造函数设置中的值来将窗口移动到停靠位置。那也没用。事实上,情况更糟,因为窗口首先在停靠位置绘制,然后显然从桌面边缘移开以腾出空间,然后又移回停靠位置。
如何让这个窗口在启动时出现在停靠位置并且之后不移动?
编辑:
我想我已经找到了问题的原因。AppBarFunctions
类代码中有一条注释,在方法中,就在它安排对窗口(UI 线程)上ABSetPos
的方法的调用之前。评论内容如下:DoResize
Dispatcher
// This is done async, because WPF will send a resize after a new appbar is added.
// if we size right away, WPFs resize comes last and overrides us.
所以显然 WPF 或 Windows 正在将窗口移出为窗口保留的空间,然后我将其移回。我在代码中添加了很多跟踪点,我可以看到窗口直到之后才呈现做出了这一举动(代码注释中提到的那个)。渲染窗口后,我的代码将其移动到停靠位置。
该类AppBarFunctions
已经添加了一个窗口过程挂钩,用于查看来自 shell 的消息。如果我添加对 WM_WINDOWPOSCHANGED 的检查,我可以以某种方式阻止消息被处理吗?或者,也许我可以更改 Windows / WPF 完成的移动的Left
和Top
属性的值,以便窗口最终到达我想要的位置?