あのコードだと、メモリーは消費するわ、メモリーリークしまくるわで使えないことがわかりました。
ここに、改良版を投稿します
- メモリーリークの問題
右クリックで表示しているContextMenuですが、その右クリックを監視している ContextMenuService自体がメモリーリークを起こしているみたいで、OwnerのTextblockが破棄されれもContextMenuServiceに設定されたContextMenuは残っているみたいです。 - メモリーの消費
コードを読めばわかると思いますが、TextBlockの配下にContextMenuServiceとContextMenu、その下にMenuItemが作成されます。これらの生成されたインスタンス「TextBlockClipBoardService.ID=""」によって、すべてのTextBlockの子となるので、メモリーの消費量がタダモンではありません。
(実際、設定されたTextBlockをDataGridのCellTempleteに設定したらスクロールが耐えられないほどカクカク)
対応策として
- なるべく生成するインスタンスを少なくする
ContextMenuService は諦め、シングルトンのPopupオブジェクトを表示する - メモリーリークをなくす
循環参照を撲滅する。
で、できたコードは次の通り
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>
TextBlockClipboard.cs
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using System.Windows.Media.Effects;
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>
/// ポップアップ
/// </summary>
private static Popup RightPopupMenu = null;
/// <summary>
/// ポップアップに表示するTextBlock
/// </summary>
private static TextBlock PopupTextBlock = null;
/// <summary>
/// Popup 作成
/// </summary>
/// <returns>Popup</returns>
private static Popup CreatePopupMenu()
{
PopupTextBlock = new TextBlock();
PopupTextBlock.FontSize = 12;
PopupTextBlock.Padding = new Thickness(5, 2, 5, 2);
var border = new Border();
border.BorderThickness = new Thickness(1);
border.CornerRadius = new CornerRadius(5);
border.BorderBrush = new SolidColorBrush(Colors.Black);
border.Background = new SolidColorBrush(SystemColors.MenuColor);
border.Child = PopupTextBlock;
var grid = new Grid();
grid.Effect = new DropShadowEffect { Opacity = 0.4 };
grid.Children.Add(border);
var popup = new Popup();
popup.Child = grid;
popup.Opened += (s, e) =>
{
Popup localPopup = (Popup)s;
var text = (string)localPopup.Tag;
if (text.Length > 30)
{
text = text.Substring(0, 30) + " ... ";
}
PopupTextBlock.Text = "「" + text + "」をコピーします";
};
PopupTextBlock.MouseLeftButtonUp += (s, e) =>
{
try
{
Clipboard.SetText((string)popup.Tag);
}
catch (System.Security.SecurityException)
{
}
popup.IsOpen = false;
e.Handled = true;
};
PopupTextBlock.MouseLeave += (s, e) =>
{
popup.IsOpen = false;
};
return popup;
}
/// <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)
{
if (null == RightPopupMenu)
{
RightPopupMenu = CreatePopupMenu();
}
uiTextBlock.MouseRightButtonDown += (sender, ea) =>
{
RightPopupMenu.IsOpen = false;
ea.Handled = true;
};
uiTextBlock.MouseRightButtonUp += (sender,ea) =>
{
// 表示位置はカーソルがPopupの中へ入るように
RightPopupMenu.HorizontalOffset = ea.GetPosition(null).X - 50.0;
RightPopupMenu.VerticalOffset = ea.GetPosition(null).Y - 5.0;
TextBlock tb = (TextBlock)sender;
RightPopupMenu.Tag = tb.Text;
RightPopupMenu.IsOpen = true;
ea.Handled = true;
};
uiTextBlock.MouseLeftButtonUp += (sender,ea) =>
{
RightPopupMenu.IsOpen = false;
};
}
else
{
throw new InvalidProgramException("TextBlockClipBoardService は TextBlock のみサポートしてます");
}
}
}
}
画面はこんな感じ
プロジェクト:TextBlockClipboard2.zip

0 件のコメント:
コメントを投稿