あのコードだと、メモリーは消費するわ、メモリーリークしまくるわで使えないことがわかりました。
ここに、改良版を投稿します
- メモリーリークの問題
右クリックで表示している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 件のコメント:
コメントを投稿