質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.76%

DockPanelなどでMessageBoxを表現して標準のMessageBoxのように使いたい

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 542

tride

score 29

以下コードはウィンドウ内にパネルを使ってメッセージボックスを表現しています。
この場合に、よくあるMessageBoxのように、ボタン(OKボタンやキャンセルボタンなど)で状況に応じてイベントを起こしたいと思っていますが、どうすればよいか糸口すらつかめず悩んでおります。
通常、MessageBoxの場合、以下C#コードのように戻り値を得る事でその後にif文でボタンの動作を分けると思うのですが、同じような感じにしたいです。

MessageBoxResult flag =  MessageBox.Show("OKですか?");

尚、メッセージボックスの表示非表示は、Layer_MessageBoxWindowをVisibilityをHiddenとVisibleで切替えていることとします。
また、メッセージウィンドウを表示している間は、他の領域に触れない(Layer_MessageBoxWindowがドックパネルで他領域を占有している)状況です。

環境:Visual Studio 2019

<MainWindow.xaml>

        <!-- MessageBox Layer -->
        <DockPanel x:Name="Layer_MessageBoxWindow" Visibility="Hidden" Opacity="0" Background="#50000000">
            <StackPanel x:Name="MessageBox" Margin="0" Orientation="Vertical" VerticalAlignment="Center">
                <StackPanel Margin="0" Orientation="Horizontal" HorizontalAlignment="Center" >
                    <window:MessageBoxWindow />
                </StackPanel>
            </StackPanel>
        </DockPanel>

        <!-- Language -->
        <StackPanel x:Name="Layer_SubWindow" VerticalAlignment="Center">
            <window:LanguageMenuWindow x:Name="LanguageMenuWindow" />
        </StackPanel>

<MessageBoxWindow.xaml>

<UserControl x:Class="AppWPF.UserControlObjects.SubWindow.MessageBoxWindow"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             mc:Ignorable="d" 
             d:DesignHeight="150">
    <Grid x:Name="gridMessageBoxWindow" Background="#FFEEEEEE" Width="600" >

        <!-- レイアウト分割 -->
        <Grid.RowDefinitions>
            <RowDefinition Height="5"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="7*"/>
        </Grid.RowDefinitions>

        <!-- トップカラーバー -->
        <Canvas Height="5" Margin="0" Background="#FF0071EF" Grid.Row="0"/>

        <!-- メッセージ部分 -->
        <StackPanel VerticalAlignment="Center" Margin="25,30,25,25" Grid.Row="1">
            <TextBlock x:Name="MessageText" Text="{Binding Path=textData.Text, UpdateSourceTrigger=PropertyChanged}" Grid.Row="1" Margin="0" FontSize="18" FontWeight="Bold" TextWrapping="Wrap"/> 
        </StackPanel>

        <!-- ボタン部分 -->
        <StackPanel x:Name="PanelAllButton" Orientation="Horizontal" HorizontalAlignment="Center" Grid.Row="2" Margin="0,0,0,10">
            <Button x:Name="ButtonOK" Content="OK" VerticalAlignment="Center" Width="90" Height="35" Margin="10,0,10,0" Click="ButtonOK_Click" Visibility="Visible"/>
            <Button x:Name="ButtonCancel" Content="Cancel" VerticalAlignment="Center" Width="90" Height="35" Margin="10,0,10,0" Click="ButtonCancel_Click"/>
        </StackPanel>

    </Grid>
</UserControl>

2019/12/04追記:
回答の際に質問させていただいた内容を追記します。
内容としては別のユーザーコントロールでOKボタンを押し、メッセージウィンドウ(Layer_MessageBoxWindowのDockPanel)をVisibleまたはHiddenで表示切替して、OKボタンを押した結果を以下のような他のユーザーコントロール(LanguageMenuWindow)で受け取りたい場合となります。

尚表示非表示の処理に関しては、本文と違う為省略いたします。
※MainWindow.xamlにも以下xamlを読み込む部分を追記しました。
<LanguageMenuWindow>

