cosky 发表于 2026-1-7 10:46:15

使用DevExpressWinform实现窗体对齐到网格和边缘吸附效果

本帖最后由 cosky 于 2026-3-1 18:02 编辑

先看效果:




核心代码:
在窗体构造函数中初始化:
      void InitSnapOption()
      {
            SnapOptions snapOptions1 = new SnapOptions
            {
                //当窗体移动时是否触发吸附效果
                SnapOnMoving = DefaultBoolean.True,
                //当窗体改变大小时是否触发吸附效果
                SnapOnResizing = DefaultBoolean.True,
                //当窗体移动时是否有网格吸附效果
                SnapToGrid = DefaultBoolean.False,
                //当窗体拖放到屏幕边缘时是否启用吸附效果
                SnapToScreen = DefaultBoolean.True,
                //当两个窗体靠近时是否启用吸附效果(需要两个窗体都有BehaviorManager,并且设置了SnapToSnapForms=True)
                SnapToSnapForms = DefaultBoolean.True,
                //当窗体移动时吸附的网格大小
                GridCellSize = new Size(20, 20),
                //吸附效果触发的距离(注意:如果启用了多个窗体之间吸附效果,请保持该属性一致,否则会出现不可描述的效果)
                SnapThreshold = 20
            };
            this.behaviorManager.SetBehaviors(this, new DevExpress.Utils.Behaviors.Behavior[] {
                DevExpress.Utils.Behaviors.Common.SnapWindowBehavior.Create(typeof(DevExpress.Utils.BehaviorSource.SnapWindowBehaviorSourceForForm), snapOptions1)
            });
      }

鼠标悬停在窗体区域上的效果:
void InitLabelEvents(LabelControl labelControl)
{
    labelControl.MouseEnter += LabelControl_MouseEnter;
    labelControl.MouseLeave += LabelControl_MouseLeave;
    labelControl.MouseClick += LabelControl_MouseClick;
}

private void LabelControl_MouseClick(object sender, MouseEventArgs e)
{
    var label = (sender as LabelControl);
    this.ShowChildForm<SnapForm2>(label.Size, PointToScreen(label.Location));
}
private void LabelControl_MouseLeave(object sender, EventArgs e)
{
    (sender as LabelControl).Appearance.BackColor = Color.LightCyan;
}

private void LabelControl_MouseEnter(object sender, EventArgs e)
{
    (sender as LabelControl).Appearance.BackColor = Color.Silver;
}


绘制窗口停靠矩形区域:
protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);
    Pen customPen = new Pen(Color.Silver)
    {
      DashStyle = System.Drawing.Drawing2D.DashStyle.Dash,
      DashCap = System.Drawing.Drawing2D.DashCap.Flat,
      DashPattern = new float[] { 3, 1 },
      Width = 2
    };
    using (DevExpress.Utils.Drawing.GraphicsCache cache = new DevExpress.Utils.Drawing.GraphicsCache(e.Graphics))
    {
      cache.DrawRectangle(customPen, GetLabelRect(labelControl1));
      cache.DrawRectangle(customPen, GetLabelRect(labelControl2));
      cache.DrawRectangle(customPen, GetLabelRect(labelControl3));
      cache.DrawRectangle(customPen, GetLabelRect(labelControl4));
    }
}


点击Label生成子窗体:
private void LabelControl_MouseClick(object sender, MouseEventArgs e)
{
    var label = (sender as LabelControl);
    this.ShowChildForm<SnapForm2>(label.Size, PointToScreen(label.Location));
}


ShowChildForm方法扩展:
public static (T Form, bool InstanceCreated) ShowChildForm<T>(this FormBase parentForm, Size formSize, Point formPoint) where T : FormBase
{
    var childType = typeof(T);
    var childForm = CreateInstance<T>(childType, parentForm);
    childForm.Owner = parentForm;
    childForm.StartPosition = FormStartPosition.Manual;
    childForm.Size = formSize;
    childForm.Location = formPoint;
    if (parentForm.FormTag == null)
    {
      parentForm.FormTag = new FormTags { BeforeBound = parentForm.Bounds };
      parentForm.SizeMoving += (sender) => FixedChildForm(parentForm);
      parentForm.ExitSizeMove += (sender) =>
      {
            parentForm.FormTag.BeforeBound = parentForm.Bounds;
            UpdateChildForm(parentForm);
      };
    }
    childForm.Shown += (sender, e) =>
    {
      childForm.FormTag = new FormTags()
      {
            BoundLeft = childForm.Left - parentForm.Left,
            BoundTop = childForm.Top - parentForm.Top,
            BoundRight = parentForm.Right - childForm.Right,
            BoundBottom = parentForm.Bottom - childForm.Bottom,
            ParentBound = parentForm.Bounds,
            BeforeBound = childForm.Bounds
      };
    };
    childForm.ExitSizeMove += (sender) =>
    {
      UpdateChildForm(parentForm, childForm);
    };
    childForm.Show(parentForm);
    return (childForm, true);
}


2026-1-6 更新:
可设置拖动主窗体时,子窗体跟随主窗体移动

完整方案下载:
(环境.NetFramework4.8 + DevExpress24.2)

jikefeng 发表于 2026-1-13 09:59:17

感谢楼主的热心分享
页: [1]
查看完整版本: 使用DevExpressWinform实现窗体对齐到网格和边缘吸附效果