首页 > 其他分享 >WPF-利用装饰器实现空间的自由拖动

WPF-利用装饰器实现空间的自由拖动

时间:2023-08-25 13:44:05浏览次数:36  
标签:控件 拖动 Text tempMargin WPF Border 装饰 adornerLayer

  在项目中经常会遇到类似如下要求的需求,创建允许自由拖动的控件,这样的需求可以使用WPF的装饰器Adorner来实现。

 

一、什么是装饰器?

装饰器是一种特殊类型的FrameworkElement,装饰器始终呈现在被装饰元素的顶部,用于向用户提供可视化提示。装饰器可以在不改变原有控件结构的基础上,将功能点增加到元素中或元素上提供视觉效果等,如WPF的光标效果,焦点效果等都是通过装饰器来实现的。 装饰器是一个始终位于装饰元素或装饰元素集合顶部的呈现图层,其呈现独立与它所绑定的UIElement,WPF中的装饰器是在一个单独的曾AnornerLayer上进行绘制的,该层位于普通控件元素之上,而且允许多个AdornerLayer进行叠加,当加入AdornerLayer层后,Adorner会默认使用其所装饰元素的左上角作为原点进行定位。
  • Adorner 是一个抽象类,所有装饰器的实现都需要继承此类,比如ThumbBorderAdorner
  • AdornerLayer 一个类,表示一个或多个装饰元素的装饰器呈现层
    • 利用AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(userControl)函数,来获取指定控件是否有装饰器布局层
    • 利用Adorner[] adorners = adornerLayer.GetAdorners(userControl);,来查看当前控件的装饰器个数
  • AdornerDecorator 一个类,为可视化树中的子元素提供AdornerLayer
0

二、装饰器的使用场景

  • 为现有的元素添加额外的装饰,如为Border添加8个装饰矩形
 

三、如何创建自定义的装饰器?

  • 创建一个类,继承自Adorner类
  • 重写此类中需要的函数
    • OnRender(DrawingContext drawingContext) 在派生类中重写,参与由布局系统控制的呈现操作,调用此方法时,不直接使用此元素的呈现指令,而是将其保留供布局和绘制在以后异步使用,可以使用drawingContext 来绘制各种形状以及图形。
    • ArrangeOverride() 为FrameworkElement派生类定位子元素并确定大小,在其中调用Arrange()函数,来定位子元素
    • GetVisualChild() //获取第几个Thumb控件,在构造时使用
  • 简单的装饰可以重写OnRender()函数,在其中绘制所需要的装饰,参照BorderAdorner
  • 复杂一些的如需要可以定义VisualCollection的集合来存放装饰器,重写ArrangeOverride和GetVisualChild函数来实现,参照ThumbBorderAdorner
 

四、给控件使用自定义的Adorner

  • 添加Adorner
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(userControl);
if (adornerLayer != null)
{
    Adorner[] adorners = adornerLayer.GetAdorners(userControl);
    if (adorners == null || adorners.Count() == 0)
    {
        adornerLayer.Add(new ThumbBorderAdorner(userControl)
        {
            DragCompletedAction = ThumbBorderAdornerDragCompletedActionFunc
        });
    }
}

 

  • 移除 Adorner
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(userControl);
if (adornerLayer != null)
{
    Adorner[] adorners = adornerLayer.GetAdorners(userControl);
    if (adorners != null && adorners.Count() > 0)
        adornerLayer.Remove(adorners[0]);
}

 

五、处理拖拽Thumb时,导致控件范围超出父级容器的情况

  • 即在添加装饰器的控件中添加UserControl_SizeChanged()来处理控件大小和和未知变化
private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e)
{
    int tempMargin = 1;  //MarkRectUserControl控件始终在父级容器的1个像素以内

    double left = Canvas.GetLeft(this);
    if (left < tempMargin)
    {
        left = tempMargin;
        Canvas.SetLeft(this, left);
    }

    double top = Canvas.GetTop(this);
    if (top < tempMargin)
    {
        top = tempMargin;
        Canvas.SetTop(this, top);
    }

    if (left + this.ActualWidth > canvasParent.ActualWidth - tempMargin)
        this.Width = canvasParent.ActualWidth - tempMargin - left;

    if (top + this.ActualHeight > canvasParent.ActualHeight - tempMargin)
        this.Height = canvasParent.ActualHeight - tempMargin - top;

}

 

六、测试使用

 

<UserControl x:Class="BlogDemo.Views.AdornerTestUserControl"
             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:BlogDemo.Views"
             mc:Ignorable="d" 
             Background="White"
             d:DesignHeight="450" d:DesignWidth="800"
             FontSize="20"
             Foreground="Blue"
             Loaded="UserControl_Loaded"
             >
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        
        <TextBlock Text="请拖拽Thumb控件来改变,控件大小。" HorizontalAlignment="Center" Margin="10"/>

        <Canvas x:Name="Canvas_Main" Grid.Row="1">
            <Border x:Name="Border_Text" Canvas.Left="200" Canvas.Top="100" Width="200" Height="100" BorderThickness="2" BorderBrush="Green" SizeChanged="Border_Text_SizeChanged"/>
        </Canvas>
    </Grid>