<UserControl x:Class="AppWPF.UserControlObjects.SubWindow.LanguageMenuWindow"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             mc:Ignorable="d" Width="108" Height="134">
    <Grid x:Name="LanguageMenuPannel" Height="191" VerticalAlignment="Top" Margin="0,0,0,-57">

        <StackPanel Margin="10,10,10,41">
            <RadioButton x:Name="radioButton_ENG" Content="English" Height="22" VerticalContentAlignment="Center" Checked="RadioButtonENG_Checked"/>
            <RadioButton x:Name="radioButton_JPA" Content="日本語" Height="22" VerticalContentAlignment="Center" Checked="RadioButtonJPA_Checked" />
        </StackPanel>
        <Button x:Name="OK" Content="OK" Margin="10,0,10,10" Height="20" VerticalAlignment="Bottom" Click="OK_Click"/>

    </Grid>
</UserControl>
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

まずMessageBoxWindowはクライアント領域内を完全に覆うのでしょうから単に、

<Window>
  <Grid>
    <window:MessageBoxWindow />
    <window:LanguageMenuWindow />
  </Grid>
</Window>


こういう形でいいんじゃないでしょうか。

バインディングをうまく使えば、割とすっきりできそうにも思うのですが、

MessageBoxResult flag =  MessageBox.Show("OKですか?");

こういうイメージとのことでちょっと無理やり感もありますが、今のコードをあまり壊さない形でやってみました。


MainWindow

using System.Diagnostics;
using System.Windows;

namespace Questions227100
{
    public partial class MainWindow : Window
    {
        public MainWindow() => InitializeComponent();

        private async void Button_Click(object sender, RoutedEventArgs e)
        {
            var result = await MessageBoxWindow.ShowAsync(this, "いいんですね?");

            if(result == MessageBoxResult.OK) Debug.WriteLine("OK");
            if(result == MessageBoxResult.Cancel) Debug.WriteLine("Cancel");
        }
    }
}
<Window
  x:Class="Questions227100.MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:window="clr-namespace:Questions227100"
  Width="800"
  Height="450">
  <Grid>
    <window:MessageBoxWindow x:Name="messageBoxWindow" />

    <!--<window:LanguageMenuWindow x:Name="LanguageMenuWindow" />-->
    <!--  ↑の代わりにただのボタン  -->
    <Button Click="Button_Click" Content="何かを実行" />
  </Grid>
</Window>

MessageBoxWindow

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace Questions227100
{
    public partial class MessageBoxWindow : UserControl
    {
        private TaskCompletionSource<MessageBoxResult> taskCompletionSource;

        public MessageBoxWindow()
        {
            InitializeComponent();
            SetValue(Panel.ZIndexProperty, 10);
            Visibility = Visibility.Hidden;
        }

        public static Task<MessageBoxResult> ShowAsync(UIElement element, string message)
        {
            var win = Window.GetWindow(element);
            var msgbox = win.Descendants<MessageBoxWindow>().FirstOrDefault();
            if(msgbox == null) throw new InvalidOperationException("MessageBoxWindowが見つかりません。");
            return msgbox.ShowAsync(message);
        }
        public Task<MessageBoxResult> ShowAsync(string message)
        {
            MessageText.Text = message;
            Visibility = Visibility.Visible;
            taskCompletionSource = new TaskCompletionSource<MessageBoxResult>();
            return taskCompletionSource.Task;
        }

        private void ButtonOK_Click(object sender, RoutedEventArgs e)
        {
            Visibility = Visibility.Hidden;
            taskCompletionSource.SetResult(MessageBoxResult.OK);
        }
        private void ButtonCancel_Click(object sender, RoutedEventArgs e)
        {
            Visibility = Visibility.Hidden;
            taskCompletionSource.SetResult(MessageBoxResult.Cancel);
        }
    }



    //https://blog.xin9le.net/entry/2013/10/29/222336
    internal static class DependencyObjectExtensions
    {
        public static IEnumerable<DependencyObject> Children(this DependencyObject obj)
        {
            if(obj == null) throw new ArgumentNullException(nameof(obj));
            var count = VisualTreeHelper.GetChildrenCount(obj);
            if(count == 0) yield break;
            for(var i = 0; i < count; i++)
            {
                var child = VisualTreeHelper.GetChild(obj, i);
                if(child != null) yield return child;
            }
        }
        public static IEnumerable<DependencyObject> Descendants(this DependencyObject obj)
        {
            if(obj == null) throw new ArgumentNullException(nameof(obj));
            foreach(var child in obj.Children())
            {
                yield return child;
                foreach(var grandChild in child.Descendants()) yield return grandChild;
            }
        }
        public static IEnumerable<T> Children<T>(this DependencyObject obj)
            where T : DependencyObject => obj.Children().OfType<T>();
        public static IEnumerable<T> Descendants<T>(this DependencyObject obj)
            where T : DependencyObject => obj.Descendants().OfType<T>();
    }
}
<UserControl
  x:Class="Questions227100.MessageBoxWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  d:DesignHeight="450"
  d:DesignWidth="800"
  Background="#4C000000"
  mc:Ignorable="d">
  <Border
    MaxWidth="500"
    MaxHeight="300"
    VerticalAlignment="Center"
    Background="#FFEEEEEE"
    BorderBrush="#FF0071EF"
    BorderThickness="0,5,0,0">
    <Grid>
      <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition Height="Auto" />
      </Grid.RowDefinitions>
      <TextBlock
        x:Name="MessageText"
        Margin="10"
        FontWeight="Bold"
        TextWrapping="Wrap" />
      <StackPanel
        Grid.Row="1"
        HorizontalAlignment="Right"
        Orientation="Horizontal">
        <Button
          MinWidth="90"
          Margin="10"
          Click="ButtonOK_Click"
          Content="OK" />
        <Button
          MinWidth="90"
          Margin="10"
          Click="ButtonCancel_Click"
          Content="Cancel" />
      </StackPanel>
    </Grid>
  </Border>
