2014-03-03 20 views
6

當應用程序啓動時,我想讓我的WPF窗口自動捕捉到屏幕的右邊緣。有沒有辦法做到這一點?我也希望能夠保留它的尺寸。因此,與將窗口拖動到屏幕邊緣時發生的捕捉行爲不同,導致窗口調整爲屏幕的一部分或全屏幕時,我希望我的窗口能夠簡單地捕捉到某個位置的邊緣默認情況下,或者如果用戶將其拖動到特定位置,而不調整大小。我仍然希望保留用戶將窗口拖離邊緣的能力。如何在保持其大小的同時自動將WPF窗口捕捉到屏幕邊緣?

有沒有像已經實現的東西,或者我將不得不創建自己的行爲模式?我嘗試了許多搜索關鍵字組合,但找不到類似於我正在做的事情。一些搜索包括禁用捕捉行爲或提供捕捉行爲,但沒有以上述方式。

編輯:

我一直沒能找到一個現成的解決方案,所以我寫了我自己。該解決方案基於BenVlodgi的建議,所以我感謝他幫助我。這是一個非常粗糙的實現,仍然需要大量的拋光和更好的代碼技術,但它的工作原理,並且它是任何人想要嘗試這個的好基礎。它非常簡單,並且與WPF一起工作得非常好。這個實現的唯一限制是我還沒有嘗試讓它在兩個屏幕上工作,但它非常簡單(我只是沒有時間去做,而且現在我不需要這個功能) 。所以,這裏的代碼,我希望它可以幫助別人那裏:

public partial class MainWindow : Window 
{ 
    // Get the working area of the screen. It excludes any dockings or toolbars, which 
    // is exactly what we want. 
    private System.Drawing.Rectangle screen = 
           System.Windows.Forms.Screen.PrimaryScreen.WorkingArea; 

    // This will be the flag for the automatic positioning. 
    private bool dragging = false; 

    // The usual initialization routine 
    public MainWindow() 
    { 
     InitializeComponent(); 
    } 

    // Wait until window is lodaded, but prior to being rendered to set position. This 
    // is done because prior to being loaded you'll get NaN for this.Height and 0 for 
    // this.ActualHeight. 
    private void Window_Loaded(object sender, RoutedEventArgs e) 
    { 
     // Sets the initial position. 
     SetInitialWindowPosition(); 
     // Sets the monitoring timer loop. 
     InitializeWindowPositionMonitoring(); 
    } 

    // Allows the window to be dragged where the are no other controls present. 
    // PreviewMouseButton could be used, but then you have to do more work to ensure that if 
    // you're pressing down on a button, it executes its routine if dragging was not performed. 
    private void Window_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e) 
    { 
     // Set the dragging flag to true, so that position would not be reset automatically. 
     if (e.ChangedButton == System.Windows.Input.MouseButton.Left) 
     { 
      dragging = true; 
      this.DragMove(); 
     } 
    } 

    // Similar to MouseDown. We're setting dragging flag to false to allow automatic 
    // positioning. 
    private void Window_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e) 
    { 
     if (e.ChangedButton == System.Windows.Input.MouseButton.Left) 
     { 
      dragging = false; 
     } 
    } 

    // Sets the initial position of the window. I made mine static for now, but later it can 
    // be modified to be whatever the user chooses in the settings. 
    private void SetInitialWindowPosition() 
    { 
     this.Left = screen.Width - this.Width; 
     this.Top = screen.Height/2 - this.Height/2; 
    } 

    // Setup the monitoring routine that automatically positions the window based on its location 
    // relative to the working area. 
    private void InitializeWindowPositionMonitoring() 
    { 
     var timer = new System.Windows.Threading.DispatcherTimer(); 
     timer.Tick += delegate 
     { 
      // Check if window is being dragged (assuming that mouse down on any portion of the 
      // window is connected to dragging). This is a fairly safe assumption and held 
      // true thus far. Even if you're not performing any dragging, then the position 
      // doesn't change and nothing gets reset. You can add an extra check to see if 
      // position has changed, but there is no significant performance gain. 
      // Correct me if I'm wrong, but this is just O(n) execution, where n is the number of 
      // ticks the mouse has been held down on that window. 
      if (!dragging) 
      { 
       // Checking the left side of the window. 
       if (this.Left > screen.Width - this.Width) 
       { 
        this.Left = screen.Width - this.Width; 
       } 
       else if (this.Left < 0) 
       { 
        this.Left = 0; 
       } 

       // Checking the top of the window. 
       if (this.Top > screen.Height - this.Height) 
       { 
        this.Top = screen.Height - this.Height; 
       } 
       else if (this.Top < 0) 
       { 
        this.Top = 0; 
       } 
      } 
     }; 

     // Adjust this based on performance and preference. I set mine to 10 milliseconds. 
     timer.Interval = new TimeSpan(0, 0, 0, 0, 10); 
     timer.Start(); 
    } 
} 

