I have recently added a window to my WPF application which can be docked to an edge of the desktop as an "app bar". The code I'm using to do the docking came from this stackoverflow post.
The program has three user settings defined related to this window. One is the edge where the window is docked, the other two are the values of the Left
& Top
properties. The idea is that when the window is closed, or the program is shut down, the window will open back in the same state and location when the program restarts.
The problem I'm having is that when the program opens up, the window is first displayed at a random location on the screen (probably the coordinates assigned to it by Windows when the window is created) and then it moves into the docked position. Other programs I've seen that have the app bar functionality, like Trillian, are drawn in the docked position from the beginning. It's a little disconcerting to see the window move like that.
Here is some code from the window:
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();
}
I have added functions to call SHAppBarrMessage
when the window is activated, or when its position and size change, as I read in this acrticle. The calls don't seem to have any effect on the behavior, so I might remove them.
I know that the SourceInitialized
and Loading
events are called before the window is displayed but after the window handle and the layout & measure passes have been completed. It appears, though, that the window is rendered before the call to AppBarFunctions.SetAppBar
is made, which is why I see it appear and then move into place.
I've also tried to move the window into the docked position by setting the Left
and Top
properties to the values saved in the settings in the window's constructor. That didn't work, either. In fact, it was worse, as the window was first drawn in the docked position, then apparently was moved away from that desktop edge to make room for it, and then moved back into the docked location.
How do I get this window to appear in the docked position upon start up and not move afterward?
Edit:
I think I have found the cause of the problem. There is a comment in the AppBarFunctions
class code, in the ABSetPos
method, just before it schedules a call to the DoResize
method on the window's Dispatcher
(UI thread). The comment reads:
// 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.
So apparently WPF or Windows is moving the window out of the space being reserved for the window, and I then move it back in. I added a lot of trace points in my code & I can see that the window isn't rendered until after that move is made (the one mentioned in the comment in the code). After the window is rendered, it is moved into the docked position by my code.
The AppBarFunctions
class already adds a window procedure hook for wathcing for messages from the shell. If I add a check for WM_WINDOWPOSCHANGED, could I somehow stop the message from being processed? Or maybe I can change the values for the Left
and Top
properties for the move done by Windows / WPF so the window ends up where I want it to be?