首页 > 其他分享 >WPF 截图控件之绘制方框与椭圆(四) 「仿微信」

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

时间:2022-10-06 23:01:16浏览次数:81  
标签:控件 Canvas null private drawRect WPF 仿微信 border ScreenCut


前言

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

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

正文

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

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

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

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

01

代码如下

一、首先接着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;

/// <summary>
/// 当前绘制矩形
/// </summary>
private Border borderRectangle;
/// <summary>
/// 绘制当前椭圆
/// </summary>
private Ellipse drawEllipse;
/// <summary>
/// 当前选择颜色
/// </summary>
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();
}
}

由于代码太多请点击下方链接查看完整代码如下

  • ScreenCut GitHub[1]
  • ScreenCut.xaml Gitee[2]
  • ScreenCut Gitee[3]
  • ScreenCut.xaml Gitee[4]
     

02

效果预览

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

WPF开发者

,赞15

项目地址

  • 框架名:WPFDevelopers
  • 作者:WPFDevelopers
  • GitHub[5]
  • Gitee[6]

参考资料

[1]

ScreenCut GitHub: ​https://github.com/WPFDevelopersOrg/WPFDevelopers/blob/master/src/WPFDevelopers/Controls/ScreenCut/ScreenCut.cs​

[2]

ScreenCut.xaml Gitee: ​https://github.com/WPFDevelopersOrg/WPFDevelopers/blob/master/src/WPFDevelopers/Themes/ScreenCut.xaml​

[3]

ScreenCut Gitee: ​https://gitee.com/WPFDevelopersOrg/WPFDevelopers/blob/master/src/WPFDevelopers/Controls/ScreenCut/ScreenCut.cs​

[4]

ScreenCut.xaml Gitee: ​https://gitee.com/WPFDevelopersOrg/WPFDevelopers/blob/master/src/WPFDevelopers/Themes/ScreenCut.xaml​

[5]

GitHub:: ​https://github.com/WPFDevelopersOrg​

[6]

Gitee:: ​https://gitee.com/WPFDevelopersOrg​

标签:控件,Canvas,null,private,drawRect,WPF,仿微信,border,ScreenCut
From: https://blog.51cto.com/biyusr/5734149

相关文章

  • Android GridView控件自定义
    虽然​​Android​​​已自带了GridView,但是,却不够灵活,同时也不能自由添加控件,因此,本人通过需要进一步封装,来实现​​Android自定义GridView控件​​,达到自己需要的效果。......
  • WPF-UI框架MahApps.Metro使用教程
    一,MahApps.Metro安装1,项目中引用“MahApps.Metro.dll”【失败】我是在网上下载了一个dll,不知道是不是版本问题,使用时一直报错2,在NuGet中安装最新版本的dll右击项目-->......
  • vc入门宝典四(常用控件)
    常用控件何志丹主要内容:1,按钮(1),位图按钮及动态按钮(2),自画按钮2,列表框3,编辑控件和CRichEdit4,CSliderCtrl按钮的使用十分简单,拖一个到对话框,双击它就可以......
  • 控件
    控件                                    何志丹​1,File=>New=>Project=>MFCActiveXControlWizard创建一个名为Owner的工程,所有......
  • 树状控件的应用(选择出阵武将)
    树状控件的应用                         何志丹下面是树状控件的一些应用,由于是由于用于演示,所以结构并不合理.其效果如图所示..步骤如下:1,......
  • 关于 SAP UI5 所有控件的共同祖先 - sap.ui.base.ManagedObject
    ManagedObject的新子类是通过调用ManagedObject.extend创建的,并且可以使用本文介绍的以下托管功能。托管属性表示ManagedObject的状态。它们可以存储简单数据类型(如“......
  • SAP UI5 应用 SimpleForm 控件 ResponsiveGridLayout 布局的工作原理深入剖析试读版
    写作动机笔者在编写本步骤时,在网上搜索了一番,没有找到高质量的中文技术文章,能够讲清楚SAPUI5的屏幕尺寸检测机制。同时SAPUI5XML视图里很多带有S,M,L和XL后缀的控......
  • 【WPF】控件使用要点
    ContentPresenter:显示控件内容,默认显示内容属性,可以指定内容源头ContentSource=。具体用法如下:<ControlTemplateTargetType="{x:TypeHeaderedContentControl}">......
  • WPF开发经验-实现自带触控键盘的TextBox
    一引入项目有个新需求,当点击或触碰TextBox时,基于TextBox的相对位置,弹出一个自定义的Keyboard,如下图所示: 二KeyboardControl先实现一个自定义的KeyboardControl,它继......
  • 【WPF】数据绑定与验证,如Login窗口中的用户名和密码验证提示
    引言数据验证在任何用户界面程序中都是不可缺少的一部分.在WPF中,数据验证更是和绑定紧紧联系在一起,下面简单介绍MVVM模式下常用的几种验证方式.错误信息显示在介绍数......