2013-08-28 44 views
5

Gần đây tôi đã thêm một cửa sổ vào ứng dụng WPF có thể được gắn vào cạnh của màn hình dưới dạng "thanh ứng dụng". Mã tôi đang sử dụng để thực hiện việc lắp ghép đến từ this bài đăng stackoverflow.App Bar Window bật ra khỏi vị trí docking, sau đó di chuyển vào vị trí docking

Chương trình có ba cài đặt người dùng được xác định liên quan đến cửa sổ này. Một là cạnh cửa sổ được gắn, hai giá trị kia là giá trị của các thuộc tính Left & Top. Ý tưởng là khi cửa sổ đóng, hoặc chương trình bị tắt, cửa sổ sẽ mở lại trong cùng trạng thái và vị trí khi chương trình khởi động lại.

Vấn đề tôi gặp phải là khi chương trình mở ra, cửa sổ được hiển thị đầu tiên ở vị trí ngẫu nhiên trên màn hình (có thể là các tọa độ được gán cho Windows khi cửa sổ được tạo) và sau đó nó di chuyển vào vị trí được cập nhật. Các chương trình khác mà tôi đã thấy có chức năng thanh ứng dụng, như Trillian, được vẽ ở vị trí được gắn vào từ đầu. Hơi bối rối khi thấy cửa sổ di chuyển như thế.

Dưới đây là một số mã từ cửa sổ:

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(); 
} 

Tôi đã thêm chức năng gọi SHAppBarrMessage khi cửa sổ được kích hoạt, hoặc khi vị trí và kích thước của nó thay đổi, như tôi đọc trong this acrticle. Các cuộc gọi dường như không có bất kỳ ảnh hưởng nào đến hành vi, vì vậy tôi có thể xóa chúng.

Tôi biết rằng các sự kiện SourceInitializedLoading được gọi trước khi cửa sổ được hiển thị nhưng sau khi xử lý cửa sổ và bố cục các đường đi đo được hoàn tất. Tuy nhiên, có vẻ như cửa sổ được hiển thị trước khi cuộc gọi đến số AppBarFunctions.SetAppBar được thực hiện, đó là lý do tại sao tôi thấy nó xuất hiện và sau đó chuyển vào vị trí.

Tôi cũng đã cố gắng di chuyển cửa sổ vào vị trí được gắn với đế bằng cách đặt các thuộc tính LeftTop cho các giá trị được lưu trong cài đặt trong hàm tạo của cửa sổ. Điều đó cũng không hiệu quả. Trong thực tế, nó đã tồi tệ hơn, khi cửa sổ lần đầu tiên được rút ra ở vị trí neo đậu, sau đó dường như đã được di chuyển ra khỏi cạnh máy tính để bàn để nhường chỗ cho nó, và sau đó di chuyển trở lại vào vị trí cập cảng.

Làm cách nào để cửa sổ này xuất hiện ở vị trí được gắn khi khởi động và không di chuyển sau đó?

Chỉnh sửa:

Tôi nghĩ rằng tôi đã tìm ra nguyên nhân của sự cố. Có một nhận xét trong mã lớp AppBarFunctions, theo phương thức ABSetPos, ngay trước khi nó lập lịch cuộc gọi đến phương thức DoResize trên cửa sổ Dispatcher (chuỗi giao diện người dùng). Các bình luận đọc:

// 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. 

Vì vậy, rõ ràng là WPF hoặc Windows đang chuyển động cửa sổ ra khỏi không gian được dành riêng cho các cửa sổ, và sau đó tôi di chuyển nó trở lại trong tôi đã thêm rất nhiều điểm dấu vết trong tôi đang & tôi. có thể thấy rằng cửa sổ không được hiển thị cho đến sau khi di chuyển đó được thực hiện (cái được đề cập trong chú thích trong mã). Sau khi cửa sổ được hiển thị, nó được chuyển vào vị trí được gắn bởi mã của tôi.

Lớp AppBarFunctions đã thêm móc móc thủ tục cửa sổ để thu thập thông điệp từ trình bao. Nếu tôi thêm séc cho WM_WINDOWPOSCHANGED, tôi có thể ngừng gửi thư không?Hoặc có lẽ tôi có thể thay đổi các giá trị cho các thuộc tính LeftTop cho di chuyển được thực hiện bởi Windows/WPF để cửa sổ kết thúc ở nơi tôi muốn nó được?

Trả lời

5

Tôi đã tìm thấy cách để cửa sổ không bị di chuyển khỏi khu vực được gắn. Về cơ bản, mã tôi đang sử dụng đã sử dụng phương thức Thủ tục cửa sổ móc để xem thông báo thông báo ABN_*. Tôi đã thêm mã trong phương thức này để xem cho WM_WINDOWPOSCHANGING thư.

Dưới đây là đoạn code tôi đã viết:

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; 
} 

Khi cửa sổ được cập cảng đến một cạnh & đăng ký với vỏ, nó nhớ lại hình chữ nhật docking trở về từ cuộc gọi đến SHAppBarMessage/ABM_SETPOS. Khi phương thức nhận được thông báo WM_WINDOWPOSCHANGED, nó sẽ kiểm tra xem cửa sổ có được gắn dọc theo cạnh và không bị kéo hay không. Nếu có, nó so sánh cấu trúc WINDOWPOS từ bộ nhớ không được quản lý thành một đối tượng được quản lý, đặt vị trí & kích thước của cửa sổ trở lại vị trí được gắn với kích thước & và so khớp với bộ nhớ không được quản lý. Sau đó, bộ được xử lý thành đúng số & thoát.

Điều này hoạt động hoàn hảo & giữ cho cửa sổ không bị bật khỏi vị trí được gắn của nó & trở lại. Và không cần phải lên lịch di chuyển vào vị trí được gắn trên cửa sổ Dispatcher luồng.