確保你的窗口有以下幾點:

MouseDown="Window_MouseDown" 
MouseUp="Window_MouseUp" 
WindowStartupLocation="Manual" 
Loaded="Window_Loaded" 

而且,這不適合與Windows本地組件工作窗口的,比如最上面一欄,所以我禁用的風格和創建自己的(這實際上是爲我好,因爲我不希望這樣做的windows風格):

WindowStyle="None" 
+0

我用你的解決方案的一部分只是設置初始位置謝謝。不過,我改變了屏幕大小得到例程: private Rect screen = System.Windows.SystemParameters.WorkArea; 擺脫使用表格的需要 – sergeantKK

+0

@sergeantKK感謝您的提示,我會試一試。如果它對你有幫助,不要忘記註冊。 :) –

回答

4

沒有您可以調用API調用(據我所見)使用Windows捕捉功能,但是您只需獲得屏幕的System.Windows.Forms.Screen.PrimaryScreen.WorkingArea並相應地設置您的Top,Left,HeightWidthWindow的屬性。

編輯:上述建議確實需要Forms,您可能不需要。我相信WPF相當於System.Windows.SystemParameters.WorkArea

+1

你說得對,那是一個選項。這是我正在考慮的事情,但希望在那裏有一個捕捉API。真的很遺憾,像這樣的功能沒有暴露給我們。 –

+1

看看我的答案。我開發了自己的解決方案。這很簡單,像魅力一樣工作。 –

+1

雖然我用我的解決方案提供了額外的答案,但我相信您的建議讓我通過提供必要的屬性來研究該解決方案;因此,我認爲你的答案是正確的。 –

3

我不喜歡輪詢的方法,但它很難找到一個更好的解決方案,仍然簡單的WPF,所以我會發布我自己的。

我找到的解決方案其實很簡單,它重新實現了窗口的DragMove()方法的行爲,該方法允許您在窗口位置被拖動時更改窗口位置。以下代碼通過存儲窗口左上角與鼠標光標之間的距離來重新實現DragMove()

public partial class MainWindow : Window 
{ 
    // this is the offset of the mouse cursor from the top left corner of the window 
    private Point offset = new Point(); 

    public MainWindow() 
    { 
     InitializeComponent(); 
    } 

    private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e) 
    { 
     Point cursorPos = PointToScreen(Mouse.GetPosition(this)); 
     Point windowPos = new Point(this.Left, this.Top); 
     offset = (Point)(cursorPos - windowPos); 

     // capturing the mouse here will redirect all events to this window, even if 
     // the mouse cursor should leave the window area 
     Mouse.Capture(this, CaptureMode.Element); 
    } 

    private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e) 
    { 
     Mouse.Capture(null); 
    } 

    private void OnMouseMove(object sender, MouseEventArgs e) 
    { 
     if (Mouse.Captured == this && Mouse.LeftButton == MouseButtonState.Pressed) 
     { 
      Point cursorPos = PointToScreen(Mouse.GetPosition(this)); 
      double newLeft = cursorPos.X - offset.X; 
      double newTop = cursorPos.Y - offset.Y; 

      // here you can change the window position and implement 
      // the snapping behaviour that you need 

      this.Left = newLeft; 
      this.Top = newTop; 
     } 
    } 
} 

現在你可以實現這樣的捕捉/粘窗口行爲:該窗口會粘到屏幕的邊緣,如果它是一個範圍內的25個像素(正或負)之內。

int snappingMargin = 25; 

if (Math.Abs(SystemParameters.WorkArea.Left - newLeft) < snappingMargin) 
    newLeft = SystemParameters.WorkArea.Left; 
else if (Math.Abs(newLeft + this.ActualWidth - SystemParameters.WorkArea.Left - SystemParameters.WorkArea.Width) < snappingMargin) 
    newLeft = SystemParameters.WorkArea.Left + SystemParameters.WorkArea.Width - this.ActualWidth; 

if (Math.Abs(SystemParameters.WorkArea.Top - newTop) < snappingMargin) 
    newTop = SystemParameters.WorkArea.Top; 
else if (Math.Abs(newTop + this.ActualHeight - SystemParameters.WorkArea.Top - SystemParameters.WorkArea.Height) < snappingMargin) 
    newTop = SystemParameters.WorkArea.Top + SystemParameters.WorkArea.Height - this.ActualHeight; 

這種方法的缺點是捕捉將無法正常工作,如果正在標題欄拖動窗口,因爲這不火的OnMouseLeftButtonDown事件(我不需要,因爲我的窗口是無邊界的)。也許它仍然會幫助某人。

+0

非常感謝 - 爲我節省了一堆工作。 – 2017-03-28 21:01:00

相關問題