かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

Silverlight 4で右クリックメニューを出すまでの道のり

画面上に置いたRectangle上で下のようなしょぼい右クリックメニューを出すまでの道のりは、意外と長いです。

とりあえず、はっきりいえるのは、右クリックイベントが追加されたから、おまえらがんばって右クリックメニューらしいものをうまく表示してね♪ってことみたいです。
ということで、上記の画像のようなものを実現するためのコードを晒します。解説はコード中のコメントとして入れてます。

<UserControl x:Class="SilverlightApplication9.MainPage"
    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"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">
        <Rectangle Height="94" HorizontalAlignment="Left" Margin="58,59,0,0" Name="rectangle1" Stroke="Black" StrokeThickness="1" VerticalAlignment="Top" Width="187" Fill="#FF8D4444" MouseRightButtonDown="rectangle1_MouseRightButtonDown" MouseRightButtonUp="rectangle1_MouseRightButtonUp" />
    </Grid>
</UserControl>
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;

namespace SilverlightApplication9
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
        }

        private void rectangle1_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            // デフォルトの右クリックメニューは出さない
            e.Handled = true;
        }

        private void rectangle1_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
        {
            // ポップアップに
            var popup = new Popup();

            // 画面いっぱいのGridを
            var grid = new Grid();
            grid.Width = Application.Current.Host.Content.ActualWidth;
            grid.Height = Application.Current.Host.Content.ActualHeight;

            // 追加して
            popup.Child = grid;

            // ポップアップ外の領域を表すキャンバスを
            var canvas = new Canvas { Width = grid.Width, Height = grid.Height };
            // クリックされたらポップアップを閉じるようにして
            canvas.MouseLeftButtonDown += (s, evt) =>
            {
                popup.IsOpen = false;
            };
            canvas.MouseRightButtonDown += (s, evt) =>
            {
                popup.IsOpen = false;
                evt.Handled = true;
            };
            canvas.Background = new SolidColorBrush(Colors.Transparent);

            // Gridに追加する
            grid.Children.Add(canvas);

            // 画面のリサイズ時にGridとCanvasのサイズ調整をする仕掛けを入れて
            EventHandler resize = (s, evt) =>
            {
                grid.Width = Application.Current.Host.Content.ActualWidth;
                grid.Height = Application.Current.Host.Content.ActualHeight;
                canvas.Width = grid.Width;
                canvas.Height = grid.Height;
            };
            Application.Current.Host.Content.Resized += resize;

            // Popupが閉じるタイミングでイベントハンドラの掃除をするようにして
            popup.Closed += (s, evt) =>
            {
                Application.Current.Host.Content.Resized -= resize;
            };

            // マウスが押された位置に
            var pos = e.GetPosition(this);

            // Borderをセットする
            var border = new Border 
            { 
                Background = new SolidColorBrush(Colors.LightGray),
                Margin = new Thickness(pos.X, pos.Y, 0, 0),
                HorizontalAlignment = HorizontalAlignment.Left,
                VerticalAlignment = VerticalAlignment.Top
            };
            // Borderにテキストを入れ込んで
            border.Child = new TextBlock 
            { 
                Text = "Hello world", 
            };
            // クリックイベントを登録して
            border.MouseLeftButtonDown += (s, evt) =>
            {
                MessageBox.Show("OK");
                popup.IsOpen = false;
            };
            // Gridに追加して
            grid.Children.Add(border);

            // Popupを表示する
            popup.IsOpen = true;
        }
    }
}

いや〜。もっと簡単にできるもんだと思ってた。