【winForm】レンジスライダーを自作する

C#

【winForm】でコントローラーを自作する

あまり僕はwinFormでコントローラーを作成することはない
大体のものがそろっているからだ
ただデータを絞る上でレンジスライダーが必要になったので作成方法を残しておく

最終的な動作

winform

手順と説明

コントローラーフォルダの作成
フォルダにRangeSlider.csファイルを作成する
winform

必要な項目

  • スライダーが取り得る最小値と最大値
  • スライダーの現在の下限値と上限値
  • 値が変更されたときに発生するイベント
  • スライダーのハンドルとトラックを描画するためのメソッド
  • マウス操作に対応するイベントハンドラ
  • イベントハンドラをドラッグしている間にスライダーの値の更新

コード全体

using System;
using System.Drawing;
using System.Windows.Forms;

public class RangeSlider : UserControl
{
    // 最小値、最大値、現在の下限値、上限値を保持する変数
    private int _minimum = 0;
    private int _maximum = 100;
    private int _lowerValue = 10;
    private int _upperValue = 90;

    // ドラッグ操作の状態を追跡するフラグ
    private bool isDraggingLower;
    private bool isDraggingUpper;

    // ドラッグが開始された時のマウスのX座標
    private int dragStartX;

    // Minimum と Maximum はスライダーの範囲を設定するプロパティ
    public int Minimum
    {
        get => _minimum;
        set { _minimum = value; Invalidate(); }
    }

    public int Maximum
    {
        get => _maximum;
        set { _maximum = value; Invalidate(); }
    }

    // LowerValue と UpperValue はスライダーの選択範囲を設定するプロパティ
    public int LowerValue
    {
        get => _lowerValue;
        set
        {
            if (_lowerValue != value)
            {
                _lowerValue = value;
                ValueChanged?.Invoke(this, EventArgs.Empty);
                Invalidate();
            }
        }
    }

    public int UpperValue
    {
        get => _upperValue;
        set
        {
            if (_upperValue != value)
            {
                _upperValue = value;
                ValueChanged?.Invoke(this, EventArgs.Empty);
                Invalidate();
            }
        }
    }

    // ValueChanged はプロパティの値が変更されたときに発火するイベント
    public event EventHandler ValueChanged;

    // OnPaint はコントロールの描画処理を担う
    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        Graphics g = e.Graphics;

        // スライダーのトラック(線)を描画
        g.DrawLine(Pens.Black, 0, Height / 2, Width, Height / 2);

        // 下限と上限のハンドルを描画
        int lowerX = GetHandlePosition(LowerValue);
        g.FillRectangle(Brushes.Blue, lowerX - 5, (Height / 2) - 10, 10, 20);

        int upperX = GetHandlePosition(UpperValue);
        g.FillRectangle(Brushes.Red, upperX - 5, (Height / 2) - 10, 10, 20);
    }

    // コンストラクタでマウスイベントを登録
    public RangeSlider()
    {
        this.MouseDown += RangeSlider_MouseDown;
        this.MouseMove += RangeSlider_MouseMove;
        this.MouseUp += RangeSlider_MouseUp;
    }

    // マウスが押されたときの処理
    private void RangeSlider_MouseDown(object sender, MouseEventArgs e)
    {
        int lowerHandleX = GetHandlePosition(LowerValue);
        int upperHandleX = GetHandlePosition(UpperValue);

        if (e.X >= lowerHandleX - 5 && e.X <= lowerHandleX + 5)
        {
            isDraggingLower = true;
            dragStartX = e.X;
        }
        else if (e.X >= upperHandleX - 5 && e.X <= upperHandleX + 5)
        {
            isDraggingUpper = true;
            dragStartX = e.X;
        }
    }

    // マウスが動いたときの処理
    private void RangeSlider_MouseMove(object sender, MouseEventArgs e)
    {
        if (isDraggingLower)
        {
            UpdateHandlePosition(ref _lowerValue, e.X);
        }
        else if (isDraggingUpper)
        {
            UpdateHandlePosition(ref _upperValue, e.X);
        }
    }

    // マウスボタンが離されたときの処理
    private void RangeSlider_MouseUp(object sender, MouseEventArgs e)
    {
        isDraggingLower = false;
        isDraggingUpper = false;
    }

    // ハンドルの位置を計算するヘルパーメソッド
    private int GetHandlePosition(int value)
    {
        return (int)((value - _minimum) / (double)(_maximum - _minimum) * Width);
    }

    // ハンドルの位置を更新するメソッド
    private void UpdateHandlePosition(ref int value, int mouseX)
    {
        int newValue = (int)(_minimum + (mouseX / (double)Width) * (_maximum - _minimum));
        newValue = Math.Max(_minimum, Math.Min(newValue, _maximum));

        if (isDraggingLower)
        {
            newValue = Math.Min(newValue, _upperValue);
        }
        else if (isDraggingUpper)
        {
            newValue = Math.Max(newValue, _lowerValue);
        }

        value = newValue;
        Invalidate();  // コントロールを再描画
        ValueChanged?.Invoke(this, EventArgs.Empty);  // 値変更イベントを発火
    }
}

コードを張り付け保存するとコントローラーとしてアイコンも変わる
winform
この状態でビルドをするとツールボックスにコントローラーが表示される
winform

作成したコントローラーを使用する

ドラッグアンドドロップで持ってくるとこうなる
winform

ラベルを追加してスライダーを動かしたときの数値の変化を確かめる
winform

using System;
using System.Windows.Forms;

namespace SampleControl
{
    public partial class Form1 : Form
    {
        private RangeSlider rangeSlider;
        public Form1()
        {
            InitializeComponent();
            rangeSlider1.ValueChanged += RangeSlider_ValueChanged;
        }

        private void RangeSlider_ValueChanged(object sender, EventArgs e)
        {
            label1.Text = rangeSlider1.LowerValue.ToString();
            label2.Text = rangeSlider1.UpperValue.ToString();
        }
    }
}

実際に動かすとこうなる
winform

数値が反映されているのがわかる

さいごに

このレンジスライダーは左右それぞれの領域を侵さない部分の作成が重要だ

コメント