</UserControl>
/// <summary>
/// AdornerTestUserControl.xaml 的交互逻辑
/// </summary>
public partial class AdornerTestUserControl : UserControl
{
    public AdornerTestUserControl()
    {
        InitializeComponent();
    }

    private void UserControl_Loaded(object sender, RoutedEventArgs e)
    {
        AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(Border_Text);
        if (adornerLayer != null)
        {
            Adorner[] adorners = adornerLayer.GetAdorners(Border_Text);
            if (adorners == null || adorners.Count() == 0)
            {
                adornerLayer.Add(new ThumbBorderAdorner(Border_Text));
            }
        }
    }

    private void Border_Text_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        int tempMargin = 100; 

        double left = Canvas.GetLeft(Border_Text);
        if (left < tempMargin)
        {
            left = tempMargin;
            Canvas.SetLeft(Border_Text, left);
        }

        double top = Canvas.GetTop(Border_Text);
        if (top < tempMargin)
        {
            top = tempMargin;
            Canvas.SetTop(Border_Text, top);
        }

        if (Border_Text.ActualWidth < tempMargin)
            Border_Text.Width = tempMargin;

        if (Border_Text.ActualHeight < tempMargin)
            Border_Text.Height = tempMargin;

        if (left + Border_Text.ActualWidth > Canvas_Main.ActualWidth - tempMargin)
            Border_Text.Width = Canvas_Main.ActualWidth - tempMargin - left;

        if (top + Border_Text.ActualHeight > Canvas_Main.ActualHeight - tempMargin)
            Border_Text.Height = Canvas_Main.ActualHeight - tempMargin - top;
    }
}

 

 

源码地址:https://gitee.com/LiuShuiRuoBing/code_blog

 

 

标签:控件,拖动,Text,tempMargin,WPF,Border,装饰,adornerLayer
From: https://www.cnblogs.com/liushuiruobing/p/17656703.html

相关文章

  • DevExpress WPF知识点
    1、表格右键菜单定位到行privatevoidTableView_ContextMenuOpening(objectsender,ContextMenuEventArgse){vartableView=(senderasTableView);varhitInfo=tableView.CalcHitInfo(Mouse.GetPosition(tableView));if(!hitInfo.InRow||tableVie......
  • WPF PasswordBox控件的使用
    在做登陆框的时候使用到PasswordBox,PasswordBox并不能像TextBox一样通过Binding就可以实现MVVM,需要用到依赖属性。 LoginView文件的代码:<StackPanelGrid.Row="0"Orientation="Horizontal"Margin="5"><TextBlockText="Username:"Width=&qu......
  • JS 拖动DIV边框改变其大小
    效果如下图所示:详细代码如下:<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>Title</title><style>body,html{width:100%;heig......
  • WPF 触发器Trigger
    Trigger:当某些条件满足时会触发一个行为。一、触发器的类型数据变化触发型:Trigger/DataTrigger多条件触发型:MultiTrigger/MultiDataTrigger事件触发型:EventTrigger二、TriggerTrigger:Property用来指明关注目标控件的哪个属性,Value则是触发条件。Setter:一旦触发条件被......
  • WPF输入框验证
    WPF使用ValidationRule进行数据绑定验证1.xaml代码如下:<Windowx:Class="WpfApp1.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"......
  • 图片预览组件 (放大 缩小 旋转 鼠标拖动)
    效果图其中的图片初始化不需要如果需要可自行修改一下**完整代码如下**点击查看代码<template><transitionname="zoom"><divclass="previewImage_wrapper"ref="previewImage_wrapper"@wheel="handleScroll"><divclass=&......
  • 福建学校草坪装饰镜面不锈钢风车雕塑厂家报价
    福建学校草坪装饰镜面不锈钢风车雕塑厂家报价不锈钢风车雕塑不仅是风景,更是精神象征和图腾。正如欧洲流传的一句话:上帝创造了人,荷兰风车创造了陆地。风车象征着荷兰的民族文化,人们对天空的热爱,童话般的幸福。风车在西班牙随处可见。风车虽然失去了原有的功效,但却象征着西班牙农业的......
  • 装饰电镀添加剂行业市场调研及发展趋势报告2023-2029
    2023-2029全球装饰电镀添加剂行业调研及趋势分析报告2022年全球装饰电镀添加剂市场规模约亿元,2018-2022年年复合增长率CAGR约为%,预计未来将持续保持平稳增长的态势,到2029年市场规模将接近亿元,未来六年CAGR为%。从核心市场看,中国装饰电镀添加剂市场占据全球约%的市场份额,为全......
  • DevExpress WPF HeatMap组件,一个高度可自定义的热图控件!
    像所有DevExpress UI组件一样,HeatMap组件针对速度进行了优化,包括数十个自定义设置和高级API,因此用户可以快速将美观的数据可视化集成到下一个WPF应用程序中。P.S:DevExpressWPF拥有120+个控件和库,将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpressWPF能创......
  • WinForm窗口拖动
    privatePointmypoint;privatevoidpanel1_MouseDown(objectsender,MouseEventArgse){mypoint=newPoint(-e.X,-e.Y);}privatevoidpanel1_MouseMove(objectsender,MouseEventArgse){......