WPF實現多視窗在特定螢幕開啟並放大

  • 1197
  • 0

本文章提供如何

  • 透過MVVM概念實現WPF多視窗畫面
  • 將個別視窗顯示在指定螢幕中,與可能會犯的錯

在WPF的概念中,一個WPF僅會有一個application可以執行,而一個application可以顯示一個window(視窗),若需多視窗,只要多新增需要數量的window,並需設定其中一window為主視窗,本文以兩個視窗為範例:

兩個視窗的結構

主視窗(MainWindow)擁有子視窗(SlaveWindow);MainWindowModel擁有SlaveWinowModel,以保持結構的乾淨與親屬關係

當MainWindow與SlaveWindow的資料需要互相連動(例如兩個視窗操作相同資料時),則可採用以下做法:

MainWindowModel

// Fields
private SlaveWindowModel _slaveWindowModel = null;

// Constructors
public MainWindowModel(SlaveWindowModel slaveWindowModel)
{
    _slaveWindowModel = slaveWindowModel;

    // Handlers
    _slaveWindowModel.ButtonClicked += this.ButtonClicked;
}

// Methods
public void ButtonClicked(string input)
{
    // Do Something

    // Slave
    _slaveWindowModel.UpdateView(input);
}

SlaveWinowModel

// Constructors
public SlaveWindowModel()
{
}

// Events
public Action<string> ButtonClicked;
private void OnButtonClicked(string input)
{
    #region Contracts

    if (string.IsNullOrEmpty(input)) throw new ArgumentException();

    #endregion

    // Raise
    var handler = this.ButtonClicked;
    if (handler != null)
    {
        handler(input);
    }
}

// Methods
public void UpdateView(string input)
{
    // Update
}

子視窗在特定螢幕開啟並放大(錯誤示範)

在主視窗(MainWindow)中創立一個子視窗(SlaveWindow),並且將SlaveWindow的ViewModel提供給自己的ViewModel,使之可以操控SlaveWindowModel

MainWindow

// Fields
private System.Windows.Window _slaveWindow;

// Constructors
public MainWindow(int slaveScreenPosition)
{
    // Initailize
    this.InitializeComponent();

    // Start Slave Window
    _slaveWindow = new SlaveWindow(slaveScreenPosition);
    _slaveWindow.Show();
    
    // DataContext
    this.DataContext = new MainWindowModel(_slaveWindow.DataContext);
}

SlaveWindow

using System.Windows;
using System.Windows.Forms;

public SlaveWindow(int screenPosition)
{
    // Initailize
    this.InitializeComponent();

    // Screen.AllScreens.Length會先取得所有螢幕,確保指定螢幕存在
    if (Screen.AllScreens.Length >= (screenPosition + 1))
    {
         // 取得指定螢幕
        Screen screen = Screen.AllScreens[screenPosition];
        // 將Window畫布移至該螢幕的工作區
        System.Drawing.Rectangle r1 = screen.WorkingArea;
        this.Top = r1.Top;
        this.Left = r1.Left;

        // Maximized Window
        this.WindowState = WindowState.Maximized;
        this.Left = screen.WorkingArea.Left;
        this.Top = screen.WorkingArea.Top;
        this.Width = screen.WorkingArea.Width;
        this.Height = screen.WorkingArea.Height;
    }

    // DataContext
    this.DataContext = new SlaveWindowModel();
}

將SlaveWindow指定在規定的螢幕中打開,並且將視窗放至最大,

但要注意的是,如果這樣寫的話,Windows會自動把視窗放大並開在主螢幕

子視窗在特定螢幕開啟並放大(正確示範)

必須改成一開始是普通視窗且移置想要的螢幕上,在發生Window.Loaded事件時,才把視窗放大,才能確保Windows不會亂動該視窗,作法如下:

MainWindow

// Fields
private System.Windows.Window _slaveWindow;

// Constructors
public MainWindow(int slaveScreenPosition)
{
    // Initailize
    this.InitializeComponent();

    // Start Slave Window
    _slaveWindow = new SlaveWindow(slaveScreenPosition);
    
    // 增加Window_Loaded事件
    _slaveWindow.Loaded += Window_Loaded;
    _slaveWindow.Show();
    
    // DataContext
    this.DataContext = new MainWindowModel(_slaveWindow.DataContext);
}

// Handlers
private void Window_Loaded(object sender, RoutedEventArgs e)
{
    var senderWindow = sender as System.Windows.Window;
    senderWindow.WindowState = WindowState.Maximized;
    senderWindow.WindowStyle = WindowStyle.None;
}

SlaveWindow

using System.Windows.Forms;

public SlaveWindow(int screenPosition)
{
    // Initailize
    this.InitializeComponent();

    // 指定螢幕
    if (Screen.AllScreens.Length >= (screenPosition + 1))
    {
        Screen screen = Screen.AllScreens[screenPosition];
        System.Drawing.Rectangle r1 = screen.WorkingArea;
        this.Top = r1.Top;
        this.Left = r1.Left;

        // Normal Window
        this.WindowState = WindowState.Normal;
        this.Left = screen.WorkingArea.Left;
        this.Top = screen.WorkingArea.Top;
        this.Width = screen.WorkingArea.Width;
        this.Height = screen.WorkingArea.Height;
    }

    // DataContext
    this.DataContext = new CustomerWindowModel();
}

在SlaveWindow的Constructor中指定視窗為普通大小(WindowState.Normal),直到Load事件發生,才將視窗放大