午後わてんのブログ

ベランダ菜園とWindows用アプリ作成(WPFとC#)

WPF、マウスでTextBoxのサイズ変更するのにAdorner(装飾者)を使ってみた

サイズ変更のつまみは右下だけの簡易なもので、実質50行

結果

結果

Adornerを使ってサイズ変更
回転表示させているTextBoxのサイズ変更も違和感なくできているのがAdornerのすごいところだと思った
もしAdornerを使わずにCanvas上にThumbを配置っていう以前の方法だと回転角度の分がずれるから、その修正処理が必要だったはず


環境

コード

2023WPF/20230305_Adorner/20230305_Adorner at main · gogowaten/2023WPF

github.com



EzAdorner.cs

using System;
using System.Windows.Controls.Primitives;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;


//embellishとadornとdecorateの語感の違い... - Yahoo!知恵袋
//https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q1023045950
//adorn:飾る、装飾する、アドーン
//adorner:装飾者、アドーナー、アドナー
//装飾:decorate、装飾者:decorator

//装飾の概要 - WPF .NET Framework | Microsoft Learn
//https://learn.microsoft.com/ja-jp/dotnet/desktop/wpf/controls/adorners-overview?view=netframeworkdesktop-4.8
//WPF Adorners Part 1 – What are adorners
//https://www.nbdtech.com/Blog/archive/2010/06/21/wpf-adorners-part-1-ndash-what-are-adorners.aspx

//Mitesh Sureja's Blog: Adorners in WPF
//    http://miteshsureja.blogspot.com/2016/08/adorners-in-wpf.html
//簡略化した、例外とか考えてない、Thumbは右下に一個だけ
//装飾できるのはUIElementから狭めてFrameworkElementに変更
namespace _20230305_Adorner
{
    public class EzAdoner : Adorner
    {
        readonly Thumb MyThumb;//サイズ変更用つまみ
        readonly VisualCollection MyVisualChildren;//表示したい要素を管理する用?
        readonly FrameworkElement MyTarget;//装飾する対象要素
        public EzAdoner(FrameworkElement adornedElement) : base(adornedElement)
        {
            MyTarget = adornedElement;
            MyVisualChildren = new VisualCollection(this);
            MyThumb = new Thumb()
            {
                Cursor = Cursors.SizeNWSE,
                Height = 20,
                Width = 20,
                Opacity = 0.5,
                Background = Brushes.Red,
            };
            MyThumb.DragDelta += MyThumb_DragDelta;
            MyVisualChildren.Add(MyThumb);

            //TextBoxなどWidthの既定値がNaNなのを解除する
            MyTarget.Width = MyTarget.DesiredSize.Width;
            MyTarget.Height = MyTarget.DesiredSize.Height;
        }

        private void MyThumb_DragDelta(object sender, DragDeltaEventArgs e)
        {
            //対象要素のサイズ変更、Thumbより小さくならないようにする
            MyTarget.Width = Math.Max(MyTarget.Width + e.HorizontalChange, MyThumb.Width);
            MyTarget.Height = Math.Max(MyTarget.Height + e.VerticalChange, MyThumb.Height);
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            //Thumbの表示位置修正、常に対象要素の右下に表示
            MyThumb.Arrange(new Rect(
                MyTarget.Width / 2, MyTarget.Height / 2,
                MyTarget.Width, MyTarget.Height));
            return base.ArrangeOverride(finalSize);
        }

        #region VisualCollectionで必要        
        protected override int VisualChildrenCount => MyVisualChildren.Count;

        protected override Visual GetVisualChild(int index) => MyVisualChildren[index];
        #endregion VisualCollectionで必要
    }
}


参照したところは
Mitesh Sureja's Blog: Adorners in WPF
ここのを元にして、右下Thumbだけに絞って、その他の要らなさそうなのを削りまくったので、なんか不具合あるかも


MainWindow.xaml

<Window x:Class="_20230305_Adorner.MainWindow"
        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:local="clr-namespace:_20230305_Adorner"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="400">
  <Grid>
    <Canvas>
      <Ellipse x:Name="MyEllipse" Fill="Lavender" Stroke="DarkMagenta"                 
                 Canvas.Left="20" Canvas.Top="100" Width="50" Height="50"/>
      <TextBox x:Name="MyTextBox" Text="TextBox" FontSize="30"
               Canvas.Left="100" Canvas.Top="20">
        <TextBox.RenderTransform>
          <RotateTransform Angle="20"/>
        </TextBox.RenderTransform>
      </TextBox>
    </Canvas>
  </Grid>
</Window>




MainWindow.xaml.cs

using System.Windows;
using System.Windows.Documents;

namespace _20230305_Adorner
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            //Loaded時に装飾する
            Loaded += MainWindow_Loaded;
        }

        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            //静的メソッドのGetAdornerLayerで対象要素のAdornerLayerを取得して
            //そこにAdornerを追加する
            if (AdornerLayer.GetAdornerLayer(MyTextBox) is AdornerLayer layer)
            {
                layer.Add(new EzAdoner(MyTextBox));
                layer.Add(new EzAdoner(MyEllipse));
            }
            //if(AdornerLayer.GetAdornerLayer(MyEllipse)is AdornerLayer layer1)
            //{
            //    layer1.Add(new EzAdoner(MyEllipse));
            //}
        }
    }
}




