2013-08-28 1 views
5

Недавно я добавил окно в свое приложение WPF, которое можно состыковать до края рабочего стола как «панель приложений». Код, который я использую для док-станции, получен из сообщения this stackoverflow.Окно App Bar Window отскакивает от стыковки, затем переходит в положение стыковки

Программа имеет три пользовательских настроек, связанных с этим окном. Один из них - это край, где окно состыковано, а два других - значения свойств 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. Вызовы, похоже, не влияют на поведение, поэтому я могу удалить их.

Я знаю, что события SourceInitialized и Loading вызываются до отображения окна, но после того, как оконный дескриптор и макет & пройдены. Однако оказывается, что окно отображается до вызова AppBarFunctions.SetAppBar, поэтому я вижу, что оно появляется, а затем перемещается на место.

Я также попытался переместить окно в пристыкованное положение, установив свойства Left и Top на значения, сохраненные в настройках конструктора окна. Это тоже не сработало. На самом деле это было хуже, так как окно было сначала нарисовано в пристыкованном положении, а затем, по-видимому, было отодвинуто от края рабочего стола, чтобы освободить место для него, а затем снова переместилось в пристыкованное место.

Как я могу получить это окно, чтобы появиться в пристыкованном положении при запуске и не двигаться дальше?

Edit:

Я думаю, что я нашел причину этой проблемы. В коде класса AppBarFunctions есть комментарий, в методе 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, перемещает окно из пространства резервируется для окна, а затем переместить его обратно в Я добавил много точек трассировки в моем коде & I. может видеть, что окно не отображается до тех пор, пока не будет сделан этот шаг (тот, который указан в комментарии в коде). После визуализации окна он перемещается в пристыкованное положение по моему коду.

Класс AppBarFunctions уже добавляет крючок процедуры окна для wathcing для сообщений из оболочки. Если я добавлю чек для WM_WINDOWPOSCHANGED, могу ли я как-то остановить обработку сообщения?Или, может быть, я могу изменить значения свойств и Top для перемещения, выполняемого Windows/WPF, чтобы окно заканчивалось там, где я хочу?

ответ

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 нити окна.