diff --git a/src/Shared/HandyControlDemo_Shared/HandyControlDemo_Shared.projitems b/src/Shared/HandyControlDemo_Shared/HandyControlDemo_Shared.projitems
index ebaa04c5f..39c668e72 100644
--- a/src/Shared/HandyControlDemo_Shared/HandyControlDemo_Shared.projitems
+++ b/src/Shared/HandyControlDemo_Shared/HandyControlDemo_Shared.projitems
@@ -67,6 +67,9 @@
ConfettiCannonDemo.xaml
+
+ DragDialogDemo.xaml
+
ElementGroupDemo.xaml
@@ -555,6 +558,10 @@
MSBuild:Compile
+
+ MSBuild:Compile
+ Designer
+
Designer
MSBuild:Compile
@@ -1080,4 +1087,4 @@
-
+
\ No newline at end of file
diff --git a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.Designer.cs b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.Designer.cs
index e58f827af..c6385428a 100644
--- a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.Designer.cs
+++ b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.Designer.cs
@@ -1,4 +1,4 @@
-//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
//
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
@@ -718,6 +718,17 @@ public static string Documentation
}
}
+ ///
+ /// Looks up a localized string similar to 可拖拽对话框.
+ ///
+ public static string DragDialog
+ {
+ get
+ {
+ return ResourceManager.GetString("DragDialog", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to 在这里拖拽.
///
diff --git a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.cs.resx b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.cs.resx
index 2731d1b2b..bac1f6a98 100644
--- a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.cs.resx
+++ b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.cs.resx
@@ -810,4 +810,7 @@
ConfettiCannon
-
+
+ Přetahovatelný dialog
+
+
\ No newline at end of file
diff --git a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.en.resx b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.en.resx
index 40aed4f48..9442cfce4 100644
--- a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.en.resx
+++ b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.en.resx
@@ -810,4 +810,7 @@
ConfettiCannon
+
+ Draggable dialog
+
\ No newline at end of file
diff --git a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.es.resx b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.es.resx
index 476ee2895..ecd7c095b 100644
--- a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.es.resx
+++ b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.es.resx
@@ -810,4 +810,7 @@
ConfettiCannon
+
+ Diálogo arrastrable
+
\ No newline at end of file
diff --git a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.fa.resx b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.fa.resx
index 6cf5a5360..b6599943b 100644
--- a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.fa.resx
+++ b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.fa.resx
@@ -810,4 +810,7 @@
ConfettiCannon
+
+ کادر محاورهای قابل کشیدن
+
\ No newline at end of file
diff --git a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.fr.resx b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.fr.resx
index 76115eb01..2ab9c6bc5 100644
--- a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.fr.resx
+++ b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.fr.resx
@@ -810,4 +810,7 @@
ConfettiCannon
+
+ Boîte de dialogue déplaçable
+
\ No newline at end of file
diff --git a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.ja.resx b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.ja.resx
index f48296385..31fde49f7 100644
--- a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.ja.resx
+++ b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.ja.resx
@@ -810,4 +810,7 @@
ConfettiCannon
+
+ ドラッグ可能なダイアログ
+
\ No newline at end of file
diff --git a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.ko-KR.resx b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.ko-KR.resx
index cef1546f9..038527c42 100644
--- a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.ko-KR.resx
+++ b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.ko-KR.resx
@@ -810,4 +810,7 @@
ConfettiCannon
+
+ 드래그 가능한 대화상자
+
\ No newline at end of file
diff --git a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.pl.resx b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.pl.resx
index a417ab99c..84094923a 100644
--- a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.pl.resx
+++ b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.pl.resx
@@ -810,4 +810,7 @@
ConfettiCannon
+
+ Przeciągane okno dialogowe
+
\ No newline at end of file
diff --git a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.pt-BR.resx b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.pt-BR.resx
index d07b4b93d..26947d575 100644
--- a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.pt-BR.resx
+++ b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.pt-BR.resx
@@ -810,4 +810,7 @@
ConfettiCannon
+
+ Diálogo arrastável
+
\ No newline at end of file
diff --git a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.resx b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.resx
index 06c16e1eb..ede85366c 100644
--- a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.resx
+++ b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.resx
@@ -810,4 +810,7 @@
礼炮
+
+ 可拖拽对话框
+
\ No newline at end of file
diff --git a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.ru.resx b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.ru.resx
index 57e30dc1a..94a314235 100644
--- a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.ru.resx
+++ b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.ru.resx
@@ -810,4 +810,7 @@
ConfettiCannon
+
+ Перетаскиваемое диалоговое окно
+
\ No newline at end of file
diff --git a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.tr.resx b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.tr.resx
index 5272c4277..ae3801daa 100644
--- a/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.tr.resx
+++ b/src/Shared/HandyControlDemo_Shared/Properties/Langs/Lang.tr.resx
@@ -810,4 +810,7 @@
ConfettiCannon
+
+ Sürüklemeyle taşınabilir iletişim kutusu
+
\ No newline at end of file
diff --git a/src/Shared/HandyControlDemo_Shared/Properties/Langs/LangProvider.cs b/src/Shared/HandyControlDemo_Shared/Properties/Langs/LangProvider.cs
index d5ada5061..e5d1662af 100644
--- a/src/Shared/HandyControlDemo_Shared/Properties/Langs/LangProvider.cs
+++ b/src/Shared/HandyControlDemo_Shared/Properties/Langs/LangProvider.cs
@@ -1,4 +1,4 @@
-using System.ComponentModel;
+using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
@@ -101,6 +101,7 @@ private void UpdateLangs()
OnPropertyChanged(nameof(Doc_cn));
OnPropertyChanged(nameof(Doc_en));
OnPropertyChanged(nameof(Documentation));
+ OnPropertyChanged(nameof(DragDialog));
OnPropertyChanged(nameof(DragHere));
OnPropertyChanged(nameof(Drawer));
OnPropertyChanged(nameof(Effects));
@@ -569,7 +570,10 @@ private void UpdateLangs()
/// 查找类似 文献资料 的本地化字符串。
///
public string Documentation => Lang.Documentation;
-
+ ///
+ /// 查找类似 在这里拖拽 的本地化字符串。
+ ///
+ public string DragDialog => Lang.DragDialog;
///
/// 查找类似 在这里拖拽 的本地化字符串。
///
@@ -1733,7 +1737,10 @@ public class LangKeys
/// 查找类似 文献资料 的本地化字符串。
///
public static string Documentation = nameof(Documentation);
-
+ ///
+ /// 查找类似 可拖拽对话框 的本地化字符串。
+ ///
+ public static string DragDialog = nameof(DragDialog);
///
/// 查找类似 在这里拖拽 的本地化字符串。
///
diff --git a/src/Shared/HandyControlDemo_Shared/UserControl/Controls/DialogDemo.xaml b/src/Shared/HandyControlDemo_Shared/UserControl/Controls/DialogDemo.xaml
index e3b465079..00e08cffb 100644
--- a/src/Shared/HandyControlDemo_Shared/UserControl/Controls/DialogDemo.xaml
+++ b/src/Shared/HandyControlDemo_Shared/UserControl/Controls/DialogDemo.xaml
@@ -1,4 +1,4 @@
-
+
diff --git a/src/Shared/HandyControlDemo_Shared/UserControl/Controls/DragDialogDemo.xaml b/src/Shared/HandyControlDemo_Shared/UserControl/Controls/DragDialogDemo.xaml
new file mode 100644
index 000000000..cb02ab542
--- /dev/null
+++ b/src/Shared/HandyControlDemo_Shared/UserControl/Controls/DragDialogDemo.xaml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Shared/HandyControlDemo_Shared/UserControl/Controls/DragDialogDemo.xaml.cs b/src/Shared/HandyControlDemo_Shared/UserControl/Controls/DragDialogDemo.xaml.cs
new file mode 100644
index 000000000..8f4c87697
--- /dev/null
+++ b/src/Shared/HandyControlDemo_Shared/UserControl/Controls/DragDialogDemo.xaml.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace HandyControlDemo.UserControl;
+
+///
+/// DragDialogDemo.xaml 的交互逻辑
+///
+public partial class DragDialogDemo
+{
+ public DragDialogDemo()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/src/Shared/HandyControlDemo_Shared/ViewModel/Controls/DialogDemoViewModel.cs b/src/Shared/HandyControlDemo_Shared/ViewModel/Controls/DialogDemoViewModel.cs
index 219412c2d..4bd5e6b6b 100644
--- a/src/Shared/HandyControlDemo_Shared/ViewModel/Controls/DialogDemoViewModel.cs
+++ b/src/Shared/HandyControlDemo_Shared/ViewModel/Controls/DialogDemoViewModel.cs
@@ -1,4 +1,4 @@
-using System.Windows;
+using System.Windows;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
#if !NET40
@@ -82,4 +82,8 @@ private async Task ShowInteractiveDialog(bool withTimer)
public RelayCommand ShowWithTokenCmd => new(token => Dialog.Show(new TextDialog(), token));
public RelayCommand CloseMainWindowDialogCmd => new(Dialog.Close);
+
+ public RelayCommand DragDialogCmd => new(() => {
+ Dialog.Show(new DragDialogDemo(), MessageToken.MainWindow);
+ });
}
diff --git a/src/Shared/HandyControl_Shared/Controls/Attach/DragAdornerElement.cs b/src/Shared/HandyControl_Shared/Controls/Attach/DragAdornerElement.cs
new file mode 100644
index 000000000..e394c3314
--- /dev/null
+++ b/src/Shared/HandyControl_Shared/Controls/Attach/DragAdornerElement.cs
@@ -0,0 +1,203 @@
+using System;
+using System.Windows;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using HandyControl.Controls;
+using HandyControl.Interactivity;
+
+namespace HandyControl.Controls;
+
+public static class DragAdornerElement
+{
+ public static readonly DependencyProperty DragTargetProperty =
+ DependencyProperty.RegisterAttached("DragTarget", typeof(UIElement), typeof(DragAdornerElement), null);
+
+ public static void SetDragTarget(DependencyObject element, UIElement value) =>
+ element.SetValue(DragTargetProperty, value);
+
+ public static UIElement GetDragTarget(DependencyObject element) =>
+ (UIElement) element.GetValue(DragTargetProperty);
+
+ public static readonly DependencyProperty IsDraggableProperty =
+ DependencyProperty.RegisterAttached("IsDraggable", typeof(bool), typeof(DragAdornerElement),
+ new PropertyMetadata(false, OnIsDraggableChanged));
+
+ public static void SetIsDraggable(DependencyObject element, bool value) =>
+ element.SetValue(IsDraggableProperty, value);
+
+ private static void OnIsDraggableChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ if (d is UIElement handle)
+ {
+ ((FrameworkElement) handle).Cursor = Cursors.SizeAll;
+ handle.MouseLeftButtonDown -= OnMouseDown;
+ if ((bool) e.NewValue)
+ handle.MouseLeftButtonDown += OnMouseDown;
+ }
+ }
+
+ private static void OnMouseDown(object sender, MouseButtonEventArgs e)
+ {
+ if (sender is not UIElement handle || e.ButtonState != MouseButtonState.Pressed)
+ return;
+
+ var dragTarget = GetDragTarget(handle) ?? VisualTreeHelper.GetParent(handle) as UIElement;
+ if (dragTarget == null) return;
+
+ var adorner = GetAncestor(dragTarget);
+ if (adorner == null) return;
+
+ adorner.SizeChanged -= Adorner_SizeChanged;
+ adorner.SizeChanged += Adorner_SizeChanged;
+
+ var adornerLayer = VisualTreeHelper.GetParent(adorner) as FrameworkElement;
+ if (adornerLayer == null) return;
+
+ handle.CaptureMouse();
+
+ // Get Or Set TranslateTransform
+ TranslateTransform currentTransform;
+ if (dragTarget.RenderTransform is TranslateTransform tt)
+ {
+ currentTransform = tt;
+ }
+ else
+ {
+ currentTransform = new TranslateTransform();
+ if (dragTarget.RenderTransform == null)
+ {
+ dragTarget.RenderTransform = currentTransform;
+ }
+ else
+ {
+ // TransformGroup
+ dragTarget.RenderTransform = new TransformGroup
+ {
+ Children = { dragTarget.RenderTransform, currentTransform }
+ };
+ }
+ }
+
+ double currentX = currentTransform.X;
+ double currentY = currentTransform.Y;
+
+ // 全局坐标系位置
+ var mouseInLayer = e.GetPosition(adornerLayer);
+ // 相对坐标系位置
+ var relatePos = e.GetPosition(dragTarget);
+
+ // 通过鼠标初始位置与当前偏移量计算出偏移公式
+ var gripOffset = new Point(mouseInLayer.X - currentX, mouseInLayer.Y - currentY);
+
+ void OnMouseMove(object s, MouseEventArgs args)
+ {
+ if (args.LeftButton != MouseButtonState.Pressed || !handle.IsMouseCaptured)
+ return;
+
+ // 实时获取当前尺寸(应对布局或窗口大小变化)
+ double targetWidth = dragTarget.RenderSize.Width;
+ double targetHeight = dragTarget.RenderSize.Height;
+
+ if (targetWidth <= 1)
+ targetWidth = (dragTarget as FrameworkElement)?.ActualWidth ?? 100;
+ if (targetHeight <= 1)
+ targetHeight = (dragTarget as FrameworkElement)?.ActualHeight ?? 100;
+
+ double layerWidth = adornerLayer.ActualWidth;
+ double layerHeight = adornerLayer.ActualHeight;
+
+ if (layerWidth <= 0 || layerHeight <= 0)
+ {
+ var window = System.Windows.Window.GetWindow(adornerLayer);
+ layerWidth = window?.ActualWidth ?? SystemParameters.WorkArea.Width;
+ layerHeight = window?.ActualHeight ?? SystemParameters.WorkArea.Height;
+ }
+
+ // 全局坐标系
+ var currentMouse = args.GetPosition(adornerLayer);
+ var desiredX = currentMouse.X - gripOffset.X;
+ var desiredY = currentMouse.Y - gripOffset.Y;
+
+ // 计算允许的拖动边界
+ double minX = relatePos.X - gripOffset.X;
+ double maxX = layerWidth - (targetWidth - relatePos.X) - gripOffset.X;
+ double minY = relatePos.Y - gripOffset.Y;
+ double maxY = layerHeight - (targetHeight - relatePos.Y) - gripOffset.Y;
+
+ var clampedX = Math.Max(minX, Math.Min(maxX, desiredX));
+ var clampedY = Math.Max(minY, Math.Min(maxY, desiredY));
+
+ // 调试日志(可选,发布时可移除)
+ System.Diagnostics.Debug.WriteLine(
+ $"currentMouse=({currentMouse.X:F1},{currentMouse.Y:F1}) | " +
+ $"gripOffset=({gripOffset.X:F1},{gripOffset.Y:F1}) | " +
+ $"desiredX=({desiredX:F1},{desiredY:F1}) | " +
+ $"clampedX=({clampedX:F1},{clampedY:F1}) | " +
+ $"X:[{minX:F1}, {maxX:F1}] Y:[{minY:F1}, {maxY:F1}]"
+ );
+
+ currentTransform.X = clampedX;
+ currentTransform.Y = clampedY;
+ }
+
+ void OnMouseUp(object s, MouseButtonEventArgs args)
+ {
+ handle.ReleaseMouseCapture();
+ handle.MouseMove -= OnMouseMove;
+ handle.MouseLeftButtonUp -= OnMouseUp;
+ }
+
+ // 先解绑再绑定,防止重复注册
+ handle.MouseMove -= OnMouseMove;
+ handle.MouseLeftButtonUp -= OnMouseUp;
+ handle.MouseMove += OnMouseMove;
+ handle.MouseLeftButtonUp += OnMouseUp;
+
+ e.Handled = true;
+ }
+
+ private static void Adorner_SizeChanged(object sender, SizeChangedEventArgs e)
+ {
+ // 当 Adorner 尺寸变化时,修改拖动目标的位置以保持在边界内
+ if (sender is not AdornerContainer adorner) return;
+ // 获取dialog实例
+ if (adorner.Child is not Dialog dialog)
+ {
+ return;
+ }
+ // 获取拖动目标
+ var dragTarget = dialog.Content as FrameworkElement;
+ if (dragTarget == null) return;
+ // 获取当前Transform
+ if (dragTarget.RenderTransform is not TransformGroup transformGroup) return;
+ TranslateTransform currentTransform = null;
+ foreach (var item in transformGroup.Children)
+ {
+ currentTransform = item as TranslateTransform;
+ if (currentTransform != null)
+ {
+ break;
+ }
+ }
+ if (currentTransform == null)
+ {
+ return;
+ }
+ // 父级容器大小变化重置当前dialog偏移为0
+ currentTransform.X = 0;
+ currentTransform.Y = 0;
+ }
+
+ // 辅助方法:向上查找祖先
+ private static T GetAncestor(DependencyObject obj) where T : DependencyObject
+ {
+ while (obj != null)
+ {
+ if (obj is T result)
+ return result;
+ obj = VisualTreeHelper.GetParent(obj);
+ }
+ return null;
+ }
+}
diff --git a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems
index 43224f7b5..40c478d98 100644
--- a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems
+++ b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems
@@ -13,6 +13,7 @@
+