</UserControl>

ダイアログだけかっこよくなってもバランスがありますので、
mahapps.metro - Dialogs
Material Design In XAML Toolkit - Dialogs

こういったものに乗ってしまえば、すでに同様のものが用意されています。
ただ導入するにもまた調整がいるので、是非にとまでは言えません。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/12/05 17:21

    回答ありがとうございます。
    現在、環境前にいないので、先にお礼だけしておきます。
    ソースについては、後程読んでいきます。

    それと既存で公開されているものは、とてもいいですね。
    ちょっと試してみたいと思います。

    キャンセル

  • 2019/12/09 14:59

    > MessageBoxWindowはクライアント領域内を完全に ・・・

    既存のコードから抜き出しての事だったので、このような形で記載してました。
    ただ、記載のほうがすっきりしてるので、ちょっとできないか試してみたいと思います。

    コードの内容については、理解するのに少々時間を取りましたが、ご記載されてる内容で実現する事ができました。
    特にawaitの部分は、コードをみて思わずその手があったかと手をたたきました(笑)

    キャンセル

+1

WPF はイベント発生元でイベントを処理しなかった場合に、親要素でイベントを処理できます。
これをバブルイベントと言います。
使い方は簡単で、MessageBoxWindowのイベントハンドラを消して、MainWindowのDockPanelにでもイベント処理を書きます。
ButtonBase.Click="Layer_MessageBoxWindow_Click"

そのイベントハンドラprivate void Layer_MessageBoxWindow_Click(object sender, RoutedEventArgs e)
引数 sender に押したボタンコントロールが入ってます。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/12/03 19:52

    となると、senderにて返ってきたbuttonの名前なりでMainWindow側で条件を判定させてやればよい、という事ですか?
    もしそうであるなら、他のユーザーコントロールからメッセージボックスのボタン結果を得たい場合も同じようにやればよいという事でしょうか。
    少々今目の前に環境がないので、先に回答させて頂きました。

    キャンセル

  • 2019/12/06 12:04

    あくまでも今のやり方に近い一例を挙げただけですので、このやり方が必ずしもベストではありません。
    そのうえでの回答としては、buttonの名前なりで判断せざるを得ないでしょう。
    何が良いかは状況に応じて流動的ですから、他のユーザーコントロール云々に対する答えはありません。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.76%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る