2013-08-28 23 views
5

我最近向WPF應用程序添加了一個窗口,該窗口可作爲「應用程序欄」停靠在桌面邊緣。我用來做對接的代碼來自this stackoverflow post。應用程序欄窗口從對接位置彈出,然後移動到對接位置

程序有三個與此窗口相關的用戶設置。一個是窗口對接的邊緣,另外兩個是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時被激活的窗口,或者當其位置和大小的變化,如我在this acrticle讀取。這些調用似乎對行爲沒有任何影響,所以我可能會刪除它們。

我知道SourceInitializedLoading事件在窗口顯示之前調用,但窗口句柄和佈局&度量傳遞已完成。不過,看起來窗口在致電AppBarFunctions.SetAppBar之前被渲染,這就是爲什麼我看到它出現然後移動到位。

我也嘗試通過將LeftTop屬性設置爲窗口構造函數中保存的值來將窗口移動到停靠位置。那也沒用。事實上,情況更糟,因爲窗戶首先被拉到停靠位置,然後顯然已從桌面邊緣移開以騰出空間,然後移回停靠位置。

如何讓這個窗口在啓動時出現在停靠的位置,而不是在之後移動?

編輯:

我想我找到了問題的原因。在AppBarFunctions類代碼中,在ABSetPos方法中,在它調度窗口的Dispatcher(UI線程)上的DoResize方法之前,有一條評論。註釋寫着:

// 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消息的wathcing添加了一個窗口過程掛鉤。如果我添加一個WM_WINDOWPOSCHANGED檢查,我可以以某種方式停止正在處理的消息?或者,也許我可以更改由Windows/WPF完成的移動的LeftTop屬性的值,以便窗口結束到我想要的位置?

回答

5

我發現了一種方法來防止窗口從停靠區域移動。基本上,我使用的代碼已經使用Window Procedure Hook方法來觀察ABN_*通知消息。我在此方法中添加了代碼以查看WM_WINDOWPOSCHANGING消息。

這是我寫的代碼:

public IntPtr WindowProcedureHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { 
    if (msg == (int) WinMessages.WM_WINDOWPOSCHANGING) { 
     if (IsDocked && !IsDragging) { 
      WindowPos pos = (WindowPos) Marshal.PtrToStructure(lParam, typeof(WindowPos)); 

      // Keep this window in its docked position. 
      pos.x = (int) DockedPosition.X; 
      pos.y = (int) DockedPosition.Y; 
      pos.cx = (int) DockedSize.Width; 
      pos.cy = (int) DockedSize.Height; 

      Marshal.StructureToPtr(pos, lParam, false); 
      handled = true; 
     } 

    } else if (msg == CallbackId) { 
     if (wParam.ToInt32() == (int) ABNotify.ABN_WINDOWPOSCHANGED) { 
      SetDockedPosition(Window, this, true); 
      handled = true; 
     } 
    } 
    return IntPtr.Zero; 
} 

當窗口停靠到與外殼註冊的邊緣&,它會記住從調用返回到SHAppBarMessage/ABM_SETPOS對接矩形。當該方法收到一個WM_WINDOWPOSCHANGED消息時,它會檢查窗口是否沿着邊緣停靠並且沒有被拖動。如果是,則將來自非託管內存的WINDOWPOS結構編組爲管理對象,將該窗口的位置大小設置回停靠位置&大小,然後將其封送回非託管內存。然後將其設置爲真正的&出口。

這工作完全&保持窗口從後彈跳從其停放的位置&了。而且也沒有必要安排搬進窗口的Dispatcher線對接的位置。

相關問題