single.php

C# WinUI3 で別画面をモーダル表示させる手順

C# WinUI 3アプリを作っていく途中で、調べたことを忘録的に投稿します。今回はWinUI3プロジェクトで、別のXamlで用意した画面をモーダル表示する場合の手順です。

MainWindowに別画面をモーダル表示

WinUI3プロジェクトを作成した際に、自動で作成される[MainWindow.xaml]で構成される画面に、こんな感じで別画面をモーダル表示する機能を実装してみます。

モーダル表示は、表示された画面が閉じられるまでメイン画面の操作ができなくなる表示方法です。

別画面を追加

準備として、表示する別画面をプロジェクトに追加します。

1.[ソリューション エクスプローラー]でプロジェクトのアイコンを右クリックして表示されたポップアップメニューで[追加|新しい項目]を選択します。

2. 表示された[新しい項目の追加]画面で[空白のウィンドウ(WinUI3)]を選択して名前を付けて[追加]をクリックします。

3. プロジェクトに新しく[.xaml]と[.xaml.cs]が追加されます。

追加された[.xaml]に画面を閉じるための[OK]と[キャンセル]ボタンを追加します。

<Window
  x:Class="WindowingApp.PopupWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:local="using:WindowingApp"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  mc:Ignorable="d"
  Title="PopupWindow">

  <Grid>
    <StackPanel>
      <TextBlock Text="PopupWindow.xaml" FontSize="14"/>
    </StackPanel>
    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
      <Button Name="button_ok" Click="button_ok_Click">OK</Button>
      <Button Name="button_cancel" Click="button_cancel_Click">キャンセル</Button>
    </StackPanel>
  </Grid>
</Window>

追加したボタンのイベントを[,xaml.cs]に追加します。

public sealed partial class PopupWindow : Window
{
    public PopupWindow()
    {
        this.InitializeComponent();
    }

    private void button_ok_Click(object sender, RoutedEventArgs e)
    {
        this.Close();
    }

    private void button_cancel_Click(object sender, RoutedEventArgs e)
    {
        this.Close();
    }
}

新しい画面を[PopupWindow]クラスとして表示するコードを[MainWindow]に追加します。

private void modalWindow_Click(object sender, RoutedEventArgs e)
{
  var popupWindow = new PopupWindow();
  OverlappedPresenter? presenter = popupWindow.AppWindow.Presenter as OverlappedPresenter;
  if (presenter != null)
  {
    popupWindow.AppWindow.Resize(new SizeInt32(640, 360));
    Center(popupWindow, this);
    SetOwnership(popupWindow.AppWindow, this);
    presenter.IsModal = true;
    presenter.IsResizable = false;
    popupWindow.AppWindow.SetPresenter(presenter);
    popupWindow.AppWindow.Show(true);
  }
}

private static void Center(Window window, Window ownerWindow)
{
  //画面を親画面の中央に配置
  PointInt32 CenteredPosition = ownerWindow.AppWindow.Position;
  CenteredPosition.X += (ownerWindow.AppWindow.Size.Width - window.AppWindow.Size.Width) / 2;
  CenteredPosition.Y += (ownerWindow.AppWindow.Size.Height - window.AppWindow.Size.Height) / 2;
  window.AppWindow.Move(CenteredPosition);
}

private static void SetOwnership(AppWindow ownedAppWindow, Window ownerWindow)
{
  //親画面を設定
  IntPtr parentHwnd = WindowNative.GetWindowHandle(ownerWindow);
  IntPtr ownedHwnd = Win32Interop.GetWindowFromWindowId(ownedAppWindow.Id);
  if (IntPtr.Size == 8)
  {
    //for x64
    SetWindowLongPtr(ownedHwnd, GWLP_HWNDPARENT, parentHwnd);
  }
  else
  {
    //for x86
    SetWindowLong(ownedHwnd, GWLP_HWNDPARENT, parentHwnd);
  }
}

[DllImport("User32.dll", CharSet = CharSet.Auto, EntryPoint = "SetWindowLongPtr")]
public static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong);

[DllImport("User32.dll", CharSet = CharSet.Auto, EntryPoint = "SetWindowLong")]
public static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);

注意点として、画面の親子関係を設定するために[SetOwnership]関数で表示する画面の親画面を設定する必要があります。

実行すると、こんな感じで[PopupWindow]クラスで作成した別画面が表示されます。

[OverlappedPresenter]の[IsModal]プロパティを有効にしているので、別画面が表示されている間は親画面に指定している[MainWindow]画面は操作出来ない状態です。

戻り値の追加

モーダル表示した別画面が閉じた時に[OK]や[キャンセル]など画面を閉じたボタンなどを戻り値で判定する機能を実装します。

列挙型の[PopupWindowResult]変数をクラスに追加して、各ボタンをクリックしたイベントプロシージャで設定します。

public enum PopupWindowResult
{
  OK,
  Cancel,
  None
}
public sealed partial class PopupWindow : Window
{
  public PopupWindowResult Result { get; private set; }

  public PopupWindow()
  {
    this.InitializeComponent();
    this.Result = PopupWindowResult.None;
  }

  private void button_ok_Click(object sender, RoutedEventArgs e)
  {
    this.Result = PopupWindowResult.OK;
    this.Close();
  }

  private void button_cancel_Click(object sender, RoutedEventArgs e)
  {
    this.Result = PopupWindowResult.Cancel;
    this.Close();
  }
}

画面を表示する側のプロシージャで[PopupDialogResult]変数を取得して、画面を閉じたボタンを判定できます。

private void modalWindow_Click(object sender, RoutedEventArgs e)
{
  var popupWindow = new PopupWindow();
  OverlappedPresenter? presenter = popupWindow.AppWindow.Presenter as OverlappedPresenter;
  if (presenter != null)
  {
    popupWindow.AppWindow.Resize(new SizeInt32(640, 360));
    Center(popupWindow, this);
    SetOwnership(popupWindow.AppWindow, this);
    presenter.IsModal = true;
    presenter.IsResizable = false;
    popupWindow.AppWindow.SetPresenter(presenter);
    popupWindow.AppWindow.Show(true);

    popupWindow.Closed += (sender, e) =>
    {
      if(popupWindow.Result == PopupWindowResult.OK)
      {
        //[OK]がクリックされた
      }
      else if(popupWindow.Result == PopupWindowResult.Cancel)
      {
        //[キャンセル]がクリックされた
      }
      else if(popupWindow.Result == PopupWindowResult.None)
      {
        //それ以外
      }    
    };
  }
}

まとめ

今回は短い記事ですが、Visual StudioのWinUI3プロジェクトで、別で追加した[.xaml]画面を、モーダル状態で別ウィンドウで表示する手順について紹介しました。

追加した[.xaml]画面`を[OverlappedPresenter]クラスを利用して別画面でモーダル表示が可能です。

また、列挙型の変数を追加して画面を閉じた際の判定も行うことができます。

WinUI 3アプリで[.xaml]画面を別画面としてモーダル表示したい人の参考になれば幸いです。

スポンサーリンク

最後までご覧いただき、ありがとうございます。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です