Вот пример того, как это можно сделать:
using System;
using System.Runtime.InteropServices;
using System.Windows.Interop;
namespace DeleteMeWPF {
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow {
public MainWindow() {
InitializeComponent();
}
protected override void OnSourceInitialized(EventArgs e) {
base.OnSourceInitialized(e);
IntPtr handle = new WindowInteropHelper(this).Handle;
HwndSource.FromHwnd(handle).AddHook(new HwndSourceHook(this.WindowProc));
}
[StructLayout(LayoutKind.Sequential)]
private struct RECT {
public int left;
public int top;
public int right;
public int bottom;
}
private const int WM_SIZING = 0x0214;
private const int WMSZ_BOTTOM = 6;
private const int WMSZ_BOTTOMLEFT = 7;
private const int WMSZ_BOTTOMRIGHT = 8;
private const int WMSZ_LEFT = 1;
private const int WMSZ_RIGHT = 2;
private const int WMSZ_TOP = 3;
private const int WMSZ_TOPLEFT = 4;
private const int WMSZ_TOPRIGHT = 5;
private const int SnappingIncrement = 100;
private const int SnappingThresholdWidth = 300;
private const int SnappingThresholdHeight = 400;
private IntPtr WindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) {
switch (msg) {
case WM_SIZING:
RECT bounds = (RECT)Marshal.PtrToStructure(lParam, typeof(RECT));
int width = bounds.right - bounds.left;
int height = bounds.bottom - bounds.top;
switch (wParam.ToInt32()) {
case WMSZ_BOTTOM:
if (height > SnappingThresholdHeight)
bounds.bottom = bounds.top + ((int)((double)height / (double)SnappingIncrement) * SnappingIncrement);
break;
case WMSZ_BOTTOMLEFT:
if (height > SnappingThresholdHeight)
bounds.bottom = bounds.top + ((int)((double)height / (double)SnappingIncrement) * SnappingIncrement);
if (width > SnappingThresholdWidth)
bounds.left = bounds.right - ((int)((double)width / (double)SnappingIncrement) * SnappingIncrement);
break;
case WMSZ_BOTTOMRIGHT:
if (height > SnappingThresholdHeight)
bounds.bottom = bounds.top + ((int)((double)height / (double)SnappingIncrement) * SnappingIncrement);
if (width > SnappingThresholdWidth)
bounds.right = bounds.left + ((int)((double)width / (double)SnappingIncrement) * SnappingIncrement);
break;
case WMSZ_LEFT:
if (width > SnappingThresholdWidth)
bounds.left = bounds.right - ((int)((double)width / (double)SnappingIncrement) * SnappingIncrement);
break;
case WMSZ_RIGHT:
if (width > SnappingThresholdWidth)
bounds.right = bounds.left + ((int)((double)width / (double)SnappingIncrement) * SnappingIncrement);
break;
case WMSZ_TOP:
if (height > SnappingThresholdHeight)
bounds.top = bounds.bottom - ((int)((double)height / (double)SnappingIncrement) * SnappingIncrement);
break;
case WMSZ_TOPLEFT:
if (width > SnappingThresholdWidth)
bounds.left = bounds.right - ((int)((double)width / (double)SnappingIncrement) * SnappingIncrement);
if (height > SnappingThresholdHeight)
bounds.top = bounds.bottom - ((int)((double)height / (double)SnappingIncrement) * SnappingIncrement);
break;
case WMSZ_TOPRIGHT:
if (width > SnappingThresholdWidth)
bounds.right = bounds.left + ((int)((double)width / (double)SnappingIncrement) * SnappingIncrement);
if (height > SnappingThresholdHeight)
bounds.top = bounds.bottom - ((int)((double)height / (double)SnappingIncrement) * SnappingIncrement);
break;
}
Marshal.StructureToPtr(bounds, lParam, false);
break;
}
return IntPtr.Zero;
}
}
}
Здесь используются приращения 100, чтобы действительно проиллюстрировать эффект «привязки».Кроме того, вы можете настроить пороговые значения привязки, чтобы гарантировать, что привязка вступает в силу только тогда, когда размер превышает заданную ширину / высоту.