WPF使用Thumb实现拖动和改变控件大小
  JWGppPCsR1pT 2023年11月02日 102 0

关于Thumb控件

在MSDN中文档中有这样的描述: "Represents a control that can be dragged by the user."

Thumb控件提供了一些事件用于管理拖拽操作:

  • DragStarted当用户按下鼠标左键,Thumb控件获得焦点并捕获鼠标,触发此事件。
  • DragDelta当Thumb控件获得焦点并捕获鼠标时,此事件会触发多次。
  • DragCompleted当控件失去鼠标捕获时,触发此事件

实现拖拽功能

从简单的布局开始

<Canvas>
        <Thumb 
               Canvas.Left="100" 
               Canvas.Top="50" Width="100"
               Height="100" />
    </Canvas>

运行结果:

WPF使用Thumb实现拖动和改变控件大小_xml

现在这个控件并不能拖动。

处理DragDelta事件

<Thumb 
     Canvas.Left="100" 
     Canvas.Top="50"
     Width="100"
     Height="100"
     DragDelta="Thumb_DragDelta"
     />
private void Thumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
 {
     Control c = sender as Control;
     if (c != null)
     {
         double x = Canvas.GetLeft(c);
         double y = Canvas.GetTop(c);
         x = double.IsNaN(x) ? 0 : x;
         y = double.IsNaN(y) ? 0 : y;

         Canvas.SetLeft(c, x + e.HorizontalChange);
         Canvas.SetTop(c, y + e.VerticalChange);
     }
 }

编译运行,现在这个控件可以拖动了。

拖动任意类型的控件

经过前面的操作,可以拖动Thumb控件,并且实现方式也很简单, 但是在实际工作中并没有任何作用。

<Canvas>
        <ContentControl 
            x:Name="cc"
            Canvas.Left="100" 
            Canvas.Top="50"
            Width="100"
            Height="100">
            <Grid>
                <Thumb  DragDelta="Thumb_DragDelta" 
                        DataContext="{Binding ElementName=cc}"/>
                <Ellipse Fill="Blue"/>
            </Grid>
        </ContentControl>
   </Canvas>
  • 我们用ContentControl控件包裹 Thumb 和 Ellipse 控件
  • Ellipse 表示我们要拖动的控件, Thumb 用于实现拖动操作

之前的Thumb_DragDelta函数直接从sender中获取要拖动的控件,但是现在我们要拖动的是ContentControl控件,因此处理代码要稍作改动。

private void Thumb_DragDelta(object sender, 
                             System.Windows.Controls.Primitives.DragDeltaEventArgs e)
{
    Control thb = sender as Control;
    if (thb == null)
    {
        return;
    }
    Control c = thb.DataContext as Control;
    if (c != null)
    {
        double x = Canvas.GetLeft(c);
        double y = Canvas.GetTop(c);
        x = double.IsNaN(x) ? 0 : x;
        y = double.IsNaN(y) ? 0 : y;

        Canvas.SetLeft(c, x + e.HorizontalChange);
        Canvas.SetTop(c, y + e.VerticalChange);
    }
}

从sender的DataContext中获取要拖动的控件,然后设置其位置..., DataContext我们在XAML代码中做了绑定所以可以正确获取到内容DataContext="{Binding ElementName=cc}"

编译运行:

WPF使用Thumb实现拖动和改变控件大小_Math_02

IsHitTestVisible

运行前面的程序,发现当鼠标在蓝色椭圆上时,并不能拖动控件。

<Ellipse Fill="Blue" IsHitTestVisible="False"/>

因为Ellipse阻断了鼠标事件,导致鼠标事件没能传Thumb处理.IsHitTestVisible="False"可以让控件不处理鼠标事件。

隐藏Thumb控件

显示Thumb控件,显然不是我们想要的, 第一想法是将Thumb的Background设置为透明

<Thumb  DragDelta="Thumb_DragDelta" 
         Background="Transparent"
         DataContext="{Binding ElementName=cc}"/>

但好像并没有什么用...经过测试Opacity="0"可以工作

<Thumb  DragDelta="Thumb_DragDelta" 
        Opacity="0"
        DataContext="{Binding ElementName=cc}"/>

使用模板隐藏Thumb控件

<Thumb  DragDelta="Thumb_DragDelta" 
        DataContext="{Binding ElementName=cc}">
    <Thumb.Template>
        <ControlTemplate>
            <Rectangle Fill="Transparent"></Rectangle>
        </ControlTemplate>
    </Thumb.Template>
