Skip to content

Instantly share code, notes, and snippets.

@overing
Created April 22, 2020 20:46
Show Gist options
  • Save overing/8767255061e8cd4d19fa04d6eac6f482 to your computer and use it in GitHub Desktop.
Save overing/8767255061e8cd4d19fa04d6eac6f482 to your computer and use it in GitHub Desktop.
WinForms Task Anime Demo
using System;
using System.Drawing;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WinFormTaskAnimeDemo
{
public sealed class MainForm : Form
{
Control DraggingControl;
Point DraggingStartLocation;
Task MoveBackAnime;
[STAThread]
static void Main()
{
Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
public MainForm()
{
var pictureBox1 = new PictureBox
{
Image = Icon.ToBitmap(),
Size = new Size(128, 128),
SizeMode = PictureBoxSizeMode.StretchImage,
BackColor = Color.LightSkyBlue,
};
pictureBox1.MouseDown += Control_MouseDown;
pictureBox1.MouseMove += Control_MouseMove;
pictureBox1.MouseUp += Control_MouseUp;
var pictureBox2 = new PictureBox
{
Image = Icon.ToBitmap(),
Size = new Size(128, 128),
SizeMode = PictureBoxSizeMode.StretchImage,
BackColor = Color.LightSeaGreen,
Location = pictureBox1.Location + pictureBox1.Size,
};
pictureBox2.MouseDown += Control_MouseDown;
pictureBox2.MouseMove += Control_MouseMove;
pictureBox2.MouseUp += Control_MouseUp;
var tip = new ToolTip();
tip.SetToolTip(pictureBox1, "Drag me !");
tip.SetToolTip(pictureBox2, "Drag me !");
var panel = new Panel
{
Dock = DockStyle.Fill,
BackColor = Color.LightGray,
};
panel.Controls.Add(pictureBox1);
panel.Controls.Add(pictureBox2);
panel.Resize += Panel_Resize;
SuspendLayout();
Text = "WinForms Task Anime Demo";
Controls.Add(panel);
ClientSize = new Size(640, 480);
ResumeLayout(false);
}
void Control_MouseDown(object sender, MouseEventArgs e)
{
// 如果有動畫而且還沒跑完就不處理事件
if (MoveBackAnime != null && !MoveBackAnime.IsCompleted) return;
// 把觸發事件的控制項跟滑鼠位置記錄下來
DraggingControl = sender as Control;
DraggingStartLocation = e.Location;
// 把最新被點的移到最前面顯示
DraggingControl.Parent.Controls.SetChildIndex(DraggingControl, 0);
}
void Control_MouseMove(object sender, MouseEventArgs e)
{
// 如果有動畫而且還沒跑完就不處理事件
if (MoveBackAnime != null && !MoveBackAnime.IsCompleted) return;
// 如果觸發事件的控制項不是原先觸發 MouseDown 的控制項就不處裡事件
if (sender != DraggingControl) return;
// 算出滑鼠的偏移量
var mouseOffset = e.Location.Subtract(DraggingStartLocation);
// append 給控制項
DraggingControl.Location = DraggingControl.Location.Add(mouseOffset);
}
void Control_MouseUp(object sender, MouseEventArgs e)
{
// 如果有動畫而且還沒跑完就不處理事件
if (MoveBackAnime != null && !MoveBackAnime.IsCompleted) return;
// 如果觸發事件的控制項不是原先觸發 MouseDown 的控制項就不處裡事件
if (sender != DraggingControl) return;
// 如果控制項已經移出父控制項範圍就啟動動畫
if (!DraggingControl.IsInsideParent())
MoveBackAnime = DraggingControl.MoveBackInsideParentAsync();
// 清除觸發事件的控制項
DraggingControl = null;
}
void Panel_Resize(object sender, EventArgs e)
{
if (MoveBackAnime != null && !MoveBackAnime.IsCompleted) return;
var control = (sender as Control);
if (control == null) return;
var animations = control.Controls
.OfType<Control>()
.Where(c => !c.IsInsideParent())
.Select(c => c.MoveBackInsideParentAsync())
.ToArray();
MoveBackAnime = Task.WhenAll(animations);
}
}
static class Extensions
{
public static Point Add(this Point p1, Point p2) => new Point(p1.X + p2.X, p1.Y + p2.Y);
public static Point Subtract(this Point p1, Point p2) => new Point(p1.X - p2.X, p1.Y - p2.Y);
/// <summary>
/// 判斷控制項的螢幕範圍是否在父控制項的螢幕範圍內
/// </summary>
public static bool IsInsideParent(this Control control)
{
var parent = control.Parent;
if (parent == null) return false;
var parentBounds = parent.RectangleToScreen(Rectangle.Empty);
var controlBounds = control.RectangleToScreen(Rectangle.Empty);
return parentBounds.Contains(controlBounds);
}
/// <summary>
/// 將控制項移回父控制項的範圍內
/// </summary>
public static async Task MoveBackInsideParentAsync(this Control control)
{
var parent = control.Parent;
if (parent == null) return;
while (!control.IsInsideParent())
{
var offset = new Point();
if (parent.Width > control.Width)
{
if (control.Left < 0) offset.X += 1;
if (control.Right > parent.Width) offset.X -= 1;
}
else
offset.X = -control.Left;
if (parent.Height > control.Height)
{
if (control.Top < 0) offset.Y += 1;
if (control.Bottom > parent.Height) offset.Y -= 1;
}
else
offset.Y = -control.Top;
if (offset == Point.Empty)
return;
control.Location = control.Location.Add(offset);
await Task.Yield();
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment