移動とリサイズ可能なコントロール
ネタ元:VB.NETで作る!移動、リサイズの出来るコントロールを作る
http://shinshu.fm/MHz/88.44/archives/0000043418.html
単純におもろいなぁと思いまして、C#でMix-inっぽくシンプルにコントロールに実装できるように
それ用のインターフェイスおよび、その拡張メソッドをこさえてみた。
深くは煮詰めてはいないけど、とりあえずこんな感じでよいでしょう。
using System; using System.Drawing; using System.Windows.Forms; namespace ClassLibrary1 { public interface IResizeAndMove { int MinWidth { get; } int MinHeight { get; } int MaxWidth { get; } int MaxHeight { get; } int ResizeWidth { get; set; } int ResizeHeight { get; set; } } public static class IResizeAndMoveExtentions { #region メンバ private static readonly ContextMenu _cmResize = new ContextMenu(); private static readonly MenuItem _mnuResize = new MenuItem("Edit",mnuResize_Click); private static readonly MenuItem _mnuBringToFront = new MenuItem("BringToFront", mnuBringToFront_Click); private static readonly MenuItem _mnuSendToBack = new MenuItem("SendToBack", mnuSendToBack_Click); #endregion #region 拡張メソッド public static void InitializeResizeAndMove<T>(this T self) where T : System.Windows.Forms.Control, IResizeAndMove { lock (_mnuResize) { self.ContextMenu = _cmResize; if (_cmResize.MenuItems.Count > 0) return; _cmResize.MenuItems.Add(_mnuResize); _cmResize.MenuItems.Add(_mnuBringToFront); _cmResize.MenuItems.Add(_mnuSendToBack); } } #endregion #region Static Method private static void mnuResize_Click(object sender, EventArgs e) { var m = (MenuItem)sender; var c = m.GetContextMenu().SourceControl; using (var f = new IResizeAndMoveExtentions.ResizeBorderForm(c)) { f.ShowDialog(c.FindForm()); } } private static void mnuBringToFront_Click(object sender, EventArgs e) { var m = (MenuItem)sender; var c = m.GetContextMenu().SourceControl; c.BringToFront(); } private static void mnuSendToBack_Click(object sender, EventArgs e) { var m = (MenuItem)sender; var c = m.GetContextMenu().SourceControl; c.SendToBack(); } #endregion private sealed class ResizeBorderForm : System.Windows.Forms.Form { private System.ComponentModel.IContainer components = null; /// <summary> /// 使用中のリソースをすべてクリーンアップします。 /// </summary> /// <param name="disposing">マネージ リソースが破棄される場合 true、破棄されない場合は false です。</param> protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows フォーム デザイナで生成されたコード /// <summary> /// デザイナ サポートに必要なメソッドです。このメソッドの内容を /// コード エディタで変更しないでください。 /// </summary> private void InitializeComponent() { this.SuspendLayout(); this.AutoScaleBaseSize = new System.Drawing.Size(5, 12); this.BackColor = System.Drawing.Color.SkyBlue; this.ClientSize = new System.Drawing.Size(100,20); this.ControlBox = false; this.Cursor = System.Windows.Forms.Cursors.Default; this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow; this.Name = "ResizeBorderForm"; this.Opacity = 0.5; this.ShowInTaskbar = false; this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; this.ResumeLayout(false); } #endregion #region メンバ private Control _targetControl; private Point _moveAdjustPoint; private Point _resizeAdjustPoint; private bool _hasResizeEvent = false; #endregion #region コンストラクタ public ResizeBorderForm(Control target) { _targetControl = target; InitializeComponent(); var rect = new Rectangle(target.Location ,target.Size); var srect = target.Parent.RectangleToScreen(rect); this.Top = srect.Y; this.Left = srect.X; this.Width = srect.Width; this.Height = srect.Height; var tc = (IResizeAndMove)target; this.MinimumSize = new Size(tc.MinWidth, tc.MinHeight); this.MaximumSize = new Size(tc.MaxWidth, tc.MaxHeight); //リサイズ用調整値の取得 _resizeAdjustPoint.Y = this.Top; _resizeAdjustPoint.X = this.Left; this._hasResizeEvent = true; } #endregion #region オーバーライド protected override void OnMouseDown(MouseEventArgs e) { if (e.Button != MouseButtons.Left) return; _moveAdjustPoint = new Point(e.X, e.Y); this.Cursor = Cursors.SizeAll; this._hasResizeEvent = true; base.OnMouseDown(e); } protected override void OnMouseDoubleClick(MouseEventArgs e) { if (e.Button == MouseButtons.Left) { this.Close(); this._hasResizeEvent = false; return; } base.OnMouseDoubleClick(e); } protected override void OnMouseUp(MouseEventArgs e) { if (!this._hasResizeEvent) return; this.Cursor = Cursors.Default; _resizeAdjustPoint.Y = this.Top; _resizeAdjustPoint.X = this.Left; base.OnMouseUp(e); } protected override void OnMouseMove(MouseEventArgs e) { if (e.Button != MouseButtons.Left) return; var moveX = (e.X - _moveAdjustPoint.X); var moveY = (e.Y - _moveAdjustPoint.Y); this.Left += moveX; this.Top += moveY; this._targetControl.Left += moveX; this._targetControl.Top += moveY; base.OnMouseMove(e); } protected override void OnResize(EventArgs e) { var tc = (IResizeAndMove)_targetControl; base.OnResize(e); if (!this._hasResizeEvent) return; if (this.Top != _resizeAdjustPoint.Y) _targetControl.Top += (this.Top - _resizeAdjustPoint.Y); if (this.Left != _resizeAdjustPoint.X) _targetControl.Left += (this.Left - _resizeAdjustPoint.X); tc.ResizeWidth = this.Width; tc.ResizeHeight = this.Height; _resizeAdjustPoint.Y = this.Top; _resizeAdjustPoint.X = this.Left; } #endregion } } }
例えば、以下のような感じで、簡単に実装することができる。
ボタンコントロールでの実装
using System.Windows.Forms; namespace ClassLibrary1 { public partial class ButtonEx : Button,IResizeAndMove { public ButtonEx() { this.InitializeResizeAndMove(); } #region IResizeAndMove メンバ public int MinWidth { get { return 20; } } public int MinHeight { get { return 20; } } public int MaxWidth { get { return 200; } } public int MaxHeight { get { return 80; } } public int ResizeWidth { get{ return this.Width; } set { this.Width = value; } } public int ResizeHeight { get { return this.Height; } set { this.Height = value; } } #endregion } }
オーナードローなコンボボックスでの実装
using System.Drawing; using System.Windows.Forms; namespace ClassLibrary1 { public partial class ComboBoxEx : ComboBox , IResizeAndMove { public ComboBoxEx() { this.InitializeResizeAndMove(); this.DoubleBuffered = true; this.DrawMode = DrawMode.OwnerDrawFixed; } protected override void OnDrawItem(DrawItemEventArgs e) { e.DrawBackground(); var cmb = this; //項目に表示する文字列 string txt = e.Index > -1 ? cmb.Items[e.Index].ToString() : cmb.Text; Brush b = new SolidBrush(e.ForeColor); float ym = (e.Bounds.Height - e.Graphics.MeasureString(txt, cmb.Font).Height) / 2; e.Graphics.DrawString(txt, cmb.Font, b, e.Bounds.X, e.Bounds.Y + ym); b.Dispose(); e.DrawFocusRectangle(); base.OnDrawItem(e); } #region IResizeAndMove メンバ public int MinWidth {get { return 20; }} public int MinHeight{get { return 20; }} public int MaxWidth {get { return 300; }} public int MaxHeight {get { return 100; }} public int ResizeWidth { get { return this.Width; } set { this.Width = value; } } public int ResizeHeight { get { return this.ItemHeight; } set { value -= 7; if (value < this.MinHeight) value = this.MinHeight; this.ItemHeight = value; } } #endregion } }
チェックボックスでの実装
using System.Windows.Forms; namespace ClassLibrary1 { public class CheckBoxEx : CheckBox, IResizeAndMove { public CheckBoxEx() { this.InitializeResizeAndMove(); this.AutoSize = false; } #region IResizeAndMove メンバ public int MinWidth { get { return 20; } } public int MinHeight { get { return 20; } } public int MaxWidth { get { return 200; } } public int MaxHeight { get { return 80; } } public int ResizeWidth { get { return this.Width; } set { this.Width = value; } } public int ResizeHeight { get { return this.Height; } set { this.Height = value; } } #endregion } }
とりあえず、うごきますね。
実用に耐え得るようにするのであれば、もうちょい調整は必要っぽい。