2012年1月27日金曜日

TextBlock に Clipboard へのコピー機能を追加する

2011/01/29 追記
この方法は、不具合がありました。改訂版は「TextBlock に Clipboard へのコピー機能を追加する(改訂版)」で紹介してます。

とあるお客さんで 「TextBlock に表示している文字列を他のアプリへコピーしたいんだけど...」と要求がありました。
Silverlightで作成したプログラムはすでに完成間近。

手間がかからず、短期間で実装したコードを紹介する。

操作は、TextBlockのテキストを右クリックすると、コンテキストメニューが表示され、メニューを選択すると、クリップボードへコピーする。

参考URL:Silverlight で ContextMenu を使ってみる



  1. TextBlockで右クリック
  2. 選択すると



MainPage.xamlは
<UserControl  x:Class="TextBlockClipboard.MainPage"
             xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" 
             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"
             xmlns:my="clr-namespace:TextBlockClipboard"
             mc:Ignorable="d"
             d:DesignHeight="200" d:DesignWidth="200"        >

    <Grid>
        <StackPanel>
            <TextBlock Text="TextBlock123456" my:TextBlockClipBoardService.ID=""/>
            <TextBlock Text="あああああああああ" my:TextBlockClipBoardService.ID="" />
        </StackPanel>
    </Grid>
</UserControl>

TextBlockClipBoardService.cs は

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;

namespace TextBlockClipboard
{
    /// <summary>
    /// TextBlock の Text 文字列を Clipboard へ コピーするサービス
    /// </summary>
    public static class TextBlockClipBoardService
    {
        /// <summary>
        /// ID 添付プロパティ
        /// </summary>
        public static readonly DependencyProperty IDProperty =
            DependencyProperty.RegisterAttached("ID",
                                                typeof(string),
                                                typeof(TextBlockClipBoardService),
                                                new PropertyMetadata(null, IDPropertyChanged));
        /// <summary>
        /// ID設定
        /// </summary>
        public static void SetID(DependencyObject obj, string value)
        {
            obj.SetValue(IDProperty, value);
        }
        /// <summary>
        /// ID取得
        /// </summary>
        public static string GetID (DependencyObject obj)
        {
            return (string)obj.GetValue(IDProperty);
        }


        /// <summary>
        /// ID設定(TextBlockに仕掛けを設定)
        /// </summary>
        /// <param name="d"></param>
        /// <param name="e"></param>
        private static void IDPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var uiTextBlock = d as TextBlock;
            if (uiTextBlock != null)
            {
                // TextBlockなら
                ContextMenu menu = new ContextMenu();
                MenuItem menuItem = new MenuItem();
                var img = new Image { Source =  new BitmapImage(new Uri("notecheck32.png", UriKind.RelativeOrAbsolute)) };
                img.Height = 16;
                menuItem.Icon = img;
                menuItem.Click += (menuItemSender, _) =>
                    {
                        // メニューを選択した
                        MenuItem localMenuItem = (MenuItem)menuItemSender;
                        try
                        {
                            Clipboard.SetText((string)localMenuItem.Tag);
                        }
                        catch (System.Security.SecurityException)
                        {
                        }
                    };
                menu.Tag = new WeakReference(uiTextBlock, false);
                menu.Items.Add(menuItem);
                menu.Opened += (menuOpendSender, _) =>
                    {
                        // 左クリックしてメニューが開いた
                        ContextMenu localMenu = (ContextMenu)menuOpendSender;
                        MenuItem localMenuItem = (MenuItem)localMenu.Items[0];
                        var localTextBlock = (TextBlock)((WeakReference)localMenu.Tag).Target;
                        localMenuItem.Header = "「" + localTextBlock.Text + "」をコピーします";
                        localMenuItem.Tag = localTextBlock.Text;
                    };
                ContextMenuService.SetContextMenu(uiTextBlock, menu);
            }
            else
            {
                throw new InvalidProgramException("TextBlockClipBoardService は TextBlock のみサポートしてます");
            }
        }

    }
}

ミソは、65行目でWeakReferenceを使って、相互参照地獄から逃れています。

プロジェクトファイル:TextBlockClipboard.zip





0 件のコメント:

コメントを投稿