在WPF中自定義Image控件實(shí)現(xiàn)圖像縮放與平移
在 WPF 應(yīng)用開(kāi)發(fā)中,圖像的交互式瀏覽是一項(xiàng)高頻需求。無(wú)論是醫(yī)學(xué)影像查看、工業(yè)檢測(cè)界面,還是普通圖片瀏覽器,縮放和平移都是最基礎(chǔ)也最核心的交互能力。本文將從架構(gòu)設(shè)計(jì)、坐標(biāo)變換、交互邏輯三個(gè)維度,系統(tǒng)講解如何在 WPF 中構(gòu)建一個(gè)具備專(zhuān)業(yè)級(jí)體驗(yàn)的自定義圖像控件。
一、為什么需要自定義控件
WPF 內(nèi)置的 Image 控件功能單一,僅負(fù)責(zé)將圖像渲染到界面。雖然可以通過(guò) ScaleTransform 和 TranslateTransform 實(shí)現(xiàn)基礎(chǔ)的縮放平移,但在實(shí)際項(xiàng)目中往往面臨以下挑戰(zhàn):
- 變換原點(diǎn)不一致:縮放時(shí)以鼠標(biāo)位置為中心還是以控件中心為中心,直接決定用戶(hù)體驗(yàn)
- 邊界約束缺失:圖像縮放過(guò)小或平移出可視區(qū)域后,界面呈現(xiàn)失控狀態(tài)
- 交互沖突:縮放與平移的手勢(shì)識(shí)別、與外部滾動(dòng)條的協(xié)調(diào)、與選中框等疊加元素的配合
- 性能瓶頸:大圖像頻繁變換時(shí)的渲染卡頓,以及縮略圖與全圖的切換策略
自定義控件的價(jià)值在于將這些問(wèn)題封裝到控件內(nèi)部,對(duì)外暴露簡(jiǎn)潔的依賴(lài)屬性(如 ZoomLevel、PanOffset),讓業(yè)務(wù)層只需關(guān)注"顯示什么",而非"如何顯示"。
二、核心架構(gòu)設(shè)計(jì)
1. 視覺(jué)樹(shù)結(jié)構(gòu)
一個(gè)健壯的圖像瀏覽控件,視覺(jué)樹(shù)通常包含三層:
- 底層:圖像層
承載原始圖像內(nèi)容。使用 ImageBrush 填充一個(gè) Rectangle 或直接使用 Image 元素,配合 RenderTransform 應(yīng)用變換矩陣。選擇 RenderTransform 而非 LayoutTransform 的關(guān)鍵原因在于性能——前者在布局完成后執(zhí)行,不會(huì)觸發(fā)重新測(cè)量和排列。 - 中層:交互層
覆蓋在圖像之上的透明面板(如 Canvas 或自定義的 Adorner),負(fù)責(zé)捕獲鼠標(biāo)和觸摸事件。將交互層與圖像層分離,可以避免變換矩陣對(duì)事件坐標(biāo)的干擾,也便于后續(xù)疊加標(biāo)注、測(cè)量線等附加元素。 - 頂層:裝飾層
用于顯示當(dāng)前縮放比例、導(dǎo)航縮略圖、操作提示等輔助信息。通常通過(guò) AdornerLayer 實(shí)現(xiàn),保持與主內(nèi)容的解耦。
2. 變換矩陣的統(tǒng)一管理
WPF 中的 MatrixTransform 是管理縮放平移的最佳選擇。相比分別維護(hù) ScaleTransform 和 TranslateTransform,單一矩陣的優(yōu)勢(shì)在于:
- 組合變換只需一次矩陣乘法,避免多次變換的累積誤差
- 坐標(biāo)轉(zhuǎn)換(屏幕坐標(biāo) ↔ 圖像坐標(biāo))可以通過(guò)矩陣的逆運(yùn)算精確完成
- 動(dòng)畫(huà)過(guò)渡時(shí),只需對(duì)矩陣元素進(jìn)行插值,實(shí)現(xiàn)平滑的縮放動(dòng)畫(huà)
控件內(nèi)部維護(hù)一個(gè) Matrix 對(duì)象,任何用戶(hù)操作(滾輪縮放、拖拽平移)都轉(zhuǎn)化為對(duì)該矩陣的更新,再同步到 RenderTransform。
三、縮放邏輯的深度解析
1. 以鼠標(biāo)位置為中心的縮放
這是專(zhuān)業(yè)圖像瀏覽器的標(biāo)準(zhǔn)行為。核心原理是:縮放前后,鼠標(biāo)指針在圖像上的對(duì)應(yīng)物理位置保持不變。
實(shí)現(xiàn)上需要三個(gè)步驟:
- 獲取鼠標(biāo)當(dāng)前位置相對(duì)于圖像容器的坐標(biāo)
- 計(jì)算縮放后的新矩陣,使該坐標(biāo)點(diǎn)在新舊矩陣下的映射結(jié)果相同
- 應(yīng)用新矩陣并刷新顯示
數(shù)學(xué)上,這等價(jià)于以鼠標(biāo)位置為原點(diǎn)進(jìn)行縮放變換,再補(bǔ)償平移量。這種"錨點(diǎn)縮放"讓用戶(hù)能夠精確放大到感興趣的區(qū)域,而不需要縮放后再手動(dòng)平移調(diào)整。
2. 縮放級(jí)別的離散化與連續(xù)化
兩種策略適用于不同場(chǎng)景:
- 連續(xù)縮放(如 Photoshop)
滾輪每轉(zhuǎn)動(dòng)一格,縮放比例按固定步長(zhǎng)(如 1.1 倍)連續(xù)變化。體驗(yàn)流暢,適合精細(xì)調(diào)整,但可能產(chǎn)生非整數(shù)的縮放比例,對(duì)像素級(jí)查看不夠友好。 - 離散級(jí)別(如地圖應(yīng)用)
預(yù)定義一組縮放級(jí)別(如 25%、50%、100%、200%),滾輪 操作跳到最近的級(jí)別。適合需要精確比例的場(chǎng)景,但切換時(shí)可能有跳躍感。
實(shí)際實(shí)現(xiàn)中,可以結(jié)合兩者:滾輪提供連續(xù)縮放,同時(shí)提供快捷鍵直接跳轉(zhuǎn)到 100%(實(shí)際像素)或適應(yīng)窗口等離散級(jí)別。
3. 縮放范圍的邊界控制
必須限制最小和最大縮放比例,防止用戶(hù)操作失控:
- 最小縮放:通常設(shè)為圖像完全適應(yīng)控件窗口的比例,或略小一些(如 10%)。小于此值時(shí),圖像可能縮成一個(gè)小點(diǎn),失去操作意義。
- 最大縮放:取決于圖像分辨率和應(yīng)用場(chǎng)景。醫(yī)學(xué)影像可能需要放大到 800% 觀察細(xì)節(jié),而普通圖片瀏覽器 400% 已足夠。
邊界控制應(yīng)在矩陣計(jì)算階段完成,而非事后修正。即先計(jì)算理論變換矩陣,再將其裁剪到合法范圍內(nèi),最后應(yīng)用。這樣可以避免矩陣"抖動(dòng)"——用戶(hù)快速滾動(dòng)時(shí),縮放比例在邊界附近來(lái)回彈跳。
四、平移邏輯的精細(xì)處理
1. 拖拽平移的坐標(biāo)映射
鼠標(biāo)拖拽時(shí),控件需要區(qū)分"按下了哪個(gè)鍵"和"在哪個(gè)元素上按下"。通常約定:
- 左鍵拖拽:平移圖像
- 右鍵拖拽:框選區(qū)域(如有此功能)
- 中鍵拖拽:部分軟件用作快速平移(與左鍵功能相同,但適合長(zhǎng)時(shí)間拖拽)
拖拽平移的本質(zhì)是:鼠標(biāo)移動(dòng)向量直接映射為圖像的平移向量,再疊加到變換矩陣上。但需要注意,這里的移動(dòng)向量是屏幕像素距離,而矩陣中的平移量是變換后的邏輯距離。由于縮放比例的存在,兩者是線性關(guān)系,但方向相反——鼠標(biāo)向右移動(dòng),圖像應(yīng)向左滾動(dòng),以產(chǎn)生"抓住圖像拖動(dòng)"的物理直覺(jué)。
2. 邊界回彈與留白策略
圖像平移出可視區(qū)域是常見(jiàn)誤操作,控件應(yīng)提供智能約束:
- 硬邊界(Hard Clamp)
圖像邊緣不允許超出控件邊界。實(shí)現(xiàn)簡(jiǎn)單,但用戶(hù)體驗(yàn)生硬——當(dāng)圖像放大后,邊緣恰好對(duì)齊邊界時(shí),用戶(hù)可能誤以為已經(jīng)到達(dá)圖像盡頭。 - 軟邊界(Soft Bounce)
允許圖像短暫超出邊界,松手后自動(dòng)回彈。類(lèi)似手機(jī)相冊(cè)的體驗(yàn),操作感更自然,但實(shí)現(xiàn)復(fù)雜度增加,需要處理動(dòng)畫(huà)過(guò)渡。 - 留白邊距(Margin)
即使圖像完全適應(yīng)窗口,也允許少量平移,使圖像不必始終居中。這在配合其他疊加元素(如側(cè)邊屬性面板)時(shí)特別有用。
3. 慣性滾動(dòng)
對(duì)于觸摸屏或大圖像瀏覽,加入慣性滾動(dòng)能顯著提升體驗(yàn)。鼠標(biāo)/手指松開(kāi)后,圖像繼續(xù)按原有方向減速滑動(dòng),直到停止。實(shí)現(xiàn)上需要:
- 拖拽過(guò)程中記錄最近幾幀的速度向量
- 釋放時(shí)根據(jù)末速度啟動(dòng)動(dòng)畫(huà)
- 動(dòng)畫(huà)每幀按衰減系數(shù)降低速度,同時(shí)檢測(cè)邊界碰撞
WPF 的 CompositionTarget.Rendering 或 Storyboard 均可實(shí)現(xiàn)慣性動(dòng)畫(huà),前者幀率更穩(wěn)定,后者與 XAML 集成更好。
五、代碼實(shí)現(xiàn)
1. 自定義控件界面布局xaml實(shí)現(xiàn)
<UserControl
x:Class="HalconDemo.ImageUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:HalconDemo"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
d:DesignHeight="1024"
d:DesignWidth="1920"
mc:Ignorable="d">
<Grid>
<ContentControl
Name="Part_ImageContainer"
MaxWidth="1920"
MaxHeight="1024"
HorizontalAlignment="Left"
VerticalAlignment="Top"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
ClipToBounds="True"
Cursor="Hand">
<Image
Name="image"
HorizontalAlignment="Left"
VerticalAlignment="Top"
RenderOptions.BitmapScalingMode="NearestNeighbor">
<Image.RenderTransform>
<TransformGroup x:Name="group">
<ScaleTransform x:Name="scaler" />
<TranslateTransform x:Name="transer" />
</TransformGroup>
</Image.RenderTransform>
</Image>
</ContentControl>
</Grid>
</UserControl>2. 自定義控件后端代碼cs實(shí)現(xiàn)
/// <summary>
/// ImageUserControl.xaml 的交互邏輯
/// </summary>
public partial class ImageUserControl : UserControl
{
/// <summary>
/// 鼠標(biāo)是否按下
/// </summary>
private bool mouseDown;
/// <summary>
/// 移動(dòng)圖片前,按下鼠標(biāo)左鍵時(shí),鼠標(biāo)相對(duì)于Part_ImageContainer的點(diǎn)
/// </summary>
Point moveStart;
public ImageUserControl()
{
InitializeComponent();
}
~ImageUserControl()
{
Part_ImageContainer.MouseLeftButtonDown -= ImgMouseLeftButtonDown;
Part_ImageContainer.MouseMove -= ImgMouseMove;
Part_ImageContainer.MouseUp -= ImgMouseUp;
}
/// <summary>
/// 初始化加載圖像
/// </summary>
/// <param name="filePath">圖像路徑</param>
/// <param name="IsMove">是否移動(dòng),true是,false否</param>
public void InitImage(string filePath,bool IsMove = true)
{
if (IsMove)
{
Part_ImageContainer.MouseLeftButtonDown += ImgMouseLeftButtonDown;
Part_ImageContainer.MouseMove += ImgMouseMove;
Part_ImageContainer.MouseUp += ImgMouseUp;
}
image.Source = new BitmapImage(new Uri(filePath, UriKind.Absolute));
}
private void ImgMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
//嘗試將鼠標(biāo)強(qiáng)制捕獲到此元素
Part_ImageContainer.CaptureMouse();
mouseDown = true;
moveStart = e.GetPosition(Part_ImageContainer);
}
private void ImgMouseMove(object sender, MouseEventArgs e)
{
var mouseEnd = e.GetPosition(Part_ImageContainer); // 鼠標(biāo)移動(dòng)時(shí),獲取鼠標(biāo)相對(duì)圖片容器的點(diǎn)
if (mouseDown)
{
if (e.LeftButton == MouseButtonState.Pressed) // 按下鼠標(biāo)左鍵,移動(dòng)圖片
{
DoMove(mouseEnd);
}
}
}
private void ImgMouseUp(object sender, MouseButtonEventArgs e)
{
Part_ImageContainer.ReleaseMouseCapture();
mouseDown = false;
}
private void ImgMouseWheel(object sender, MouseWheelEventArgs e)
{
var point = e.GetPosition(image);
double delta = e.Delta * 0.002;
DoScale(point, delta);
}
/// <summary>
/// 縮放圖片。最小為0.1倍,最大為30倍
/// </summary>
/// <param name="point">相對(duì)于圖片的點(diǎn),以此點(diǎn)為中心縮放</param>
/// <param name="delta">縮放的倍數(shù)增量</param>
private void DoScale(Point point, double delta)
{
// 限制最大、最小縮放倍數(shù)
if (scaler.ScaleX + delta < 0.1 || scaler.ScaleX + delta > 30) return;
scaler.ScaleX += delta;
scaler.ScaleY += delta;
transer.X -= point.X * delta;
transer.Y -= point.Y * delta;
}
/// <summary>
/// 移動(dòng)圖片
/// </summary>
/// <param name="moveEndPoint">移動(dòng)圖片的終點(diǎn)(相對(duì)于Part_ImageContainer)</param>
private void DoMove(Point moveEndPoint)
{
// 考慮到旋轉(zhuǎn)的影響,因此將兩個(gè)點(diǎn)轉(zhuǎn)換到Part_Image坐標(biāo)系,計(jì)算x、y的增量
Point start = Part_ImageContainer.TranslatePoint(moveStart, image);
Point end = Part_ImageContainer.TranslatePoint(moveEndPoint, image);
// 判斷一下,如果scale很大的時(shí)候,移動(dòng)會(huì)很遲緩。此時(shí)應(yīng)該將移動(dòng)放大
if (scaler.ScaleX > 7)
{
transer.X += (end.X - start.X) * 4;
transer.Y += (end.Y - start.Y) * 4;
}
else if (scaler.ScaleX > 5)
{
transer.X += (end.X - start.X) * 3;
transer.Y += (end.Y - start.Y) * 3;
}
else if (scaler.ScaleX > 3)
{
transer.X += (end.X - start.X) * 2;
transer.Y += (end.Y - start.Y) * 2;
}
else if (scaler.ScaleX < 0.5)
{
transer.X += (end.X - start.X) * 0.5;
transer.Y += (end.Y - start.Y) * 0.5;
}
else
{
transer.X += (end.X - start.X);
transer.Y += (end.Y - start.Y);
}
moveStart = moveEndPoint;
// 以下代碼,抄的https://blog.csdn.net/weixin_42975610/article/details/113741534
// W+w > 2*move_x > -((2*scale-1)*w + W) 水平平移限制條件
// H+h > 2*move_y > -((2*scale-1)*h + H) 垂直平移限制條件
if (transer.X * 2 > image.ActualWidth + Part_ImageContainer.ActualWidth - 20)
transer.X = (image.ActualWidth + Part_ImageContainer.ActualWidth - 20) / 2;
if (-transer.X * 2 > (2 * scaler.ScaleX - 1) * image.ActualWidth + Part_ImageContainer.ActualWidth - 20)
transer.X = -((scaler.ScaleX - 0.5) * image.ActualWidth + Part_ImageContainer.ActualWidth / 2 - 10);
if (transer.Y * 2 > image.ActualHeight + Part_ImageContainer.ActualHeight - 20)
transer.Y = (image.ActualHeight + Part_ImageContainer.ActualHeight - 20) / 2;
if (-transer.Y * 2 > (2 * scaler.ScaleY - 1) * image.ActualHeight + Part_ImageContainer.ActualHeight - 20)
transer.Y = -((scaler.ScaleY - 0.5) * image.ActualHeight + Part_ImageContainer.ActualHeight / 2 - 10);
}
/// <summary>
/// 圖像縮放
/// </summary>
/// <param name="point">當(dāng)前坐標(biāo)</param>
/// <param name="scale">縮放倍數(shù)</param>
public void ImageScale(Point point, double scale)
{
DoScale(point, scale);
}
}
3. 調(diào)用自定義控件
//控件引用
<Canvas
x:Name="canvas"
Grid.Row="1"
Background="Black">
<HalconDemo:ImageUserControl
x:Name="ImageUserControl"
Grid.Row="1"
Width="{Binding ElementName=canvas, Path=ActualWidth}"
Height="{Binding ElementName=canvas, Path=ActualHeight}"
MouseWheel="ImageUserControl_MouseWheel" />
</Canvas>
//鼠標(biāo)滾動(dòng)縮放
private void ImageUserControl_MouseWheel(object sender, MouseWheelEventArgs e)
{
var point = e.GetPosition(image);
double delta = e.Delta * 0.002;
ImageUserControl.ImageScale(point, delta);
}六、性能優(yōu)化要點(diǎn)
1. 大圖像的分級(jí)加載
直接加載一張 100MB 的 TIFF 圖像到 Image 控件,即使只是顯示縮略圖,也會(huì)消耗大量?jī)?nèi)存。優(yōu)化策略:
- 解碼尺寸控制:使用 BitmapImage.DecodePixelWidth 限制解碼后的像素尺寸,只加載當(dāng)前視圖需要的分辨率
- 虛擬化:對(duì)于超大圖像(如衛(wèi)星圖、病理切片),采用金字塔分層結(jié)構(gòu),根據(jù)當(dāng)前縮放級(jí)別加載對(duì)應(yīng)層級(jí)的瓦片
- 異步加載:圖像解碼在后臺(tái)線程完成,避免阻塞 UI
2. 渲染層優(yōu)化
- 設(shè)置 RenderOptions.BitmapScalingMode 為 NearestNeighbor(放大時(shí)保持像素銳利)或 Fant(縮小時(shí)平滑)
- 對(duì)靜態(tài)圖像啟用 BitmapCache,將變換后的結(jié)果緩存為位圖,減少每幀重繪
- 避免在變換過(guò)程中頻繁更新其他 UI 元素,減少布局傳遞
七、工程實(shí)踐
WPF 自定義 Image 控件的縮放與平移實(shí)現(xiàn),核心在于變換矩陣的精確控制與用戶(hù)交互的細(xì)膩映射:
- 架構(gòu)選擇:根據(jù)復(fù)用需求選擇附加行為、自定義控件或裝飾器方案
- 坐標(biāo)管理:理清 DIP、圖像坐標(biāo)、變換矩陣的層級(jí)關(guān)系
- 交互細(xì)節(jié):以光標(biāo)為中心的縮放、邊界約束、慣性動(dòng)畫(huà)提升體驗(yàn)
- 性能平衡:緩存策略、大圖像虛擬化、GPU 加速應(yīng)對(duì)復(fù)雜場(chǎng)景
- 工程化:MVVM 集成、狀態(tài)持久化、無(wú)障礙支持完善產(chǎn)品化
八、總結(jié)
WPF 中實(shí)現(xiàn)圖像縮放平移,表面上是變換矩陣的應(yīng)用,實(shí)質(zhì)上是對(duì)坐標(biāo)系、交互模型、渲染性能的系統(tǒng)把控。自定義控件的意義不在于技術(shù)復(fù)雜度,而在于將"圖像如何被觀看"這一交互細(xì)節(jié)封裝起來(lái),讓業(yè)務(wù)代碼保持清晰。
從以鼠標(biāo)為中心的錨點(diǎn)縮放,到邊界約束與慣性滾動(dòng)的手感調(diào)優(yōu),再到與大圖像、觸控、疊加元素的協(xié)同,每一個(gè)環(huán)節(jié)都直接影響最終用戶(hù)體驗(yàn)。在工業(yè)軟件、醫(yī)療影像、地理信息系統(tǒng)等專(zhuān)業(yè)領(lǐng)域,這些細(xì)節(jié)往往決定了產(chǎn)品的可用性邊界。
對(duì)于 WPF 開(kāi)發(fā)者而言,掌握自定義控件的開(kāi)發(fā)范式,不僅能解決圖像瀏覽這一具體問(wèn)題,更能建立起對(duì) WPF 渲染管線、輸入系統(tǒng)和依賴(lài)屬性的深層理解,為構(gòu)建更復(fù)雜的交互界面打下堅(jiān)實(shí)基礎(chǔ)。
以上就是在WPF中自定義Image控件實(shí)現(xiàn)圖像縮放與平移的詳細(xì)內(nèi)容,更多關(guān)于WPF Image圖像縮放與平移的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#串口通訊概念及簡(jiǎn)單的實(shí)現(xiàn)方法
這篇文章主要給大家介紹了關(guān)于C#串口通訊概念及簡(jiǎn)單的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者使用C#具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03
詳解C#如何利用TcpListener和TcpClient實(shí)現(xiàn)Tcp通訊
TcpListener 和 TcpClient 是在 System.Net.Sockets.Socket 類(lèi)的基礎(chǔ)上做的進(jìn)一步封裝,使用 GetStream 方法返回網(wǎng)絡(luò)流,下面我們就來(lái)詳細(xì)一下如何使用TcpListener和TcpClient實(shí)現(xiàn)Tcp通訊吧2023-12-12
C#實(shí)現(xiàn)跨進(jìn)程條件變量的示例代碼
C#提供的多進(jìn)程同步對(duì)象有互斥鎖和信號(hào)量,但是并沒(méi)有條件變量,雖然信號(hào)量條件變量一定程度可以等效,但是具體的使用還是會(huì)有區(qū)別,本文提供了一種條件變量的實(shí)現(xiàn)方法,可以用于進(jìn)程間的同步控制,需要的朋友可以參考下2024-07-07
C# 多線程學(xué)習(xí)之基礎(chǔ)入門(mén)
線程(英語(yǔ):thread)是操作系統(tǒng)能夠進(jìn)行運(yùn)算調(diào)度的最小單位。它被包含在進(jìn)程之中,是進(jìn)程中的實(shí)際運(yùn)作單位。今天小編就帶大家了解一下C#中的多線程,快來(lái)學(xué)習(xí)一下吧2021-12-12
C#中關(guān)于double.ToString()的用法
這篇文章主要介紹了C#中關(guān)于double.ToString()的用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02
C#實(shí)現(xiàn)讀取被進(jìn)程占用的文件實(shí)現(xiàn)方法
這篇文章主要介紹了C#實(shí)現(xiàn)讀取被進(jìn)程占用的文件實(shí)現(xiàn)方法,涉及C#進(jìn)程操作及文件讀取的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-08-08
C#的FileInfo類(lèi)實(shí)現(xiàn)文件操作實(shí)例
這篇文章主要介紹了C#的FileInfo類(lèi)實(shí)現(xiàn)文件操作實(shí)例,比較實(shí)用的功能,需要的朋友可以參考下2014-07-07
C#簡(jiǎn)易圖片格式轉(zhuǎn)換器實(shí)現(xiàn)方法
這篇文章主要介紹了C#簡(jiǎn)易圖片格式轉(zhuǎn)換器實(shí)現(xiàn)方法,涉及C#基于WinForm操作圖片的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11
C#基礎(chǔ)語(yǔ)法:as 運(yùn)算符使用實(shí)例
這篇文章主要介紹了C#基礎(chǔ)語(yǔ)法:as 運(yùn)算符使用實(shí)例,本文給出了類(lèi)、字符串、數(shù)字、浮點(diǎn)數(shù)、null等值的運(yùn)算實(shí)例,需要的朋友可以參考下2015-06-06

