欧美成人精品手机在线观看_69视频国产_动漫精品第一页_日韩中文字幕网 - 日本欧美一区二区

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

.NET 控件轉(zhuǎn)圖片

freeflydom
2024年10月16日 10:15 本文熱度 684

Windows應(yīng)用開發(fā)有很多場景需要動態(tài)獲取控件顯示的圖像,即控件轉(zhuǎn)圖片,用于其它界面的顯示、傳輸圖片數(shù)據(jù)流、保存為本地圖片等用途。

下面分別介紹下一些實現(xiàn)方式以及主要使用場景

RenderTargetBitmap

控件轉(zhuǎn)圖片BitmapImage/BitmapSource,在WPF中可以使用RenderTargetBitmap獲取捕獲控件的圖像。

RenderTargetBitmap是用于將任何 Visual 元素內(nèi)容渲染為位圖的主要工具

下面我們展示下簡單快速的獲取控件圖片:

?
 1     private void CaptureButton_OnClick(object sender, RoutedEventArgs e)
 2     {
 3         var dpi = GetAppStartDpi();
 4         var bitmapSource = ToImageSource(Grid1, Grid1.RenderSize, dpi.X, dpi.Y);
 5         CaptureImage.Source = bitmapSource;
 6     }
 7     /// <summary>
 8     /// Visual轉(zhuǎn)圖片
 9     /// </summary>
10     public static BitmapSource ToImageSource(Visual visual, Size size, double dpiX, double dpiY)
11     {
12         var validSize = size.Width > 0 && size.Height > 0;
13         if (!validSize) throw new ArgumentException($"{nameof(size)}值無效:${size.Width},${size.Height}");
14         if (Math.Abs(size.Width) > 0.0001 && Math.Abs(size.Height) > 0.0001)
15         {
16             RenderTargetBitmap bitmap = new RenderTargetBitmap((int)(size.Width * dpiX), (int)(size.Height * dpiY), dpiX * 96, dpiY * 96, PixelFormats.Pbgra32);
17             bitmap.Render(visual);
18             return bitmap;
19         }
20         return new BitmapImage();
21     }

獲取當(dāng)前窗口所在屏幕DPI,使用控件已經(jīng)渲染的尺寸,就可以捕獲到指定控件的渲染圖片。捕獲到圖片BitmapSource,即可以將位圖分配給Image的Source屬性來顯示。

DPI獲取可以參考 C# 獲取當(dāng)前屏幕DPI - 唐宋元明清2188 - 博客園 (cnblogs.com)

上面方法獲取的是BitmapSource,BitmapSource是WPF位圖的的抽象基類,繼承自ImageSource,因此可以直接用作WPF控件如Image的圖像源。RenderTargetBitmap以及BitmapImage均是BitmapSource的派生實現(xiàn)類

RenderTargetBitmap此處用于渲染Visual對象生成位圖,RenderTargetBitmap它可以用于拼接、合并(上下層疊加)、縮放圖像等。BitmapImage主要用于從文件、URL及流中加載位圖。

而捕獲返回的基類BitmapSource可以用于通用位圖的一些操作(如渲染、轉(zhuǎn)成流數(shù)據(jù)、保存),BitmapSource如果需要轉(zhuǎn)成可以支持支持更高層次圖像加載功能和延遲加載機制的BitmapImage,可以按如下操作:


 1     /// <summary>
 2     /// WPF位圖轉(zhuǎn)換
 3     /// </summary>
 4     private static BitmapImage ToBitmapImage(BitmapSource bitmap,Size size,double dpiX,double dpiY)
 5     {
 6         MemoryStream memoryStream = new MemoryStream();
 7         BitmapEncoder encoder = new PngBitmapEncoder();
 8         encoder.Frames.Add(BitmapFrame.Create(bitmap));
 9         encoder.Save(memoryStream);
10         memoryStream.Seek(0L, SeekOrigin.Begin);
11 
12         BitmapImage bitmapImage = new BitmapImage();
13         bitmapImage.BeginInit();
14         bitmapImage.DecodePixelWidth = (int)(size.Width * dpiX);
15         bitmapImage.DecodePixelHeight = (int)(size.Height * dpiY);
16         bitmapImage.StreamSource = memoryStream;
17         bitmapImage.EndInit();
18         bitmapImage.Freeze();
19         return bitmapImage;
20     }

這里選擇了Png編碼器,先將bitmapSource轉(zhuǎn)換成圖片流,然后再解碼為BitmapImage。

圖片編碼器有很多種用途,上面是將流轉(zhuǎn)成內(nèi)存流,也可以轉(zhuǎn)成文件流保存本地文件:

