QQ截圖看起來很簡單,但實際做起來比較複雜。下面先看看完成後的效果圖,後續再詳細說明開發歷程!
與QQ原生截圖還是挺像的吧。整個開發斷斷續續歷時一週左右,期間更換了三種不同思路,重構了2次代碼才最終有上圖展示效果。
廢話不說了,開始講代碼。
第一種方案(有坑):
參考地址:https://www.cnblogs.com/lonelyxmas/p/10754115.html
Demo下載:https://download.csdn.net/download/asciil/12473318
原理描述:
截圖其實一個透明Windows窗體覆蓋在最頂層,其中用InkCanvas畫布存儲截圖時的屏幕圖片。鼠標在其上按下左鍵拖動時,首先畫矩形(也就是截圖區域高亮部分),其次設置四周蒙層部分大小及位置(分爲上下左右四部分,以Rectangle元素實現)。
原理聽起來其實很簡單,但實際上會有問題。請看下面效果圖
看到了嗎,在拖動過程中鼠標處會有白色線條出現。這個現象產生的原因是由於Win10 DPI縮放引起的,通過找資料沒有發現到比較好的解決辦法,所以放棄此思路。(但就在我寫博客時,Windows1909版本更新了,然後我發現同樣的代碼居然沒有這個現象了)
貼上主要部分代碼:
-
樣式文件(ScreenshotWindowStyle.xaml)
<Style x:Key="MaskCanvasStyle" TargetType="Rectangle"> <Setter Property="IsHitTestVisible" Value="False"/> <Setter Property="Fill" Value="#40000000"/> <Setter Property="HorizontalAlignment" Value="Left"/> <Setter Property="VerticalAlignment" Value="Top"/> </Style> <Style x:Key="SnapAreaStyle" TargetType="Border"> <Setter Property="IsHitTestVisible" Value="False"/> <Setter Property="BorderThickness" Value="4"/> <Setter Property="BorderBrush" Value="DodgerBlue"/> <Setter Property="Background" Value="Transparent"/> <Setter Property="HorizontalAlignment" Value="Left"/> <Setter Property="VerticalAlignment" Value="Top"/> <Style.Triggers> <DataTrigger Binding="{Binding IsShowTargetArea,RelativeSource={RelativeSource AncestorType=local:ScreenshotWindow}}" Value="True"> <Setter Property="IsHitTestVisible" Value="True"/> <Setter Property="BorderThickness" Value="1"/> </DataTrigger> </Style.Triggers> </Style> <Style x:Key="HotspotStyle" TargetType="Rectangle"> <Setter Property="Height" Value="5"/> <Setter Property="Width" Value="5"/> <Setter Property="Fill" Value="DodgerBlue"/> <Setter Property="HorizontalAlignment" Value="Left"/> <Setter Property="VerticalAlignment" Value="Top"/> <Setter Property="Visibility" Value="Collapsed"/> <Style.Triggers> <Trigger Property="local:ToolTipAttachProperty.Placement" Value="LeftTop"> <Setter Property="Margin" Value="-3, -3, 0, 0"/> <Setter Property="HorizontalAlignment" Value="Left"/> <Setter Property="VerticalAlignment" Value="Top"/> </Trigger> <Trigger Property="local:ToolTipAttachProperty.Placement" Value="TopCenter"> <Setter Property="Margin" Value="0, -3, 0, 0"/> <Setter Property="HorizontalAlignment" Value="Center"/> <Setter Property="VerticalAlignment" Value="Top"/> </Trigger> <Trigger Property="local:ToolTipAttachProperty.Placement" Value="RightTop"> <Setter Property="Margin" Value="0, -3, -3, 0"/> <Setter Property="HorizontalAlignment" Value="Right"/> <Setter Property="VerticalAlignment" Value="Top"/> </Trigger> <Trigger Property="local:ToolTipAttachProperty.Placement" Value="RightCenter"> <Setter Property="Margin" Value="0, 0, -3, 0"/> <Setter Property="HorizontalAlignment" Value="Right"/> <Setter Property="VerticalAlignment" Value="Center"/> </Trigger> <Trigger Property="local:ToolTipAttachProperty.Placement" Value="RightBottom"> <Setter Property="Margin" Value="0, 0, -3, -3"/> <Setter Property="HorizontalAlignment" Value="Right"/> <Setter Property="VerticalAlignment" Value="Bottom"/> </Trigger> <Trigger Property="local:ToolTipAttachProperty.Placement" Value="BottomCenter"> <Setter Property="Margin" Value="0, 0, 0, -3"/> <Setter Property="HorizontalAlignment" Value="Center"/> <Setter Property="VerticalAlignment" Value="Bottom"/> </Trigger> <Trigger Property="local:ToolTipAttachProperty.Placement" Value="LeftBottom"> <Setter Property="Margin" Value="-3, 0, 0, -3"/> <Setter Property="HorizontalAlignment" Value="Left"/> <Setter Property="VerticalAlignment" Value="Bottom"/> </Trigger> <Trigger Property="local:ToolTipAttachProperty.Placement" Value="LeftCenter"> <Setter Property="Margin" Value="-3, 0, 0, 0"/> <Setter Property="HorizontalAlignment" Value="Left"/> <Setter Property="VerticalAlignment" Value="Center"/> </Trigger> <DataTrigger Binding="{Binding IsShowTargetArea,RelativeSource={RelativeSource AncestorType=local:ScreenshotWindow}}" Value="True"> <Setter Property="Visibility" Value="Visible"/> </DataTrigger> </Style.Triggers> </Style> <Style TargetType="{x:Type local:ScreenshotWindow}"> <Setter Property="UseLayoutRounding" Value="True"/> <Setter Property="WindowStyle" Value="None"/> <Setter Property="WindowState" Value="Maximized"/> <Setter Property="Topmost" Value="True"/> <Setter Property="ResizeMode" Value="NoResize"/> <Setter Property="Background" Value="Transparent"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:ScreenshotWindow}"> <AdornerDecorator> <Grid> <!--背景畫布--> <InkCanvas Name="PART_CANVAS" EditingMode="None" Background="Transparent"/> <!--蒙層,分上下左右四部分--> <Rectangle Name="PART_MASKAREALEFT" Style="{StaticResource MaskCanvasStyle}"/> <Rectangle Name="PART_MASKAREATOP" Style="{StaticResource MaskCanvasStyle}"/> <Rectangle Name="PART_MASKAREARIGHT" Style="{StaticResource MaskCanvasStyle}"/> <Rectangle Name="PART_MASKAREABOTTOM" Style="{StaticResource MaskCanvasStyle}"/> <!--截圖區域--> <Border Name="PART_TARGETAREA" Style="{StaticResource SnapAreaStyle}"> <Grid> <Rectangle local:ToolTipAttachProperty.Placement="LeftTop" Style="{StaticResource HotspotStyle}"/> <Rectangle local:ToolTipAttachProperty.Placement="TopCenter" Style="{StaticResource HotspotStyle}"/> <Rectangle local:ToolTipAttachProperty.Placement="RightTop" Style="{StaticResource HotspotStyle}"/> <Rectangle local:ToolTipAttachProperty.Placement="RightCenter" Style="{StaticResource HotspotStyle}"/> <Rectangle local:ToolTipAttachProperty.Placement="RightBottom" Style="{StaticResource HotspotStyle}"/> <Rectangle local:ToolTipAttachProperty.Placement="BottomCenter" Style="{StaticResource HotspotStyle}"/> <Rectangle local:ToolTipAttachProperty.Placement="LeftBottom" Style="{StaticResource HotspotStyle}"/> <Rectangle local:ToolTipAttachProperty.Placement="LeftCenter" Style="{StaticResource HotspotStyle}"/> <TextBlock Background="#40000000" Foreground="White" HorizontalAlignment="Left" VerticalAlignment="Top" Padding="5,2" Text="{TemplateBinding HintText}"/> </Grid> </Border> </Grid> </AdornerDecorator> </ControlTemplate> </Setter.Value> </Setter> </Style>
-
後臺代碼(ScreenshotWindow.cs)
public class ScreenshotWindow : Window { internal InkCanvas PART_BackgroupCanvas { get; set; } //背景畫布 internal FrameworkElement PART_TargetArea { get; set; } //截圖區域 internal FrameworkElement PART_MaskAreaLeft { get; set; } internal FrameworkElement PART_MaskAreaTop { get; set; } internal FrameworkElement PART_MaskAreaRight { get; set; } internal FrameworkElement PART_MaskAreaBottom { get; set; } private int ScreenWidth, ScreenHeight; //屏幕寬高 private System.Drawing.Bitmap ScreenBitmap { get; set; } //整個屏幕圖片 private bool IsScreenshot; //是否在截圖中 private Point MouseDownPoint; //鼠標按下時座標點 private System.Drawing.Rectangle RectTargetArea { get; set; } //截圖區域矩形位置 #region 依賴屬性 public static readonly DependencyProperty IsShowTargetAreaProperty = DependencyProperty.Register("IsShowTargetArea", typeof(bool), typeof(ScreenshotWindow), new PropertyMetadata(false)); /// <summary> /// 是否顯示截圖區域 /// </summary> public bool IsShowTargetArea { get { return (bool)GetValue(IsShowTargetAreaProperty); } internal set { SetValue(IsShowTargetAreaProperty, value); } } public static readonly DependencyProperty HintTextProperty = DependencyProperty.Register("HintText", typeof(string), typeof(ScreenshotWindow), new PropertyMetadata("")); /// <summary> /// 截圖區域大小提示文字 /// </summary> public string HintText { get { return (string)GetValue(HintTextProperty); } internal set { SetValue(HintTextProperty, value); } } #endregion public override void OnApplyTemplate() { base.OnApplyTemplate(); PART_BackgroupCanvas = GetTemplateChild("PART_CANVAS") as InkCanvas; PART_TargetArea = GetTemplateChild("PART_TARGETAREA") as FrameworkElement; PART_MaskAreaLeft = GetTemplateChild("PART_MASKAREALEFT") as FrameworkElement; PART_MaskAreaTop = GetTemplateChild("PART_MASKAREATOP") as FrameworkElement; PART_MaskAreaRight = GetTemplateChild("PART_MASKAREARIGHT") as FrameworkElement; PART_MaskAreaBottom = GetTemplateChild("PART_MASKAREABOTTOM") as FrameworkElement; } public ScreenshotWindow() { DefaultStyleKey = typeof(ScreenshotWindow); this.Loaded += ScreenshotWindow_Loaded; } private void ScreenshotWindow_Loaded(object sender, RoutedEventArgs e) { //截取屏幕,用作背景 ScreenBitmap = ScreenshotHelper.GetScreenBitmap(out ScreenWidth, out ScreenHeight); var bitmap = ScreenshotHelper.ConvertToBitmapSource(ScreenBitmap); PART_BackgroupCanvas.Background = new ImageBrush(bitmap); } #region 截圖操作-鼠標事件 protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e) { MouseDown_Event(e.GetPosition(null)); } protected override void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs e) { MouseUp_Event(); } protected override void OnPreviewMouseMove(MouseEventArgs e) { MouseMove_Event(e.GetPosition(null)); } //通過鍵盤移動發露目標區域 protected override void OnPreviewKeyDown(KeyEventArgs e) { if (e.Key == Key.Escape) Close(); //ESC鍵關閉窗體 if (IsShowTargetArea) { switch (e.Key) { case Key.Left: MoveTargetShotArea(-1); break; case Key.Up: MoveTargetShotArea(offsetY: -1); break; case Key.Right: MoveTargetShotArea(1); break; case Key.Down: MoveTargetShotArea(offsetY: 1); break; case Key.Enter: Close(); break; } } base.OnPreviewKeyDown(e); } #endregion #region 私有函數 private void MouseDown_Event(Point point) { if (IsShowTargetArea == true) return; //已經截圖,則不允許再次截圖 IsScreenshot = true; //開始截圖 MouseDownPoint = point; } private void MouseUp_Event() { IsScreenshot = false; //停止截圖 } private void MouseMove_Event(Point currentPoint) { if (IsScreenshot) { IsShowTargetArea = true; RectTargetArea = new System.Drawing.Rectangle() { X = (int)Math.Min(MouseDownPoint.X, currentPoint.X), Y = (int)Math.Min(MouseDownPoint.Y, currentPoint.Y), Width = (int)Math.Abs(MouseDownPoint.X - currentPoint.X), Height = (int)Math.Abs(MouseDownPoint.Y - currentPoint.Y) }; RefreshTargetShotArea(); } } //移動截圖目標區域 private void MoveTargetShotArea(int offsetX = 0, int offsetY = 0) { if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) { if (offsetX != 0) { offsetX += RectTargetArea.Left; if (offsetX + RectTargetArea.Width >= ScreenWidth) offsetX = ScreenWidth - RectTargetArea.Width; else offsetX = Math.Max(0, offsetX); RectTargetArea = new System.Drawing.Rectangle() { X = offsetX, Y = RectTargetArea.Y, Width = RectTargetArea.Width, Height = RectTargetArea.Height }; } if (offsetY != 0) { offsetY += RectTargetArea.Top; if (offsetY + RectTargetArea.Height >= ScreenHeight) offsetY = ScreenHeight - RectTargetArea.Height; else offsetY = Math.Max(0, offsetY); RectTargetArea = new System.Drawing.Rectangle() { X = RectTargetArea.X, Y = offsetY, Width = RectTargetArea.Width, Height = RectTargetArea.Height }; } RefreshTargetShotArea(); } } private void RefreshTargetShotArea() { HintText = string.Format("{0}x{1}", RectTargetArea.Width, RectTargetArea.Height); //設置截圖區域控件位置 PART_TargetArea.Margin = new Thickness(RectTargetArea.Left, RectTargetArea.Top, 0, 0); PART_TargetArea.Width = RectTargetArea.Width; PART_TargetArea.Height = RectTargetArea.Height; //設置蒙層位置 PART_MaskAreaLeft.Width = PART_TargetArea.Margin.Left; PART_MaskAreaLeft.Height = ScreenHeight; PART_MaskAreaTop.Margin = new Thickness(PART_TargetArea.Margin.Left, 0, 0, 0); PART_MaskAreaTop.Width = PART_TargetArea.ActualWidth; PART_MaskAreaTop.Height = PART_TargetArea.Margin.Top; PART_MaskAreaRight.Margin = new Thickness(PART_TargetArea.Margin.Left + PART_TargetArea.ActualWidth, 0, 0, 0); PART_MaskAreaRight.Width = ScreenWidth - PART_TargetArea.Margin.Left - PART_TargetArea.ActualWidth; PART_MaskAreaRight.Height = ScreenHeight; PART_MaskAreaBottom.Margin = new Thickness(PART_TargetArea.Margin.Left, PART_TargetArea.Margin.Top + PART_TargetArea.ActualHeight, 0, 0); PART_MaskAreaBottom.Width = PART_TargetArea.ActualWidth; PART_MaskAreaBottom.Height = ScreenHeight - PART_TargetArea.Margin.Top - PART_TargetArea.ActualHeight; } #endregion }
程序員都是一個完美主義者,這個小毛病不能忍受,所以就有了第二種方案的產生。
第二種方案(有坑):
參考地址:不記得參考地址了
Demo下載:https://download.csdn.net/download/asciil/12473335
原理描述:
其原理與第一種方案基本相同,不同之處在於蒙層不再分爲上下左右四部分,而是用一個InkCanvas畫布來實現。蒙層部分由於是填充滿整個屏幕,所以過程中一直不用處理,只需要將截圖區域中圖像高亮顯示即可。在拖動鼠標時畫矩形(截圖區域也就是高亮部分),然後實時獲取矩形區域下的屏幕圖片賦值給此區域,這樣就實現了區域高亮效果。
原理聽起來也沒問題,但請看下面效果圖
第一種方案下下的白線是沒有了,但又出現了新問題,截圖區域內會有輕微的抖動。這是由於截圖區域座標由double轉爲int後數據數度丟失引起的。
貼上主要部分代碼:
-
樣式文件(ScreenshotWindowStyle.xaml)
<Style x:Key="SnapAreaStyle" TargetType="Border"> <Setter Property="BorderThickness" Value="3"/> <Setter Property="BorderBrush" Value="DodgerBlue"/> <Setter Property="Background" Value="Transparent"/> <Setter Property="HorizontalAlignment" Value="Left"/> <Setter Property="VerticalAlignment" Value="Top"/> <Setter Property="Visibility" Value="Collapsed"/> <Style.Triggers> <DataTrigger Binding="{Binding IsShowTargetArea,RelativeSource={RelativeSource AncestorType=local:ScreenshotWindow}}" Value="True"> <Setter Property="IsHitTestVisible" Value="True"/> <Setter Property="BorderThickness" Value="1"/> <Setter Property="Visibility" Value="Visible"/> </DataTrigger> </Style.Triggers> </Style> <Style x:Key="HotspotStyle" TargetType="Rectangle"> <Setter Property="Height" Value="5"/> <Setter Property="Width" Value="5"/> <Setter Property="Fill" Value="DodgerBlue"/> <Setter Property="HorizontalAlignment" Value="Left"/> <Setter Property="VerticalAlignment" Value="Top"/> <Setter Property="Visibility" Value="Collapsed"/> <Style.Triggers> <Trigger Property="local:ToolTipAttachProperty.Placement" Value="LeftTop"> <Setter Property="Margin" Value="-3, -3, 0, 0"/> <Setter Property="HorizontalAlignment" Value="Left"/> <Setter Property="VerticalAlignment" Value="Top"/> </Trigger> <Trigger Property="local:ToolTipAttachProperty.Placement" Value="TopCenter"> <Setter Property="Margin" Value="0, -3, 0, 0"/> <Setter Property="HorizontalAlignment" Value="Center"/> <Setter Property="VerticalAlignment" Value="Top"/> </Trigger> <Trigger Property="local:ToolTipAttachProperty.Placement" Value="RightTop"> <Setter Property="Margin" Value="0, -3, -3, 0"/> <Setter Property="HorizontalAlignment" Value="Right"/> <Setter Property="VerticalAlignment" Value="Top"/> </Trigger> <Trigger Property="local:ToolTipAttachProperty.Placement" Value="RightCenter"> <Setter Property="Margin" Value="0, 0, -3, 0"/> <Setter Property="HorizontalAlignment" Value="Right"/> <Setter Property="VerticalAlignment" Value="Center"/> </Trigger> <Trigger Property="local:ToolTipAttachProperty.Placement" Value="RightBottom"> <Setter Property="Margin" Value="0, 0, -3, -3"/> <Setter Property="HorizontalAlignment" Value="Right"/> <Setter Property="VerticalAlignment" Value="Bottom"/> </Trigger> <Trigger Property="local:ToolTipAttachProperty.Placement" Value="BottomCenter"> <Setter Property="Margin" Value="0, 0, 0, -3"/> <Setter Property="HorizontalAlignment" Value="Center"/> <Setter Property="VerticalAlignment" Value="Bottom"/> </Trigger> <Trigger Property="local:ToolTipAttachProperty.Placement" Value="LeftBottom"> <Setter Property="Margin" Value="-3, 0, 0, -3"/> <Setter Property="HorizontalAlignment" Value="Left"/> <Setter Property="VerticalAlignment" Value="Bottom"/> </Trigger> <Trigger Property="local:ToolTipAttachProperty.Placement" Value="LeftCenter"> <Setter Property="Margin" Value="-3, 0, 0, 0"/> <Setter Property="HorizontalAlignment" Value="Left"/> <Setter Property="VerticalAlignment" Value="Center"/> </Trigger> <DataTrigger Binding="{Binding IsShowTargetArea,RelativeSource={RelativeSource AncestorType=local:ScreenshotWindow}}" Value="True"> <Setter Property="Visibility" Value="Visible"/> </DataTrigger> </Style.Triggers> </Style> <Style x:Key="SnapImageStyle" TargetType="InkCanvas"> <Setter Property="HorizontalAlignment" Value="Left"/> <Setter Property="VerticalAlignment" Value="Top"/> <Setter Property="EditingMode" Value="None"/> <Style.Triggers> <DataTrigger Binding="{Binding IsShowTargetArea,RelativeSource={RelativeSource AncestorType=local:ScreenshotWindow}}" Value="True"> <Setter Property="Visibility" Value="Visible"/> </DataTrigger> </Style.Triggers> </Style> <Style TargetType="{x:Type local:ScreenshotWindow}"> <Setter Property="UseLayoutRounding" Value="True"/> <Setter Property="WindowStyle" Value="None"/> <Setter Property="WindowState" Value="Maximized"/> <Setter Property="Topmost" Value="True"/> <Setter Property="ResizeMode" Value="NoResize"/> <Setter Property="Background" Value="Transparent"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:ScreenshotWindow}"> <AdornerDecorator> <Grid> <!--背景畫布--> <InkCanvas Name="PART_CANVAS" EditingMode="None" Background="Transparent"/> <!--蒙層畫布--> <InkCanvas Name="PART_CANVASMASK" EditingMode="None" Background="#40000000"/> <!--截圖區域--> <Border Name="PART_TARGETAREA" Style="{StaticResource SnapAreaStyle}"> <Grid> <Rectangle local:ToolTipAttachProperty.Placement="LeftTop" Style="{StaticResource HotspotStyle}"/> <Rectangle local:ToolTipAttachProperty.Placement="TopCenter" Style="{StaticResource HotspotStyle}"/> <Rectangle local:ToolTipAttachProperty.Placement="RightTop" Style="{StaticResource HotspotStyle}"/> <Rectangle local:ToolTipAttachProperty.Placement="RightCenter" Style="{StaticResource HotspotStyle}"/> <Rectangle local:ToolTipAttachProperty.Placement="RightBottom" Style="{StaticResource HotspotStyle}"/> <Rectangle local:ToolTipAttachProperty.Placement="BottomCenter" Style="{StaticResource HotspotStyle}"/> <Rectangle local:ToolTipAttachProperty.Placement="LeftBottom" Style="{StaticResource HotspotStyle}"/> <Rectangle local:ToolTipAttachProperty.Placement="LeftCenter" Style="{StaticResource HotspotStyle}"/> <InkCanvas Name="PART_IMAGE" Width="{Binding Width,ElementName=PART_TARGETAREA}" Height="{Binding Height,ElementName=PART_TARGETAREA}" Style="{StaticResource SnapImageStyle}"/> <TextBlock Background="#40000000" Foreground="White" HorizontalAlignment="Left" VerticalAlignment="Top" Padding="5,2" Text="{TemplateBinding HintText}"/> </Grid> </Border> </Grid> </AdornerDecorator> </ControlTemplate> </Setter.Value> </Setter> </Style>
-
後臺代碼(ScreenshotWindow.cs)
public class ScreenshotWindow : Window { internal InkCanvas PART_BackgroupCanvas { get; set; } //背景畫布 internal InkCanvas PART_MaskCanvas { get; set; } //蒙層畫布 internal FrameworkElement PART_TargetArea { get; set; } //截圖區域 internal InkCanvas PART_TargetImage { get; set; } //截取到的圖片 private int ScreenWidth, ScreenHeight; //屏幕寬高 private System.Drawing.Bitmap ScreenBitmap { get; set; } //整個屏幕圖片 private bool IsScreenshot; //是否在截圖中 private Point MouseDownPoint; //鼠標按下時座標點 private System.Drawing.Rectangle RectTargetArea { get; set; } //截圖區域矩形位置 #region 依賴屬性 public static readonly DependencyProperty IsShowTargetAreaProperty = DependencyProperty.Register("IsShowTargetArea", typeof(bool), typeof(ScreenshotWindow), new PropertyMetadata(false)); /// <summary> /// 是否顯示截圖區域 /// </summary> public bool IsShowTargetArea { get { return (bool)GetValue(IsShowTargetAreaProperty); } internal set { SetValue(IsShowTargetAreaProperty, value); } } public static readonly DependencyProperty HintTextProperty = DependencyProperty.Register("HintText", typeof(string), typeof(ScreenshotWindow), new PropertyMetadata("")); /// <summary> /// 截圖區域大小提示文字 /// </summary> public string HintText { get { return (string)GetValue(HintTextProperty); } internal set { SetValue(HintTextProperty, value); } } #endregion public override void OnApplyTemplate() { base.OnApplyTemplate(); PART_BackgroupCanvas = GetTemplateChild("PART_CANVAS") as InkCanvas; PART_MaskCanvas = GetTemplateChild("PART_CANVASMASK") as InkCanvas; PART_TargetArea = GetTemplateChild("PART_TARGETAREA") as FrameworkElement; PART_TargetImage = GetTemplateChild("PART_IMAGE") as InkCanvas; } public ScreenshotWindow() { DefaultStyleKey = typeof(ScreenshotWindow); this.Loaded += ScreenshotWindow_Loaded; } private void ScreenshotWindow_Loaded(object sender, RoutedEventArgs e) { //截取屏幕,用作背景 ScreenBitmap = ScreenshotHelper.GetScreenBitmap(out ScreenWidth, out ScreenHeight); var bitmap = ScreenshotHelper.ConvertToBitmapSource(ScreenBitmap); PART_BackgroupCanvas.Background = new ImageBrush(bitmap); } #region 截圖操作-鼠標事件 protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e) { MouseDown_Event(e.GetPosition(null)); } protected override void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs e) { MouseUp_Event(); } protected override void OnPreviewMouseMove(MouseEventArgs e) { MouseMove_Event(e.GetPosition(null)); } //通過鍵盤移動發露目標區域 protected override void OnPreviewKeyDown(KeyEventArgs e) { if (e.Key == Key.Escape) Close(); //ESC鍵關閉窗體 if (IsShowTargetArea) { switch (e.Key) { case Key.Left: MoveTargetShotArea(-1); break; case Key.Up: MoveTargetShotArea(offsetY: -1); break; case Key.Right: MoveTargetShotArea(1); break; case Key.Down: MoveTargetShotArea(offsetY: 1); break; case Key.Enter: Close(); break; } } base.OnPreviewKeyDown(e); } #endregion #region 私有函數 private void MouseDown_Event(Point point) { if (IsShowTargetArea == true) return; //已經截圖,則不允許再次截圖 IsScreenshot = true; //開始截圖 MouseDownPoint = point; } private void MouseUp_Event() { IsScreenshot = false; //停止截圖 } private void MouseMove_Event(Point currentPoint) { if (IsScreenshot) { IsShowTargetArea = true; RectTargetArea = new System.Drawing.Rectangle() { X = (int)Math.Min(MouseDownPoint.X, currentPoint.X), Y = (int)Math.Min(MouseDownPoint.Y, currentPoint.Y), Width = (int)Math.Abs(MouseDownPoint.X - currentPoint.X), Height = (int)Math.Abs(MouseDownPoint.Y - currentPoint.Y) }; RefreshTargetShotArea(); } } //移動截圖目標區域 private void MoveTargetShotArea(int offsetX = 0, int offsetY = 0) { if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) { if (offsetX != 0) { offsetX += RectTargetArea.Left; if (offsetX + RectTargetArea.Width >= ScreenWidth) offsetX = ScreenWidth - RectTargetArea.Width; else offsetX = Math.Max(0, offsetX); RectTargetArea = new System.Drawing.Rectangle() { X = offsetX, Y = RectTargetArea.Y, Width = RectTargetArea.Width, Height = RectTargetArea.Height }; } if (offsetY != 0) { offsetY += RectTargetArea.Top; if (offsetY + RectTargetArea.Height >= ScreenHeight) offsetY = ScreenHeight - RectTargetArea.Height; else offsetY = Math.Max(0, offsetY); RectTargetArea = new System.Drawing.Rectangle() { X = RectTargetArea.X, Y = offsetY, Width = RectTargetArea.Width, Height = RectTargetArea.Height }; } RefreshTargetShotArea(); } } private void RefreshTargetShotArea() { HintText = string.Format("{0}x{1}", RectTargetArea.Width, RectTargetArea.Height); //設置截圖區域控件位置 Thickness thickness = new Thickness(RectTargetArea.Left, RectTargetArea.Top, 0, 0); PART_TargetArea.SetValue(FrameworkElement.MarginProperty, thickness); PART_TargetArea.Width = RectTargetArea.Width; PART_TargetArea.Height = RectTargetArea.Height; //高亮顯示截圖部分 if ((int)RectTargetArea.Width == 0 || (int)RectTargetArea.Height == 0) return; float dpiX, dpiY; using (System.Drawing.Graphics graphics = System.Drawing.Graphics.FromHwnd(IntPtr.Zero)) { dpiX = graphics.DpiX / 96; dpiY = graphics.DpiY / 96; } System.Drawing.RectangleF srcRect = new System.Drawing.RectangleF() { X = RectTargetArea.Left * dpiX, Y = RectTargetArea.Top * dpiY, Width = RectTargetArea.Width * dpiX, Height = RectTargetArea.Height * dpiY }; var descScreenBitmap = ScreenshotHelper.CutScreenBitmap(ScreenBitmap, srcRect); var bitmap = ScreenshotHelper.ConvertToBitmapSource(descScreenBitmap); PART_TargetImage.Background = new ImageBrush(bitmap); } #endregion }
-
距離我們的目的越來越近了,不能就半途放棄了。一頓搜索及思考後,腦海中出現了第三種方案。
第三種方案(完美):
原理描述:
綜合考慮到前兩種方案問題引起的原因,那麼我們是不是可以綜合兩種方案丟棄掉有問題的部分。
具體思路爲用InkCanvas畫布存儲截圖時的屏幕圖片,截圖區域處理邏輯不變,蒙層分上下左右四部採用Canvas畫布實現。鼠標拖動過程中,首先畫矩形(也就是截圖區域高亮部分),然後計算上下左右蒙層X,Y及寬高變化進行賦值(關鍵:不要直接用 元素.LEFT,元素.TOP進行賦值,採用 Canvas.SetLeft及 Canvas.SetToptTop進行設置),這樣可以完美避開前兩種方案的問題。