nev*_*ets 6 .net c# drag winforms
我正在用户拖动鼠标时模拟窗口多选矩形.为了同步我们的理解,这张图片显示了我想要模拟的效果:

现在我想在FlowLayoutPanel上模拟这个效果,里面有一些控件.
到目前为止,我设法几乎完成了效果:

我在这里做的是在主窗体的顶部放置一个不聚焦的无边框半透明(不透明度的一半)形式.为了模拟边界,我处理SizeChanged并Paint绘制边框.
但是,此解决方案有时会闪烁,因为在所有者边界无法按时清除:

我尝试通过设置DoubleBuffer为覆盖表单使用双缓冲true,并覆盖CreateParam设置WM_EX_COMPOSITED,但都不起作用.
我的问题是:如何减少这个神器?
非常感谢!
我的代码:
对于封面形式:
public partial class CoverForm : Form
{
public CoverForm()
{
InitializeComponent();
BackColor = Color.CadetBlue;
FormBorderStyle = FormBorderStyle.None;
SizeChanged += (s, e) => Invalidate();
Paint += (s, e) =>
{
e.Graphics.Clear(BackColor);
using (var pen = new Pen(Color.DodgerBlue))
{
e.Graphics.DrawRectangle(pen, 1, 1, Size.Width - 2, Size.Height - 2);
}
};
}
protected override bool ShowWithoutActivation
{
get { return true; }
}
}
Run Code Online (Sandbox Code Playgroud)
对于主要形式:
public Form1()
{
InitializeComponent();
// mainPanel is the panel that simulates the dragging effect
mainPanel.MouseDown += (s, e) =>
{
_isMouseDown = true;
_startPosition = e.Location;
coverForm.Location = mainPanel.PointToScreen(e.Location);
coverForm.Show();
};
mainPanel.MouseUp += (s, e) =>
{
_isMouseDown = false;
coverForm.Hide();
};
mainPanel.MouseMove += CoverPanelMouseMoveHandler;
DoubleBuffered = true;
}
~Form1()
{
if (coverForm != null && !coverForm.IsDisposed)
{
coverForm.Dispose();
}
}
# region Dragging Effect
private void CoverPanelMouseMoveHandler(object sender, MouseEventArgs e)
{
if (_isMouseDown)
{
_curPosition = e.Location;
// find the dragging rectangle
var rect = CreateRect(_curPosition, _startPosition);
coverForm.Size = rect.Size;
coverForm.Location = mainPanel.PointToScreen(rect.Location);
foreach (Control control in mainPanel.Controls)
{
// logic to get button backcolor changed
}
mainPanel.Invalidate(true);
}
}
Run Code Online (Sandbox Code Playgroud)
更新
我试图覆盖OnPaint并将我的绘图放在那里,但它的结果更糟:旧的涂料不会被删除:

代码I修改为封面形式:
public partial class CoverForm : Form
{
public CoverForm()
{
InitializeComponent();
BackColor = Color.CadetBlue;
FormBorderStyle = FormBorderStyle.None;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.Clear(BackColor);
using (var pen = new Pen(Color.FromArgb(255, 0, 0, 255)))
{
e.Graphics.DrawRectangle(pen, 0, 0, Size.Width - 1, Size.Height - 1);
}
}
protected override bool ShowWithoutActivation
{
get { return true; }
}
}
Run Code Online (Sandbox Code Playgroud)
更新2
实际上我面临的问题是在FlowLayoutPanel上方绘制,而不是普通的Panel.我Panel之前提出的原因是我正在为我闪烁的2层设计寻找答案.但是,由于有人通过向面板添加控件以将其绘制到所有控件之上来解决问题,我想指出这一点:向面板添加控件将是微不足道的,但FlowLayoutPanel会将新添加的控件自动对齐到下一个可用控件位置,这可能搞砸了预期的效果.
编辑:使用额外的 PictureBox 和 Bitmap 使整个事情正常工作
下面的Panel绘制了一个不闪烁的矩形:
internal sealed class MyPanel : Panel
{
private readonly PictureBox _pictureBox;
private Bitmap _bitmapContent;
private Bitmap _bitmapForeground;
private Point? _point1;
private Point? _point2;
public MyPanel()
{
DoubleBuffered = true;
_pictureBox = new PictureBox();
}
protected override void OnSizeChanged(EventArgs e)
{
if (_bitmapForeground != null) _bitmapForeground.Dispose();
_bitmapForeground = new Bitmap(Size.Width, Size.Height);
if (_bitmapContent != null) _bitmapContent.Dispose();
_bitmapContent = new Bitmap(Size.Width, Size.Height);
_pictureBox.Size = Size;
_pictureBox.Image = _bitmapForeground;
base.OnSizeChanged(e);
}
protected override void OnMouseDown(MouseEventArgs e)
{
_point1 = e.Location;
DrawToBitmap(_bitmapContent, new Rectangle(0, 0, Size.Width, Size.Height));
SetControlsVisibility(false);
Controls.Add(_pictureBox);
base.OnMouseDown(e);
}
private void SetControlsVisibility(bool visible)
{
IEnumerable<Control> ofType = Controls.OfType<Control>();
foreach (Control control in ofType)
{
control.Visible = visible;
}
}
protected override void OnMouseUp(MouseEventArgs e)
{
Controls.Remove(_pictureBox);
SetControlsVisibility(true);
_point1 = null;
_point2 = null;
Refresh();
base.OnMouseUp(e);
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (_point1 != null)
{
_point2 = e.Location;
if (_point1 != null && _point2 != null)
{
Point p1 = _point1.Value;
Point p2 = _point2.Value;
int x1 = p1.X;
int y1 = p1.Y;
int x2 = p2.X;
int y2 = p2.Y;
int xmin = Math.Min(x1, x2);
int ymin = Math.Min(y1, y2);
int xmax = Math.Max(x1, x2);
int ymax = Math.Max(y1, y2);
using (Graphics graphics = Graphics.FromImage(_bitmapForeground))
{
graphics.DrawImageUnscaled(_bitmapContent, 0, 0, _bitmapContent.Width, _bitmapContent.Height);
graphics.DrawRectangle(Pens.Red, new Rectangle(xmin, ymin, xmax - xmin, ymax - ymin));
}
_pictureBox.Refresh();
}
}
base.OnMouseMove(e);
}
}
Run Code Online (Sandbox Code Playgroud)
但是,矩形将位于控件下方,不知道为什么......