1     var encoder = new PngBitmapEncoder();
2     encoder.Frames.Add(BitmapFrame.Create(bitmapSource));
3     using Stream stream = File.Create(imagePath);
4     encoder.Save(stream);

回到控件圖片捕獲,上方操作是在界面控件渲染后的場景。如果控件未加載,需要更新布局下:

1     //未加載到視覺樹的,按指定大小布局
2     //按size顯示,如果設(shè)計寬高大于size則按sie裁剪,如果設(shè)計寬度小于size則按size放大顯示。
3     element.Measure(size);
4     element.Arrange(new Rect(size));

另外也存在場景:控件不確定它的具體尺寸,只是想單純捕獲圖像,那代碼整理后如下:


 1     public BitmapSource ToImageSource(Visual visual, Size size = default)
 2     {
 3         if (!(visual is FrameworkElement element))
 4         {
 5             return null;
 6         }
 7         if (!element.IsLoaded)
 8         {
 9             if (size == default)
10             {
11                 //計算元素的渲染尺寸
12                 element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
13                 element.Arrange(new Rect(new Point(), element.DesiredSize));
14                 size = element.DesiredSize;
15             }
16             else
17             {
18                 //未加載到視覺樹的,按指定大小布局
19                 //按size顯示,如果設(shè)計寬高大于size則按sie裁剪,如果設(shè)計寬度小于size則按size放大顯示。
20                 element.Measure(size);
21                 element.Arrange(new Rect(size));
22             }
23         }
24         else if (size == default)
25         {
26             Rect rect = VisualTreeHelper.GetDescendantBounds(visual);
27             if (rect.Equals(Rect.Empty))
28             {
29                 return null;
30             }
31             size = rect.Size;
32         }
33 
34         var dpi = GetAppStartDpi();
35         return ToImageSource(visual, size, dpi.X, dpi.Y);
36     }

控件未加載時,可以使用DesiredSize來臨時替代操作,這類方案獲取的圖片寬高比例可能不太準確。已加載完的控件,可以通過VisualTreeHelper.GetDescendantBounds獲取視覺樹子元素集的坐標矩形區(qū)域Bounds。

kybs00/VisualImageDemo: RenderTargetBitmap獲取控件圖片 (github.com)

所以控件轉(zhuǎn)BitmapSource、保存等,可以使用RenderTargetBitmap來實現(xiàn)

VisualBrush

如果只是程序內(nèi)其它界面同步展示此控件,就不需要RenderTargetBitmap了,可以直接使用VisualBrush

VisualBrush是非常強大的類,允許使用另一個Visual對象(界面顯示控件最底層的UI元素基類)作為畫刷的內(nèi)容,并將其繪制在其它UI元素上(當(dāng)然,不是直接掛到其它視覺樹上,WPF也不支持元素同時存在于倆個視覺樹的設(shè)計)

具體的可以看下官網(wǎng)VisualBrush 類 (System.Windows.Media) | Microsoft Learn,這里做一個簡單的DEMO:


 1 <Window x:Class="VisualBrushDemo.MainWindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 5         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 6         xmlns:local="clr-namespace:VisualBrushDemo"
 7         mc:Ignorable="d" Title="MainWindow" Height="450" Width="800">
 8     <Grid>
 9         <Grid.ColumnDefinitions>
10             <ColumnDefinition Width="*"/>
11             <ColumnDefinition Width="10"/>
12             <ColumnDefinition/>
13         </Grid.ColumnDefinitions>
14         <Canvas x:Name="Grid1" Background="BlueViolet">
15             <TextBlock x:Name="TestTextBlock" Text="截圖測試" VerticalAlignment="Center" HorizontalAlignment="Center" 
16                        Width="100" Height="30" Background="Red" TextAlignment="Center" LineHeight="30" Padding="0 6 0 0"
17                        MouseDown="TestTextBlock_OnMouseDown" 
18                        MouseMove="TestTextBlock_OnMouseMove"
19                        MouseUp="TestTextBlock_OnMouseUp"/>
20         </Canvas>
21         <Grid x:Name="Grid2" Grid.Column="2">
22             <Grid.Background>
23                 <VisualBrush Stretch="UniformToFill"
24                              AlignmentX="Center" AlignmentY="Center"
25                              Visual="{Binding ElementName=Grid1}"/>
26             </Grid.Background>
27         </Grid>
28     </Grid>
29 </Window>