</Thumb>

设置Opacity可以隐藏Thumb控件,但是更推荐这种用法。

实现Resize功能

Resize比较复杂,但处理方式和拖拽类似。

<Grid DataContext="{Binding ElementName=cc}">
     
     <Thumb  DragDelta="Thumb_DragDelta" Cursor="SizeAll">
        <Thumb.Template>
            <ControlTemplate>
                <Rectangle Fill="Transparent"></Rectangle>
            </ControlTemplate>
        </Thumb.Template>
    </Thumb>
     
     <Thumb Height="3" Cursor="SizeNS" Margin="0 -4 0 0"
           DragDelta="Thumb_DragDelta_Resize"
           VerticalAlignment="Top" HorizontalAlignment="Stretch"/>
    <Thumb Width="3" Cursor="SizeWE" Margin="-4 0 0 0"
           DragDelta="Thumb_DragDelta_Resize"
           VerticalAlignment="Stretch" HorizontalAlignment="Left"/>
    <Thumb Width="3" Cursor="SizeWE" Margin="0 0 -4 0"
           DragDelta="Thumb_DragDelta_Resize"
           VerticalAlignment="Stretch" HorizontalAlignment="Right"/>
    <Thumb Height="3" Cursor="SizeNS" Margin="0 0 0 -4"
           DragDelta="Thumb_DragDelta_Resize"
           VerticalAlignment="Bottom" HorizontalAlignment="Stretch"/>
    <Thumb Width="7" Height="7" Cursor="SizeNWSE" Margin="-6 -6 0 0"
           DragDelta="Thumb_DragDelta_Resize"
           VerticalAlignment="Top" HorizontalAlignment="Left"/>
    <Thumb Width="7" Height="7" Cursor="SizeNESW" Margin="0 -6 -6 0"
           DragDelta="Thumb_DragDelta_Resize"
           VerticalAlignment="Top" HorizontalAlignment="Right"/>
    <Thumb Width="7" Height="7" Cursor="SizeNESW" Margin="-6 0 0 -6"
           DragDelta="Thumb_DragDelta_Resize"
           VerticalAlignment="Bottom" HorizontalAlignment="Left"/>
    <Thumb Width="7" Height="7" Cursor="SizeNWSE" Margin="0 0 -6 -6"
           DragDelta="Thumb_DragDelta_Resize"
           VerticalAlignment="Bottom" HorizontalAlignment="Right"/>
     
    <Ellipse Fill="Blue" IsHitTestVisible="False"/>
 </Grid>

用8个Thumb围住Ellipse,我们把DataContext="{Binding ElementName=cc}"移到了Grid控件上,因为DataContext的继承性,不用每个Thumb都写一遍。