よくわからんところ

VisualCollection
これは検索しても日本語だとあんまり出てこない

このなかにThumbを入れて管理しているみたい
VisualChildrenCountとGetVisualChildのオーバーライドが必要みたいで、それぞれThumbの個数と、指定IndexのThumbを返さないとThumbが表示されない

基準座標(0, 0)は対象要素の中央?
普通は座標(0, 0)は左上なんだけど、違うみたいで
Thumbの表示座標とサイズを指定しているみたいなところ、ArrangeOverrideのなかでのThumb.Arrangeの部分
左上が(0, 0)とするなら右下座標は対象要素の(幅, 高さ)になるはずなんだけど、実際には幅、高さともに半分の位置を指定している。ってことは対象要素の中央が(0, 0)?
今思ったけどこれはAdornerなのか、VisualCollectionどっち?

感想

Adornerは装飾対象と一体化しているような分離されているような不思議な感じがする

マウスでの要素サイズ変更はThumbとAdornerを使うのが正攻法っぽいのは検索するとわかるんだけど、難しそうで避けてきた
それでも今までの方法ではめんどくさいことが出てきたので、Adornerならどうなんだろうってことで少し試してみたのが今回で、4回目くらいかなあ
継承とかオーバーライドとか難しいねん

発覚しためんどくさいことは、作っているPixtack紫陽花3での矢印ベジェ曲線

Pixtack紫陽花3
矢印と頂点移動用のThumbを一体化しているから、矢印ベジェ曲線だけのサイズを取得するのがめんどくさいことがわかった
今取得できているサイズが灰色の部分
これが直線Polylineだとすべての頂点は線上にあるから問題なかったけど、ベジェ曲線だと制御点が線上にない場合が多いからサイズもそうだし座標もどうすればいいのかわからん
Adornerを使ってうまくできればいいなあってところ

Visual Studioのフォントの配色

Visual Studioの背景を黒にしてフォントの配色も変更してみた

Visual Studio
Visual Studioにもフォントの配色の保存とかエクスポートとかあればいいのにねえ、あればファイル名はDFIのLANPARTY


デザイナー画面
こっちはAOpenかなあ




参照したところ

embellishとadornとdecorateの語感の違い - Yahoo!知恵袋
detail.chiebukuro.yahoo.co.jp

装飾の概要 - WPF .NET Framework | Microsoft Learn
learn.microsoft.com

WPF Adorners Part 1 – What are adorners
www.nbdtech.com
簡潔

Mitesh Sureja's Blog: Adorners in WPF
miteshsureja.blogspot.com

関連記事

前回のWPF記事は一昨日
gogowaten.hatenablog.com

以前のサイズ変更方法、271日前
gogowaten.hatenablog.com

268日前
gogowaten.hatenablog.com

3年前
gogowaten.hatenablog.com

7年前
gogowaten.hatenablog.com