Elsaの技術日記(徒然なるままに)

主に自分で作ったアプリとかの報告・日記を記載

MENU

WPFで自由にRectangleを描画する

先日、WPFで特定のアプリケーションに限定してスリーンショット録画の方法をまとめました。
elsammit-beginnerblg.hatenablog.com

ですが、
 ・複数アプリを録画したい
 ・好きなエリアを指定したい
といった場合にはアプリケーションに限定するだけでは不十分です。。
そこで、自分で指定したエリアで録画するアプリケーションを実装したいと考えました!

今回は、エリアを指定するためにRectangleを描画する方法をまとめたいと思います。



xamlの実装

今回メインウィンドウとして実装したxamlはこちらになります。
ウィンドウにBorderで枠線を作成し、その中にCanvasを差し込んでいるシンプルな構成です。
Canvasの名前は"CanvasArea"としました。

Canvas上ではトリガとしてこちらを定義しました。
 ・マウスの左ボタン押す :MouseLeftDwn
 ・マウスの左ボタン上げる:MouseLeftUp
 ・マウスを動かす    :MouseMoving

<Window x:Class="WpfApp2.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:WpfApp2"
        mc:Ignorable="d"
        Title="MainWindow" Height="408.739" Width="659.456">
    <Grid>
        <Border BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Left" Height="277" Margin="64,49,0,0" VerticalAlignment="Top" Width="528" Background="#00000000">
            <Canvas x:Name="CanvasArea" HorizontalAlignment="Left" Height="273" Margin="0,1,0,0" VerticalAlignment="Top" Width="523" Panel.ZIndex="1" PreviewMouseLeftButtonUp="MouseLeftUp" PreviewMouseLeftButtonDown="MouseLeftDwn" PreviewMouseMove="MouseMoving" Background="White" />
        </Border>
    </Grid>
</Window>

■好きなエリアにRectangleを作成する

では早速、Canvas上にエリアを指定するためのRectangleを描画してみたいと思います。
コードはこちら。

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;

namespace WpfApp2
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        bool isWriting = false;
        Point Init;
        private List<UIElement> RectangleList = new List<UIElement>();
        UIElement RectElement = new UIElement();

        public MainWindow()
        {
            InitializeComponent();
        }

        private void MouseLeftDwn(object sender, MouseButtonEventArgs e)
        {
            Console.WriteLine("Click Mouse Dwn");
            Canvas c = sender as Canvas;
            Init = e.GetPosition(c);
            c.CaptureMouse();
            isWriting = true;
        }

        private void MouseLeftUp(object sender, MouseButtonEventArgs e)
        {
            
            if (isWriting)
            {
                Console.WriteLine("Click Mouse Up");
                Canvas c = sender as Canvas;
                isWriting = false;
                c.ReleaseMouseCapture();
            }
        }

        private void WriteRectangle(Point point)
        {
           CanvasArea.Children.Remove(RectElement);
           

            Rectangle rect = new Rectangle();
            rect.Stroke = new SolidColorBrush(Colors.Red);
            rect.StrokeThickness = 1;

            rect.Width = Math.Abs(Init.X - point.X);
            rect.Height = Math.Abs(Init.Y - point.Y);

            if(Init.X < point.X)
            {
                Canvas.SetLeft(rect, Init.X);
            }
            else
            {
                Canvas.SetLeft(rect, point.X);
            }

            if(Init.Y < point.Y)
            {
                Canvas.SetTop(rect, Init.Y);
            }
            else
            {
                Canvas.SetTop(rect, point.Y);
            }

            CanvasArea.Children.Add(rect);
            RectElement = rect;
        }

        private void MouseMoving(object sender, MouseEventArgs e)
        {
            if (isWriting)
            {
                Console.WriteLine("Click Mouse Move");
                Point pos = e.GetPosition(CanvasArea);
                WriteRectangle(pos);
            }
        }
    }
}

マウスの左ボタン押すとMouseLeftDwnが実行されます。
MouseLeftDwnでは、
 ・マウスの強制キャプチャ
 ・マウスの位置を記憶
 ・マウスを押下したフラグをtrue
します。

        private void MouseLeftDwn(object sender, MouseButtonEventArgs e)
        {
            Console.WriteLine("Click Mouse Dwn");
            Canvas c = sender as Canvas;
            Init = e.GetPosition(c);
            c.CaptureMouse();
            isWriting = true;
        }

マウスの左ボタン押下した状態でマウスを動かすと、
MouseMovingがコールされます。
MouseMovingではWriteRectangleがコールされ、こちらでRectangleを描画します。

        private void MouseMoving(object sender, MouseEventArgs e)
        {
            if (isWriting)
            {
                Console.WriteLine("Click Mouse Move");
                Point pos = e.GetPosition(CanvasArea);
                WriteRectangle(pos);
            }
        }

最後にマウスの左ボタンを離すと、
MouseLeftUpがコールされます。
MouseLeftUpでは、
 ・マウスの強制キャプチャ解除
 ・マウスを押下したフラグをfalse

        private void MouseLeftUp(object sender, MouseButtonEventArgs e)
        {
            
            if (isWriting)
            {
                Console.WriteLine("Click Mouse Up");
                Canvas c = sender as Canvas;
                isWriting = false;
                c.ReleaseMouseCapture();
            }
        }

このコードを実行すると、
こちらのような結果となります。

■Rectangle描画領域をCanvasに限定する

先ほどのコードでRectangleを好きな位置に描画することが出来るのですが、
領域外にRectangleをはみ出して描画できてしまいます。

次に、Canvas領域内に限定してRectangleを留めるようなコードを実装してみます。
変更するのは、WriteRectangleのみになります。
コードはこちら。

        private void WriteRectangle(Point point)
        {
           CanvasArea.Children.Remove(RectElement);
           

            Rectangle rect = new Rectangle();
            rect.Stroke = new SolidColorBrush(Colors.Red);
            rect.StrokeThickness = 1;

            rect.Width = Math.Abs(Init.X - point.X);
            rect.Height = Math.Abs(Init.Y - point.Y);

            if (point.X > CanvasArea.ActualWidth)
            {
                Canvas.SetLeft(rect, Init.X);
                rect.Width = CanvasArea.ActualWidth - Init.X;
            }
            else if (point.X < 0)
            {
                Canvas.SetLeft(rect, 0);
                rect.Width = Init.X;
            }
            else if (Init.X < point.X)
            {
                Canvas.SetLeft(rect, Init.X);
            }
            else
            {
                Canvas.SetLeft(rect, point.X);
            }

            if (point.Y > CanvasArea.ActualHeight)
            {
                Canvas.SetTop(rect, Init.Y);
                rect.Height = CanvasArea.ActualHeight - Init.Y;
            }
            else if (point.Y < 0)
            {
                Canvas.SetTop(rect, 0);
                rect.Height = Init.Y;
            }
            else if (Init.Y < point.Y)
            {
                Canvas.SetTop(rect, Init.Y);
            }
            else
            {
                Canvas.SetTop(rect, point.Y);
            }

            CanvasArea.Children.Add(rect);
            RectElement = rect;
        }


実施していることですが、Canvasのエリア外に出てしまった場合にはサイズを
Canvasサイズ - 初期座標位置
とすることで、Canvasサイズ外にRectangleがはみ出さないように実装しております。

実際に動かすとこちらのようになります。

■最後に

今回はWPFで自由に位置やサイズを指定してRectangleを描画するコードをまとめました。
これをスクリーン録画とマージしてエリア指定して録画するアプリにしていきたいと思います!!