private void Thumb_DragDelta_Resize(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
{
    Control thumb = sender as Control;
    Control designerItem = thumb.DataContext as Control;

    if (designerItem != null)
    {
        double deltaVertical, deltaHorizontal;

        switch (thumb.VerticalAlignment)
        {
            case VerticalAlignment.Bottom:
                deltaVertical = Math.Min(-e.VerticalChange, designerItem.ActualHeight - designerItem.MinHeight);
                designerItem.Height -= deltaVertical;
                break;
            case VerticalAlignment.Top:
                deltaVertical = Math.Min(e.VerticalChange, designerItem.ActualHeight - designerItem.MinHeight);
                Canvas.SetTop(designerItem, Canvas.GetTop(designerItem) + deltaVertical);
                designerItem.Height -= deltaVertical;
                break;
            default:
                break;
        }
        switch (thumb.HorizontalAlignment)
        {
            case HorizontalAlignment.Left:
                deltaHorizontal = Math.Min(e.HorizontalChange, designerItem.ActualWidth - designerItem.MinWidth);
                Canvas.SetLeft(designerItem, Canvas.GetLeft(designerItem) + deltaHorizontal);
                designerItem.Width -= deltaHorizontal;
                break;
            case HorizontalAlignment.Right:
                deltaHorizontal = Math.Min(-e.HorizontalChange, designerItem.ActualWidth - designerItem.MinWidth);
                designerItem.Width -= deltaHorizontal;
                break;
            default:
                break;
        }
    }
    e.Handled = true;
}

Resize的事件处理函数中,根据thumb.HorizontalAlignment和thumb.VerticalAlignment设置控件的位置和大小,四个角上的Thumb两个属性都有设置,两个分支都会进入。

编译运行,结果如下:

WPF使用Thumb实现拖动和改变控件大小_控件_03

模板化

功能虽然实现了,但是如果每个控件都写那么多代码也态麻烦了. WPF的模板可以简化代码, xaml 完整代码如下:

<Window x:Class="MoveAndResizeAble.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:MoveAndResizeAble"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <ControlTemplate x:Key="MoveAndResizeTemplate" TargetType="ContentControl">
            <Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}">
                <Thumb  DragDelta="Thumb_DragDelta" 
                        Cursor="SizeAll">
                    <Thumb.Template>
                        <ControlTemplate>
                            <Rectangle Fill="Transparent"></Rectangle>
                        </ControlTemplate>
                    </Thumb.Template>
                </Thumb>

                <Thumb Height="3" Cursor="SizeNS" Margin="0 -4 0 0"
                       DragDelta="Thumb_DragDelta_Resize" 
                       VerticalAlignment="Top" HorizontalAlignment="Stretch"/>
                <Thumb Width="3" Cursor="SizeWE" Margin="-4 0 0 0" 
                       DragDelta="Thumb_DragDelta_Resize"
                       VerticalAlignment="Stretch" HorizontalAlignment="Left"/>
                <Thumb Width="3" Cursor="SizeWE" Margin="0 0 -4 0"
                       DragDelta="Thumb_DragDelta_Resize"
                       VerticalAlignment="Stretch" HorizontalAlignment="Right"/>
                <Thumb Height="3" Cursor="SizeNS" Margin="0 0 0 -4"
                       DragDelta="Thumb_DragDelta_Resize"
                       VerticalAlignment="Bottom" HorizontalAlignment="Stretch"/>
                <Thumb Width="7" Height="7" Cursor="SizeNWSE" Margin="-6 -6 0 0"
                       DragDelta="Thumb_DragDelta_Resize"
                       VerticalAlignment="Top" HorizontalAlignment="Left"/>
                <Thumb Width="7" Height="7" Cursor="SizeNESW" Margin="0 -6 -6 0"
                       DragDelta="Thumb_DragDelta_Resize"
                       VerticalAlignment="Top" HorizontalAlignment="Right"/>
                <Thumb Width="7" Height="7" Cursor="SizeNESW" Margin="-6 0 0 -6" 
                       DragDelta="Thumb_DragDelta_Resize"
                       VerticalAlignment="Bottom" HorizontalAlignment="Left"/>
                <Thumb Width="7" Height="7" Cursor="SizeNWSE" Margin="0 0 -6 -6" 
                       DragDelta="Thumb_DragDelta_Resize"
                       VerticalAlignment="Bottom" HorizontalAlignment="Right"/>
                <ContentPresenter/>
            </Grid>
        </ControlTemplate>
    </Window.Resources>
    <Canvas>
        <ContentControl 
            Canvas.Left="200" 
            Canvas.Top="50"
            Width="100"
            Height="100"
            Template="{StaticResource MoveAndResizeTemplate }"
            >
            <Ellipse Fill="Blue" IsHitTestVisible="False"/>
        </ContentControl>

        <ContentControl 
             Canvas.Left="350" 
             Canvas.Top="150"
             Width="100"
             Height="100"
             Template="{StaticResource MoveAndResizeTemplate }">
            <Button Margin="2" Content="TestButton" IsHitTestVisible="False"/>
        </ContentControl>
    </Canvas>
</Window>

封装成控件

MoveResizeItem.xaml

