午後わてんのブログ

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

昨日のを書き直した、WPF、マス目に敷き詰めたThumb、マウスドラッグ移動で入れ替え

動作

f:id:gogowaten:20210302110248g:plain
動作テスト中
動作結果は昨日のもので満足していたので、ほとんど同じ



コード

github.com

作成動作環境

動作に必要なのは.NET 5がインストール済みのWindowsで、.NET Frameworkだけでは動かないはず


MainWindow.xaml

これは昨日と変わらず

<Window x:Class="_20210302_Thumb移動入れ替え2回め.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:_20210302_Thumb移動入れ替え2回め"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="600">
  <Grid>
    <DockPanel>
      <StatusBar DockPanel.Dock="Bottom">
        <StatusBarItem x:Name="MyStatus" Content="status"/>
      </StatusBar>
      <Canvas x:Name="MyCanvas" UseLayoutRounding="True"/>
    </DockPanel>
  </Grid>
</Window>




MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Controls.Primitives;
using System.Collections.ObjectModel;

// _20210227_thumb移動入れ替え
//↑の改良版、入れ替え処理判定を面積から距離にした

namespace _20210302_Thumb移動入れ替え2回め
{
    public partial class MainWindow : Window
    {
        private const int MASU_X = 5;//横に並べる個数指定
        private const int MASU_Y = 3;//縦
        private const int MASU_WIDTH = 80;//Thumbの横幅指定
        private const int MASU_HEIGHT = 80;//縦幅
        //Thumbのリスト
        private ObservableCollection<FlatThumb> MyThumbs;
        //座標リスト
        private List<Point> MyPoints;

        public MainWindow()
        {
            InitializeComponent();
            MyInitialize();
        }

        private void MyInitialize()
        {
            MyPoints = new List<Point>();
            MyThumbs = new ObservableCollection<FlatThumb>();

            //Thumbを作成、表示
            for (int y = 0; y < MASU_Y; y++)
            {
                for (int x = 0; x < MASU_X; x++)
                {
                    FlatThumb t = new($"{(y * MASU_X) + x}")
                    {
                        Width = MASU_WIDTH,
                        Height = MASU_HEIGHT,
                        Opacity = 0.7,
                        FontSize = 40
                    };
                    MyCanvas.Children.Add(t);
                    Canvas.SetLeft(t, x * MASU_WIDTH);
                    Canvas.SetTop(t, y * MASU_HEIGHT);
                    t.DragDelta += Thumb_DragDelta;
                    t.DragCompleted += Thumb_DragCompleted;
                    MyThumbs.Add(t);

                    //基本座標セット
                    Point p = new(x * MASU_WIDTH, y * MASU_HEIGHT);
                    MyPoints.Add(p);
                }
            }
        }

        //ドラッグ移動終了イベント時、
        //Indexに対応した位置に移動させる
        private void Thumb_DragCompleted(object sender, DragCompletedEventArgs e)
        {
            FlatThumb t = sender as FlatThumb;
            int imaIndex = MyThumbs.IndexOf(t);
            //元のx座標へ移動
            Canvas.SetLeft(t, MyPoints[imaIndex].X);
            Canvas.SetTop(t, MyPoints[imaIndex].Y);

            MyStatus.Content = "";

        }

        /// <summary>
        /// ドラッグ移動中のThumbとその他のThumbとの重なり合う部分の面積を計算、
        /// 一定以上の面積があった場合、場所を入れ替えて整列
        /// </summary>
        /// <param name="t">ドラッグ移動中のThumb</param>
        /// <param name="x">Canvas上でのX座標</param>
        /// <param name="y">Canvas上でのY座標</param>
        private void Idou移動中処理(FlatThumb t, double x, double y)
        {
            t.Opacity = 0.7;
            int imaIndex = MyThumbs.IndexOf(t);//ドラッグ移動中ThumbのIndex
            MyStatus.Content = imaIndex.ToString();

            //最寄りのPoint
            int moyoriIndex = 0;
            double moyori距離 = double.MaxValue;
            for (int i = 0; i < MyPoints.Count; i++)
            {
                double distance = GetDistance(MyPoints[i], new Point(x,y));
                if (distance < moyori距離)
                {
                    moyori距離 = distance;
                    moyoriIndex = i;
                }
            }

            //最短距離のIndexと移動中のThumbのIndexが違うなら入れ替え処理
            if (moyoriIndex != imaIndex)
            {
                //Thumbリストのindexを入れ替え
                MyThumbs.RemoveAt(imaIndex);
                MyThumbs.Insert(moyoriIndex, t);

                //indexに従って表示位置変更
                ReplaceThumb表示位置更新(moyoriIndex);
            }
        }

