• WPF 截图控件之绘制方框与椭圆(四) 「仿微信」


    前言

    接着上周写的截图控件继续更新 绘制方框与椭圆

    1.WPF实现截屏「仿微信」
    2.WPF 实现截屏控件之移动(二)「仿微信」
    3.WPF 截图控件之伸缩(三) 「仿微信」

    正文

    有开发者在B站反馈第三篇有Issues已修复。

    实现在截图区域内绘制 方框椭圆 有两种方式
    1)可以在截图的区域内部添加一个Canvas宽高填充至区域内,在进行绘制方框或椭圆。
    2)直接在外层的Canvas中添加,这样需要判断鼠标按下的位置和移动的位置必须在已截图区域内,如超出范围也不绘制到区域外。

    本章使用了第二种方式
    此篇更新截图时隐藏当前窗口

    一、首先接着ScreenCut继续发电。

    1.1

    新增定义 画方框、椭圆、颜色选择框Popup、Popup内部Border、Border内部RadioButton的父容器

         [TemplatePart(Name = RadioButtonRectangleTemplateName, Type = typeof(RadioButton))]
      [TemplatePart(Name = RadioButtonEllipseTemplateName, Type = typeof(RadioButton))]
      [TemplatePart(Name = PopupTemplateName, Type = typeof(Popup))]
      [TemplatePart(Name = PopupBorderTemplateName, Type = typeof(Border))]
      [TemplatePart(Name = WrapPanelColorTemplateName, Type = typeof(WrapPanel))]
      
         private const string RadioButtonRectangleTemplateName = "PART_RadioButtonRectangle";
          private const string RadioButtonEllipseTemplateName = "PART_RadioButtonEllipse";
          private const string PopupTemplateName = "PART_Popup";
          private const string PopupBorderTemplateName = "PART_PopupBorder";
          private const string WrapPanelColorTemplateName = "PART_WrapPanelColor";
          private Popup _popup;
          private WrapPanel _wrapPanel;
          
          /// 
          /// 当前绘制矩形
          /// 
          private Border borderRectangle;
          /// 
          /// 绘制当前椭圆
          /// 
          private Ellipse drawEllipse;
          /// 
          /// 当前选择颜色
          /// 
          private Brush _currentBrush;
    

    1.2

    新增RadioButtonStyles为了选择方框、椭圆、颜色

    <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
       
       <ResourceDictionary.MergedDictionaries>
           <ResourceDictionary Source="Basic/ControlBasic.xaml"/>
       ResourceDictionary.MergedDictionaries>
    
       <Style x:Key="PathRadioButton" TargetType="{x:Type RadioButton}" BasedOn="{StaticResource ControlBasicStyle}">
           <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
           <Setter Property="FrameworkElement.OverridesDefaultStyle" Value="True" />
           <Setter Property="HorizontalContentAlignment" Value="Center" />
           <Setter Property="VerticalContentAlignment" Value="Center" />
           <Setter Property="BorderThickness" Value="1" />
           <Setter Property="Padding" Value="8" />
           <Setter Property="Cursor" Value="Hand"/>
           <Setter Property="Template">
               <Setter.Value>
                   <ControlTemplate TargetType="{x:Type RadioButton}">
                       <Border Background="Transparent">
                           <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                             Margin="{TemplateBinding Padding}" 
                                             VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                             x:Name="PART_ContentPresenter" Opacity=".8"/>
                       Border>
                       <ControlTemplate.Triggers>
                           <Trigger Property="IsChecked" Value="True">
                               <Setter Property="Opacity" TargetName="PART_ContentPresenter" Value="1"/>
                           Trigger>
                           <Trigger Property="IsMouseOver" Value="True">
                               <Setter Property="Opacity" TargetName="PART_ContentPresenter" Value="1"/>
                           Trigger>
                       ControlTemplate.Triggers>
                   ControlTemplate>
               Setter.Value>
           Setter>
       Style>
    
       <Style x:Key="ColorRadioButton" TargetType="{x:Type RadioButton}" BasedOn="{StaticResource ControlBasicStyle}">
           <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
           <Setter Property="FrameworkElement.OverridesDefaultStyle" Value="True" />
           <Setter Property="HorizontalContentAlignment" Value="Center" />
           <Setter Property="VerticalContentAlignment" Value="Center" />
           <Setter Property="Padding" Value="8" />
           <Setter Property="Width" Value="15"/>
           <Setter Property="Height" Value="15"/>
           <Setter Property="Cursor" Value="Hand"/>
           <Setter Property="Template">
               <Setter.Value>
                   <ControlTemplate TargetType="{x:Type RadioButton}">
                       <Border Background="{TemplateBinding Background}" 
                               BorderThickness="0"
                               x:Name="PART_Border"
                               CornerRadius="7"
                               Width="{TemplateBinding Width}"
                               Height="{TemplateBinding Height}">
                           <VisualStateManager.VisualStateGroups>
                               <VisualStateGroup x:Name="CheckStates">
                                   <VisualState x:Name="Checked">
                                       <Storyboard>
                                           <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)"
                                                    Storyboard.TargetName="PART_Ellipse">
                                               <DiscreteObjectKeyFrame KeyTime="0"
                                               Value="{x:Static Visibility.Visible}" />
                                           ObjectAnimationUsingKeyFrames>
                                       Storyboard>
                                   VisualState>
                                   <VisualState x:Name="Unchecked" />
                                   <VisualState x:Name="Indeterminate" />
                               VisualStateGroup>
                           VisualStateManager.VisualStateGroups>
                           <Ellipse x:Name="PART_Ellipse"
                                    Width="7"
                                    Height="7"
                                    Fill="{DynamicResource WhiteSolidColorBrush}"
                                    Visibility="Collapsed"/>
                       Border>
                       <ControlTemplate.Triggers>
                           <Trigger Property="IsMouseOver" Value="True">
                               <Setter Property="Opacity" Value=".8"/>
                           Trigger>
                       ControlTemplate.Triggers>
                   ControlTemplate>
               Setter.Value>
           Setter>
       Style>
    
    ResourceDictionary>
    
    
    折叠

    1.3

    ScreenCut.xaml增加代码如下

    
    <RadioButton x:Name="PART_RadioButtonRectangle" 
                                                    Style="{DynamicResource PathRadioButton}"
                                                    ToolTip="方框"
                                                   Margin="4,0">
                                          <RadioButton.Content>
                                              <Path Fill="{DynamicResource RegularTextSolidColorBrush}" 
                                            Width="18" Height="18" Stretch="Fill" 
                                            Data="{StaticResource PathRectangle}"/>
                                          RadioButton.Content>
                                      RadioButton>
                                      <RadioButton x:Name="PART_RadioButtonEllipse" 
                                                    Style="{DynamicResource PathRadioButton}"
                                                    ToolTip="椭圆"
                                                   Margin="4,0">
                                          <ToggleButton.Content>
                                              <Ellipse Width="19" Height="19"
                                                       StrokeThickness="1.5"
                                                       SnapsToDevicePixels="True"
                                                       UseLayoutRounding="True"
                                                       Stroke="{DynamicResource RegularTextSolidColorBrush}"/>
                                          ToggleButton.Content>
                                      RadioButton>
    
    <Popup x:Name="PART_Popup" 
                                     AllowsTransparency="True"
                                     Placement="Bottom"
                                     VerticalOffset="13">
                                  <Border Effect="{DynamicResource PopupShadowDepth}"
                                          Background="{DynamicResource WhiteSolidColorBrush}"
                                          Margin="10,30,10,10"
                                          CornerRadius="{Binding Path=(helpers:ControlsHelper.CornerRadius),RelativeSource={RelativeSource TemplatedParent}}"
                                          x:Name="PART_PopupBorder">
                                      <Grid>
                                          <Path Data="{StaticResource PathUpperTriangle}"
                                            Fill="{DynamicResource WhiteSolidColorBrush}" 
                                            Stretch="Uniform"
                                            Width="10" VerticalAlignment="Top"
                                            Margin="0,-8,0,0"
                                            SnapsToDevicePixels="True"
                                            UseLayoutRounding="True"/>
                                          <WrapPanel Margin="10"
                                                     VerticalAlignment="Center"
                                                     x:Name="PART_WrapPanelColor">
                                              <RadioButton Style="{DynamicResource ColorRadioButton}"
                                                           Margin="4,0" Background="Red"
                                                           IsChecked="True">
                                              RadioButton>
                                              <RadioButton Style="{DynamicResource ColorRadioButton}"
                                                           Margin="4,0" 
                                                           Background="DodgerBlue">
                                              RadioButton>
                                          WrapPanel>
                                      Grid>
                                  Border>
                              Popup>
    
    
    折叠

    二、ScreenCut.cs 增加的后台逻辑如下

    2.1 RadioButton选中方框和椭圆的切换Popup并设置ScreenCutMouseType枚举和鼠标:

        _radioButtonRectangle = GetTemplateChild(RadioButtonRectangleTemplateName) as RadioButton;
             if (_radioButtonRectangle != null)
                 _radioButtonRectangle.Click += _radioButtonRectangle_Click;
             _radioButtonEllipse = GetTemplateChild(RadioButtonEllipseTemplateName) as RadioButton;
             if (_radioButtonEllipse != null)
                 _radioButtonEllipse.Click += _radioButtonEllipse_Click;
                 private void _radioButtonRectangle_Click(object sender, RoutedEventArgs e)
         {
             RadioButtonChecked(_radioButtonRectangle, ScreenCutMouseType.DrawRectangle);
         }
         private void _radioButtonEllipse_Click(object sender, RoutedEventArgs e)
         {
             RadioButtonChecked(_radioButtonEllipse, ScreenCutMouseType.DrawEllipse);
         }
         void RadioButtonChecked(RadioButton radioButton, ScreenCutMouseType screenCutMouseTypeRadio)
         {
             if (radioButton.IsChecked == true)
             {
                 screenCutMouseType = screenCutMouseTypeRadio;
                 _border.Cursor = Cursors.Arrow;
                 if (_popup.PlacementTarget != null && _popup.IsOpen)
                     _popup.IsOpen = false;
                 _popup.PlacementTarget = radioButton;
                 _popup.IsOpen = true;
             }
             else
             {
                 if (screenCutMouseType == screenCutMouseTypeRadio)
                     Restore();
    
             }
         }
         void Restore()
         {
             _border.Cursor = Cursors.SizeAll;
             if (screenCutMouseType == ScreenCutMouseType.Default) return;
             screenCutMouseType = ScreenCutMouseType.Default;
         }
    
    折叠

    2.2 ScreenCut绘制方框和椭圆代码如下:

    void DrawMultipleControl(Point current)
            {
                if (current == pointStart) return;
               
                if (current.X > rect.BottomRight.X
                    ||
                    current.Y > rect.BottomRight.Y)
                    return;
                var drawRect = new Rect(pointStart, current);
                switch (screenCutMouseType)
                {
                    case ScreenCutMouseType.DrawRectangle:
                        if (borderRectangle == null)
                        {
                            borderRectangle = new Border()
                            {
                                BorderBrush = _currentBrush == null ? Brushes.Red : _currentBrush,
                                BorderThickness = new Thickness(3),
                                CornerRadius = new CornerRadius(3),
                            };
                            _canvas.Children.Add(borderRectangle);
                        }
                        break;
                    case ScreenCutMouseType.DrawEllipse:
                        if (drawEllipse == null)
                        {
                            drawEllipse = new Ellipse()
                            {
                                Stroke = _currentBrush == null ? Brushes.Red : _currentBrush,
                                StrokeThickness = 3,
                            };
                            _canvas.Children.Add(drawEllipse);
                        }
                        break;
                   
                }
               
                var _borderLeft = drawRect.Left - Canvas.GetLeft(_border);
               
                if (_borderLeft < 0)
                    _borderLeft = Math.Abs(_borderLeft);
                if (drawRect.Width + _borderLeft < _border.ActualWidth)
                {
                    var wLeft = Canvas.GetLeft(_border) + _border.ActualWidth;
                    var left = drawRect.Left < Canvas.GetLeft(_border) ? Canvas.GetLeft(_border) : drawRect.Left > wLeft ? wLeft : drawRect.Left;
                    if (borderRectangle != null)
                    {
                        borderRectangle.Width = drawRect.Width;
                        Canvas.SetLeft(borderRectangle, left);
                    }
                    if (drawEllipse != null)
                    {
                        drawEllipse.Width = drawRect.Width;
                        Canvas.SetLeft(drawEllipse, left);
                    }
                 
                   
                }
               
                var _borderTop = drawRect.Top - Canvas.GetTop(_border);
                if(_borderTop < 0)
                    _borderTop = Math.Abs(_borderTop);
                if (drawRect.Height + _borderTop < _border.ActualHeight)
                {
                    var hTop = Canvas.GetTop(_border) + _border.Height;
                    var top = drawRect.Top < Canvas.GetTop(_border) ? Canvas.GetTop(_border) : drawRect.Top > hTop ? hTop : drawRect.Top;
                    if (borderRectangle != null)
                    {
                        borderRectangle.Height = drawRect.Height;
                        Canvas.SetTop(borderRectangle, top);
                    }
    
                    if (drawEllipse != null)
                    {
                        drawEllipse.Height = drawRect.Height;
                        Canvas.SetTop(drawEllipse, top);
                    }
    
                }
            }
    
    折叠

    2.3 Popup跟随问题这里解决办法是先关闭再打开代码如下:

     if (_popup != null && _popup.IsOpen)
               {
                   _popup.IsOpen = false;
                   _popup.IsOpen = true;
               }
    

    2.4 ScreenCut使用方式如下:

     public partial class ScreenCutExample : UserControl
       {
           public bool IsChecked
           {
               get { return (bool)GetValue(IsCheckedProperty); }
               set { SetValue(IsCheckedProperty, value); }
           }
    
           public static readonly DependencyProperty IsCheckedProperty =
               DependencyProperty.Register("IsChecked", typeof(bool), typeof(ScreenCutExample), new PropertyMetadata(false));
    
    
           public ScreenCutExample()
           {
               InitializeComponent();
           }
    
           private void Button_Click(object sender, RoutedEventArgs e)
           {
               var screenCut = new ScreenCut();
               if (IsChecked)
               {
                   App.CurrentMainWindow.WindowState = WindowState.Minimized;
                   screenCut.Show();
                   screenCut.Activate();
               }
               else
                   screenCut.ShowDialog();
           }
       }
    
    折叠

    完整代码如下

    项目地址

    • 框架名:WPFDevelopers
    • 作者:WPFDevelopers
    • GitHub
    • Gitee
  • 相关阅读:
    CSS 什么是外边距重叠?重叠的结果是什么?
    7 月 2 日邀你来TD Hero 线上发布会
    设计LRU缓存(双向链表+哈希表)
    GMS地下水数值模拟丨GMS各模块、三维地质模型构建及与MODFLOW耦合、地下水流动数值模拟及报告编制、地下水溶质运移模型、反应性溶质运移等
    ​力扣解法汇总515-在每个树行中找最大值
    A Simple Baseline for BEV Perception Without LiDAR 论文笔记
    vue3+eleement plus日历选择季度
    TensorFlow之文本分类算法-4
    MyBatis动态SQL多表操作
    并查集:基本操作、路径压缩
  • 原文地址:https://www.cnblogs.com/yanjinhua/p/16529311.html