Wednesday, 28 August 2013

Weird App Bar Window behavior

Weird App Bar Window behavior

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. After the window is rendered, it is moved into
the docked position.
So I need to either get my move to occur before the render or get the WPF
move to be ignored. Any ideas?

No comments:

Post a Comment