        //2点間距離
        private double GetDistance(Point a, Point b)
        {
            return Math.Sqrt((a - b) * (a - b));
        }


        /// <summary>
        /// すべてのThumbの表示位置を更新
        /// </summary>
        /// <param name="avoidIndex">ドラッグ移動中のThumbのindex</param>
        private void ReplaceThumb表示位置更新(int avoidIndex)
        {
            for (int i = 0; i < MyThumbs.Count; i++)
            {
                if (i == avoidIndex) continue;//移動中のThumbは変更しない
                Canvas.SetLeft(MyThumbs[i], MyPoints[i].X);
                Canvas.SetTop(MyThumbs[i], MyPoints[i].Y);
            }
        }

        //ドラッグ移動イベント時
        private void Thumb_DragDelta(object sender, DragDeltaEventArgs e)
        {
            //移動
            FlatThumb t = sender as FlatThumb;
            double x = Canvas.GetLeft(t) + e.HorizontalChange;
            double y = Canvas.GetTop(t) + e.VerticalChange;
            Canvas.SetLeft(t, x);
            Canvas.SetTop(t, y);
            t.Opacity = 0.5;

            Idou移動中処理(t, x, y);
        }
    }



    public class FlatThumb : Thumb
    {
        private Grid MyPanel;

        public FlatThumb(string text)
        {
            ControlTemplate template = new(typeof(Thumb));
            template.VisualTree = new FrameworkElementFactory(typeof(Grid), "panel");
            this.Template = template;
            this.ApplyTemplate();
            MyPanel = (Grid)template.FindName("panel", this);
            MyPanel.Background = Brushes.Aqua;

            MyPanel.Children.Add(new TextBlock()
            {
                Text = text,
                TextAlignment = TextAlignment.Center,
                VerticalAlignment = VerticalAlignment.Center
            });
            MyPanel.Children.Add(new Border()
            {
                BorderBrush = Brushes.MediumBlue,
                BorderThickness = new Thickness(1)
            });
        }
    }
}

昨日は270行だったのが180行まで減った!


f:id:gogowaten:20210302111952p:plain
Templateを変更したThumb
これは昨日と同じ

大きな変更は入れ替え対象となるThumbの決定方法で、昨日は面積だったのを距離で判定することにした
ドラッグ移動中のThumbとの距離が一番近いマスをドラッグ移動中のThumbのマスとして、そのマスが今のマスと違っていたら入れ替え処理

f:id:gogowaten:20210302120049p:plain
移動中のThumbの左上の座標と、すべてのマスの左上の座標との距離を測る
距離を矢印にして表すと

f:id:gogowaten:20210302120417p:plain
こうで、一番近いのは元のマス、なのでこの状態では入れ替えは発生しない
ここからさらに下に動かして入れ替えになると

f:id:gogowaten:20210302120840p:plain
移動中のThumbが7番と入れ替え、入れ替えというより7番に移動かな
距離を見てみると

f:id:gogowaten:20210302121300p:plain
赤の矢印、7番のマスが一番近い


f:id:gogowaten:20210302112033p:plain 21行目
並べるThumb群を入れておくリストも昨日と同じ、変更通知を受け取れるObservableCollectionを使っているけど、普通のListでもいいはず
23行目
Pointのリスト
これは各Thumbの位置(左上)に対応した座標を入れておく
配置は左上から右下へなので、3x3のときは
f:id:gogowaten:20210302113238p:plain
こうで、
横幅 = 100、 縦幅 = 100のとき各Thumbの左上の座標は
f:id:gogowaten:20210302113418p:plain
こんな感じで

f:id:gogowaten:20210302113427p:plain
Pointにしてこうで、これを元にして各Thumbを配置

f:id:gogowaten:20210302123515p:plain
Thumbリストはこんな感じで、移動が発生したら

f:id:gogowaten:20210302123552p:plain
4番から7番へ移動の場合、4番をリストから一旦削除して

f:id:gogowaten:20210302123638p:plain
削除後はこうなって、7番に4番を挿入

f:id:gogowaten:20210302123717p:plain
Thumbリストで変更があったので、Pointリストに従って再配置する。t4は7番に移動したことで配置場所は(100, 100)から(100, 200)に移動になるし、t5からt7も配置場所が変更になる

f:id:gogowaten:20210302113733p:plain
DragDeltaイベントのドラッグ移動自体(144行目まで)は機能と同じ
Idou移動中処理(t, x, y);の中が

f:id:gogowaten:20210302113951p:plain
だいぶ短くなった、もうこれでいい




関連記事
31日後
gogowaten.hatenablog.com
今回の記事はこのアプリのためだった

昨日の記事は
gogowaten.hatenablog.com 今回のであんまり意味がなくなってしまった

gogowaten.hatenablog.com



5年前
gogowaten.hatenablog.com