かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

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;
        }
    }
}

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