CS代碼:


 1     private bool _isDown;
 2     private Point _relativeToBlockPosition;
 3     private void TestTextBlock_OnMouseDown(object sender, MouseButtonEventArgs e)
 4     {
 5         _isDown = true;
 6         _relativeToBlockPosition = e.MouseDevice.GetPosition(TestTextBlock);
 7         TestTextBlock.CaptureMouse();
 8     }
 9 
10     private void TestTextBlock_OnMouseMove(object sender, MouseEventArgs e)
11     {
12         if (_isDown)
13         {
14             var position = e.MouseDevice.GetPosition(Grid1);
15             Canvas.SetTop(TestTextBlock, position.Y - _relativeToBlockPosition.Y);
16             Canvas.SetLeft(TestTextBlock, position.X - _relativeToBlockPosition.X);
17         }
18     }
19 
20     private void TestTextBlock_OnMouseUp(object sender, MouseButtonEventArgs e)
21     {
22         TestTextBlock.ReleaseMouseCapture();
23         _isDown = false;
24     }

kybs00/VisualImageDemo: RenderTargetBitmap獲取控件圖片 (github.com) 

左側(cè)操作一個控件移動,右側(cè)區(qū)域動態(tài)同步顯示左側(cè)視覺。VisualBrush.Visual可以直接綁定指定控件,一次綁定、后續(xù)同步界面變更,延時超低

同步界面變更是如何操作的?下面是部分代碼,我們看到,VisualBrush內(nèi)有監(jiān)聽元素的內(nèi)容變更,內(nèi)容變更后VisualBrush也會自動同步DoLayout(element)一次:


 1     // We need 2 ways of initiating layout on the VisualBrush root.
 2     // 1. We add a handler such that when the layout is done for the
 3     // main tree and LayoutUpdated is fired, then we do layout for the
 4     // VisualBrush tree.
 5     // However, this can fail in the case where the main tree is composed
 6     // of just Visuals and never does layout nor fires LayoutUpdated. So
 7     // we also need the following approach.
 8     // 2. We do a BeginInvoke to start layout on the Visual. This approach 
 9     // alone, also falls short in the scenario where if we are already in 
10     // MediaContext.DoWork() then we will do layout (for main tree), then look
11     // at Loaded callbacks, then render, and then finally the Dispather will 
12     // fire us for layout. So during loaded callbacks we would not have done
13     // layout on the VisualBrush tree.
14     //
15     // Depending upon which of the two layout passes comes first, we cancel
16     // the other layout pass.
17     element.LayoutUpdated += OnLayoutUpdated;
18     _DispatcherLayoutResult = Dispatcher.BeginInvoke(
19         DispatcherPriority.Normal,
20         new DispatcherOperationCallback(LayoutCallback),
21         element);
22     _pendingLayout = true;

而顯示綁定元素,VisualBrush內(nèi)部是通過元素Visual.Render方法將圖像給到渲染上下文:

1     RenderContext rc = new RenderContext();
2     rc.Initialize(channel, DUCE.ResourceHandle.Null);
3     vVisual.Render(rc, 0);

其內(nèi)部是將Visual的快照拿來顯示輸出。VisualBrush基于這種渲染快照的機制,不會影響原始視覺元素在原來視覺樹的位置,所以并不會導(dǎo)致不同視覺樹之間的沖突。

此類VisualBrush方案,適合制作預(yù)覽顯示,比如打印預(yù)覽、PPT頁面預(yù)覽列表等。

下面是我們團隊開發(fā)的會議白板-頁面列表預(yù)覽效果:



該文章在 2024/10/16 10:15:00 編輯過
關(guān)鍵字查詢
相關(guān)文章
正在查詢...
點晴ERP是一款針對中小制造業(yè)的專業(yè)生產(chǎn)管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國內(nèi)大量中小企業(yè)的青睞。
點晴PMS碼頭管理系統(tǒng)主要針對港口碼頭集裝箱與散貨日常運作、調(diào)度、堆場、車隊、財務(wù)費用、相關(guān)報表等業(yè)務(wù)管理,結(jié)合碼頭的業(yè)務(wù)特點,圍繞調(diào)度、堆場作業(yè)而開發(fā)的。集技術(shù)的先進性、管理的有效性于一體,是物流碼頭及其他港口類企業(yè)的高效ERP管理信息系統(tǒng)。
點晴WMS倉儲管理系統(tǒng)提供了貨物產(chǎn)品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質(zhì)期管理,貨位管理,庫位管理,生產(chǎn)管理,WMS管理系統(tǒng),標簽打印,條形碼,二維碼管理,批號管理軟件。
點晴免費OA是一款軟件和通用服務(wù)都免費,不限功能、不限時間、不限用戶的免費OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved