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.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 @@ +