<ContentControl x:Class="MoveResizeItem.MoveResizeItem"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:MoveResizeItem"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <ContentControl.Template>
        <ControlTemplate TargetType="ContentControl">
            <Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}">
                <Thumb  DragDelta="Thumb_DragDelta" 
                Cursor="SizeAll">
                    <Thumb.Template>
                        <ControlTemplate>
                            <Rectangle Fill="Transparent"></Rectangle>
                        </ControlTemplate>
                    </Thumb.Template>
                </Thumb>

                <Thumb Height="3" Cursor="SizeNS" Margin="0 -4 0 0"
               DragDelta="Thumb_DragDelta_Resize" 
               VerticalAlignment="Top" HorizontalAlignment="Stretch"/>
                <Thumb Width="3" Cursor="SizeWE" Margin="-4 0 0 0" 
               DragDelta="Thumb_DragDelta_Resize"
               VerticalAlignment="Stretch" HorizontalAlignment="Left"/>
                <Thumb Width="3" Cursor="SizeWE" Margin="0 0 -4 0"
               DragDelta="Thumb_DragDelta_Resize"
               VerticalAlignment="Stretch" HorizontalAlignment="Right"/>
                <Thumb Height="3" Cursor="SizeNS" Margin="0 0 0 -4"
               DragDelta="Thumb_DragDelta_Resize"
               VerticalAlignment="Bottom" HorizontalAlignment="Stretch"/>
                <Thumb Width="7" Height="7" Cursor="SizeNWSE" Margin="-6 -6 0 0"
               DragDelta="Thumb_DragDelta_Resize"
               VerticalAlignment="Top" HorizontalAlignment="Left"/>
                <Thumb Width="7" Height="7" Cursor="SizeNESW" Margin="0 -6 -6 0"
               DragDelta="Thumb_DragDelta_Resize"
               VerticalAlignment="Top" HorizontalAlignment="Right"/>
                <Thumb Width="7" Height="7" Cursor="SizeNESW" Margin="-6 0 0 -6" 
               DragDelta="Thumb_DragDelta_Resize"
               VerticalAlignment="Bottom" HorizontalAlignment="Left"/>
                <Thumb Width="7" Height="7" Cursor="SizeNWSE" Margin="0 0 -6 -6" 
               DragDelta="Thumb_DragDelta_Resize"
               VerticalAlignment="Bottom" HorizontalAlignment="Right"/>
                <ContentPresenter/>
            </Grid>
        </ControlTemplate>
    </ContentControl.Template>
</ContentControl>

MoveResizeItem.xaml.cs

using System;
using System.Windows;
using System.Windows.Controls;

namespace MoveResizeItem
{
    /// <summary>
    /// UserControl1.xaml 的交互逻辑
    /// </summary>
    public partial class MoveResizeItem : ContentControl
    {
        public MoveResizeItem()
        {
            InitializeComponent();
        }
        private void Thumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
        {
            Control thb = sender as Control;
            if (thb == null)
            {
                return;
            }
            Control c = thb.DataContext as Control;
            if (c != null)
            {
                double x = Canvas.GetLeft(c);
                double y = Canvas.GetTop(c);
                x = double.IsNaN(x) ? 0 : x;
                y = double.IsNaN(y) ? 0 : y;

                Canvas.SetLeft(c, x + e.HorizontalChange);
                Canvas.SetTop(c, y + e.VerticalChange);
            }
        }
        private void Thumb_DragDelta_Resize(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
        {
            Control thumb = sender as Control;
            Control designerItem = thumb.DataContext as Control;

            if (designerItem != null)
            {
                double deltaVertical, deltaHorizontal;

                switch (thumb.VerticalAlignment)
                {
                    case VerticalAlignment.Bottom:
                        deltaVertical = Math.Min(-e.VerticalChange, designerItem.ActualHeight - designerItem.MinHeight);
                        designerItem.Height -= deltaVertical;
                        break;
                    case VerticalAlignment.Top:
                        deltaVertical = Math.Min(e.VerticalChange, designerItem.ActualHeight - designerItem.MinHeight);
                        Canvas.SetTop(designerItem, Canvas.GetTop(designerItem) + deltaVertical);
                        designerItem.Height -= deltaVertical;
                        break;
                    default:
                        break;
                }
                switch (thumb.HorizontalAlignment)
                {
                    case HorizontalAlignment.Left:
                        deltaHorizontal = Math.Min(e.HorizontalChange, designerItem.ActualWidth - designerItem.MinWidth);
                        Canvas.SetLeft(designerItem, Canvas.GetLeft(designerItem) + deltaHorizontal);
                        designerItem.Width -= deltaHorizontal;
                        break;
                    case HorizontalAlignment.Right:
                        deltaHorizontal = Math.Min(-e.HorizontalChange, designerItem.ActualWidth - designerItem.MinWidth);
                        designerItem.Width -= deltaHorizontal;
                        break;
                    default:
                        break;
                }
            }
            e.Handled = true;
        }
    }
}

步骤:

  1. 新建WPF用户控件
  2. 把基改成ContentControl
  3. 之后所有的代码都是都写过复制粘贴即可

代码下载

码云: https://gitee.com/luan-it/move-resize-able.git


【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  0piCg03t9xej   2023年12月23日   105   0   0 mavenxmlJavaJavamavenxml
  1BnnW8rtw7M9   2023年12月22日   120   0   0 算法i++i++mathMath算法
JWGppPCsR1pT