自分用バックアップ2020_01_15_23_00(安定版)

思っていたところまで形にできました!このままでもいいかもだけど、後はできれば各 Thumb に何らかの Model データをバインドさせておいて、移動前の位置データと移動後の位置データを見ることができればとりあえずは終わりです。後はこの2つのデータをロジックに渡してごにょごにょすれば完成ですね!

多分、数日後の自分が読み返したら、何をしたかったんだろう?何に使うんだろう?と思って、思い出せずに後悔することでしょう。ガッハッハ!

目次

イメージ

f:id:sutefu7:20200115225911p:plain

XAML

<Window x:Class="WpfApp17.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:WpfApp17"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    <DockPanel>

        <StackPanel DockPanel.Dock="Right" Width="100" Margin="10,30,10,0">
            <Button x:Name="button1" Content="button1" Height="50" Margin="0,0,0,10" />
            <Button IsEnabled="False" Content="button2" Height="50" Margin="0,0,0,10" />
            <Button IsEnabled="False" Content="button3" Height="50" Margin="0,0,0,10" />
            <Button IsEnabled="False" Content="button3" Height="50" Margin="0,0,0,10" />
        </StackPanel>

        <Canvas SnapsToDevicePixels="True" Margin="30">

            <Canvas.Background>
                <VisualBrush TileMode="Tile" Viewbox="0,0,100,100" ViewboxUnits="Absolute" Viewport="0,0,100,100" ViewportUnits="Absolute">
                    <VisualBrush.Visual>
                        <Rectangle Stroke="DarkGray" StrokeThickness="1" Width="100" Height="100" StrokeDashArray="5 5" />
                    </VisualBrush.Visual>
                </VisualBrush>
            </Canvas.Background>
            
            <Thumb x:Name="thumb1" Canvas.Top="0" Canvas.Left="0" DragDelta="Thumb_DragDelta">
                <Thumb.Template>
                    <ControlTemplate>
                        <Grid Margin="10">
                            <Rectangle Width="80" Height="80" Stroke="Blue" StrokeThickness="1" Fill="AliceBlue" RadiusX="20" RadiusY="20" />
                            <TextBlock Text="textBlock1" HorizontalAlignment="Center" VerticalAlignment="Center" />
                        </Grid>
                    </ControlTemplate>
                </Thumb.Template>
            </Thumb>

            <Thumb x:Name="thumb2" Canvas.Top="100" Canvas.Left="300" DragDelta="Thumb_DragDelta">
                <Thumb.Template>
                    <ControlTemplate>
                        <Grid Margin="10">
                            <Rectangle Width="80" Height="80" Stroke="Red" StrokeThickness="1" Fill="LightPink" RadiusX="20" RadiusY="20" />
                            <TextBlock Text="textBlock2" HorizontalAlignment="Center" VerticalAlignment="Center" />
                        </Grid>
                    </ControlTemplate>
                </Thumb.Template>
            </Thumb>

            <Thumb x:Name="thumb3" Canvas.Top="0" Canvas.Left="400" DragDelta="Thumb_DragDelta">
                <Thumb.Template>
                    <ControlTemplate>
                        <Grid Margin="10">
                            <Rectangle Width="80" Height="80" Stroke="Red" StrokeThickness="1" Fill="LightPink" RadiusX="20" RadiusY="20" />
                            <TextBlock Text="textBlock3" HorizontalAlignment="Center" VerticalAlignment="Center" />
                        </Grid>
                    </ControlTemplate>
                </Thumb.Template>
            </Thumb>

        </Canvas>
    </DockPanel>

</Window>

コードビハインド

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfApp17
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            //
            Loaded += (s, e) =>
            {
                Console.WriteLine($"({Canvas.GetLeft(thumb1)}, {Canvas.GetTop(thumb1)}): thumb1");
                Console.WriteLine($"({Canvas.GetLeft(thumb2)}, {Canvas.GetTop(thumb2)}): thumb2");
                Console.WriteLine($"({Canvas.GetLeft(thumb3)}, {Canvas.GetTop(thumb3)}): thumb3");
            };

            button1.Click += (s, e) =>
            {
                Console.WriteLine($"({Canvas.GetLeft(thumb1)}, {Canvas.GetTop(thumb1)}): thumb1");
                Console.WriteLine($"({Canvas.GetLeft(thumb2)}, {Canvas.GetTop(thumb2)}): thumb2");
                Console.WriteLine($"({Canvas.GetLeft(thumb3)}, {Canvas.GetTop(thumb3)}): thumb3");
            };
        }

        private void Thumb_DragDelta(object sender, DragDeltaEventArgs e)
        {
            var target = sender as Thumb;
            if (target is null)
                return;
            
            var canvas1 = target.Parent as Canvas;
            if (canvas1 is null)
                return;

            // 現在 Canvas 上にいる位置のうち左上の位置(X, Y) に、移動量を加えて、
            // 実際に移動させるかどうか(Canvas.SetXxx() を呼ぶかどうか)を判定する
            var currentX = Canvas.GetLeft(target);
            var currentY = Canvas.GetTop(target);
            var nextX = currentX + e.HorizontalChange;
            var nextY = currentY + e.VerticalChange;

            // 100px 単位の移動に制限させる。(0, 0) を右に移動したら次の左上の位置は (100, 0) となる
            nextX = Math.Round(nextX / 100.0) * 100;
            nextY = Math.Round(nextY / 100.0) * 100;

            // 画面の端を超えそうになったら、吸着させて飛び越えさせない
            if (IsWall(canvas1, target, nextX, nextY))
                return;
            
            // 実際に移動する
            Canvas.SetLeft(target, nextX);
            Canvas.SetTop(target, nextY);

            // 移動後の場所に、別の物体があった場合は、元の位置に戻る
            // 移動中の Thumb を除く他の Thumb リストを取得
            var others = canvas1.Children.OfType<Thumb>().Where(other => other != target);
            if (IsSamePlace(others, nextX, nextY))
            {
                if (currentX != nextX)
                {
                    if (e.HorizontalChange != 0)
                    {
                        //  プラス値・・・右に移動したので、左に戻る
                        // マイナス値・・・左に移動したので、右に戻る
                        if (0 < e.HorizontalChange) nextX -= 100;
                        if (e.HorizontalChange < 0) nextX += 100;
                        Canvas.SetLeft(target, nextX);
                    }
                }

                if (currentY != nextY)
                {
                    if (e.VerticalChange != 0)
                    {
                        //  プラス値・・・下に移動したので、上に戻る
                        // マイナス値・・・上に移動したので、下に戻る
                        if (0 < e.VerticalChange) nextY -= 100;
                        if (e.VerticalChange < 0) nextY += 100;
                        Canvas.SetTop(target, nextY);
                    }
                }
            }
        }

        private bool IsWall(Canvas canvas1, Thumb target, double nextX, double nextY)
        {
            if (nextX < 0)
                return true;

            if (nextY < 0)
                return true;

            if (canvas1.ActualWidth < nextX + target.ActualWidth)
                return true;

            if (canvas1.ActualHeight < nextY + target.ActualHeight)
                return true;

            return false;
        }

        private bool IsSamePlace(IEnumerable<Thumb> others, double x, double y)
        {
            if (others is null) return false;
            if (!others.Any()) return false;
            
            foreach (var other in others)
            {
                var otherX = Canvas.GetLeft(other);
                var otherY = Canvas.GetTop(other);
                if (x == otherX && y == otherY)
                    return true;
            }
            
            return false;
        }
    }
}