使用Stream讀寫WPF圖檔

by vivid 26. 三月 2014 10:10

.NET Magazine國際中文電子雜誌
者:許薰尹
稿:張智凱
文章編號:N140314602
出刊日期:2014/2014/3/26
開發工具:Visual Studio 2013 Ultimate
版本:.NET Framework 4.5.1

在WPF應用程式之中,有可能會使用到圖檔資料,有時我們會選擇將圖檔的二進位資料儲存在資料庫資料表的二進位欄位之中。在這篇文章中,將以Step-by-Step的方式設計一個WPF應用程式,利用ADO.NET將存在硬碟的JPG檔儲存到資料庫,並將資料庫中圖片的二進位資料讀出,還原成圖檔,並顯示在畫面上。

啟動Visual Studio 2013發環境。從Visual Studio 2013選單「File」-「New」-「Project」,選取「Templates」- 「Visual C#」 程式語言-「Windows」分類,建立一個「WPF Application」專案,設定Location指定檔案存放路徑,請參考下圖所示:

clip_image002

圖 1:建立一個「WPF Application」專案。

新增資料庫檔案

在「Solution Explorer」視窗,專案名稱上方按下滑鼠右鍵,從快捷選單中選取「Add」-「 New Item」,加入一個「Service-based Database」項目,命名為Database1.mdf檔案,然後按下「Add」按鈕,請參考下圖所示:

clip_image004

圖 2:加入一個「Service-based Database」項目。

預設Service-based Database類型的資料庫是一個LocalDB 類型的檔案資料庫。我們先在資料庫之中建立一個資料表來儲存圖片資料。使用Visual Studio 2013開發工具,在「Solution Explorer」視窗選取新加入的Database1.mdf檔案,按下滑鼠右鍵,從快捷選單中選取「Open」,連結並開啟資料庫,請參考下圖所示:

clip_image006

圖 3:連結並開啟資料庫。

下一步新增一個PhotoTable資料表。從Visual Studio 2013-「Server Explorer」視窗 -「Database1.mdf」-「Tables」,按滑鼠右鍵,從快捷選單中選取「Add New Table」,這樣就會開啟資料表設計畫面,請參考下圖所示:

clip_image008

圖 4:增一個PhotoTable資料表。

在資料表設計畫面新增兩個欄位,請參考下圖所示:

· Id欄位:Data Type 設為int型別。

· Photo欄位:Data Type 設為varbinary(Max)型別,並勾選「Allow Nulls」。

clip_image010

圖 5:新增兩個欄位。

選取資料表設計畫面上的Id欄位,利用「Poperties」視窗,設定「Identity Specification」-「 (Is Identity) 」屬性的值為「true」,這樣Id欄位的內容就會自動編流水號,請參考下圖所示:

clip_image012

圖 6:設定為自動編流水號欄位。

在資料表設計畫面下方「T-SQL」區段,預設會自動顯示建立資料表的T-SQL語法,我們可以修改語法,將資料表的名稱設定為「PhotoTable」,目前的T-SQL語法看起來如下:

CREATE TABLE [dbo].[PhotoTable]

(

[Id] INT NOT NULL PRIMARY KEY IDENTITY,

[Photo] VARBINARY(MAX) NULL

)

選取資料表設計畫面上方的「Update」按鈕,執行T-SQL語法建立資料表,請參考下圖所示:

clip_image014

圖 7:執行T-SQL語法建立資料表。

下一步會看到「Preview Database Updates」畫面,點選「Update Database」按鈕,就會直接執行T-SQL命令,請參考下圖所示:

clip_image016

圖 8:直接執行T-SQL命令。

完成之後「Server Explorer」視窗便會出現新增的資料表,請參考下圖所示:

clip_image018

圖 9:檢視新建立的資料表。

由於Windows Presentation Foundation沒有提供開啟檔案的對話盒,我們可以使用System.Windows.Forms組件提供的功能來達到選檔案動作。在「Solution Explorer」視窗,專案名稱上方按下滑鼠右鍵,從快捷選單中選取「Add」-「 Reference」,開啟「Reference Manager」對話盒,在右上方文字方塊輸入搜尋關鍵字「System.Windows.Forms」找到System.Windows.Forms組件之後,勾選此項目,然後按下「OK」按鈕,參考下圖所示:

clip_image020

圖 10:引用System.Windows.Forms組件。

設計使用者界面

接下來我們要來設計使用者界面,在MainWindow.xaml設計畫面中,加入ListBox、Button、Image與Label控制項,畫面大致安排如下,左方的方塊是ListBox;右方的方塊是Image,請參考下圖所示:

clip_image022

圖 11:設計WPF使用者界面。

先幫Labal取一個Name,選取設計畫面上的Label,從「Properties」視窗設定Name為「 lblFileName」,請參考下圖所示:

clip_image024

圖 12:設定Name。

按照相同的方式,利用「Properties」視窗設定Image的Name為「img」;ListBox 的Name為「lst」。完成後標籤看起來如下:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <ListBox x:Name="lst" HorizontalAlignment="Left" Height="248" Margin="35,38,0,0" VerticalAlignment="Top" Width="151"/>
        <Button Content="Load Image" HorizontalAlignment="Left" Margin="229,38,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click" />
        <Image x:Name="img" HorizontalAlignment="Left" Height="198" Margin="219,78,0,0" VerticalAlignment="Top" Width="250"/>
        <Label x:Name="lblFileName" Content="Label" HorizontalAlignment="Left" Margin="229,286,0,0" VerticalAlignment="Top"/>
    </Grid>
</Window>

 

雙擊「Load Image」按鈕產生事件處理常式,並進入到程式設計視窗,在檔案最上方,匯入System.Windows.Forms 、System.Data.SqlClient、System.Data、System.IO命名空間:

using System.Windows.Forms;

using System.Data.SqlClient;

using System.Data;

using System.IO;

在Button_Click事件處理常式之中,加入以下程式碼,利用OpenFileDialog開啟系統選取檔案的對話盒,讓使用者選取圖片,將圖片顯示在畫面Image控制項中,並利用ADO.NET將資料新增到資料庫:

private void Button_Click( object sender , RoutedEventArgs e ) {
      byte[ ] fileData = null;

      OpenFileDialog dlg = new OpenFileDialog( );
      dlg.InitialDirectory = "c:\\";
      dlg.Filter = "Image files (*.jpg)|*.jpg";

      string selectedFileName = null;
      if ( dlg.ShowDialog( ) == System.Windows.Forms.DialogResult.OK ) {

        selectedFileName = dlg.FileName;

        lblFileName.Content = selectedFileName;

        BitmapImage bitmap = new BitmapImage( );

        bitmap.BeginInit( );

        bitmap.UriSource = new Uri( selectedFileName );

        bitmap.EndInit( );

        img.Source = bitmap;

      }

      if ( selectedFileName == null ) {
        return;
      }


      FileStream fs = new FileStream( selectedFileName , FileMode.Open , FileAccess.Read );
      fileData = new byte[ fs.Length ];
      fs.Read( fileData , 0 , ( int ) fs.Length );
      fs.Close( );

      SqlConnection cn = new SqlConnection( @"Data Source=(LocalDB)\v11.0;AttachDbFilename=C:\temp\WpfApplication1\WpfApplication1\Database1.mdf;Integrated Security=True" );
      SqlCommand cmd = new SqlCommand( "INSERT INTO PhotoTable (photo) VALUES (@photo)" , cn );
      SqlParameter param1 = new SqlParameter( "@photo" , SqlDbType.Image );
      param1.Value = fileData;
      cmd.Parameters.Add( param1 );
      cn.Open( );
      cmd.ExecuteNonQuery( );
      cn.Close( );

    }

 

按F5執行這個程式,選取畫面中的「Load Image」按鈕,請參考下圖所示:

clip_image026

圖 13:測試執行結果。

在「Open」對話盒內,選取圖片檔案之後,按下「Open」按鈕,請參考下圖所示:

clip_image028

圖 14:選取圖檔。

圖片會直接呈現在畫面上,並直接儲存到資料庫,請參考下圖所示:

clip_image030

圖 15:圖片載入資料庫,並顯示在畫面上

檢視資料庫資料

使用Visual Studio 2013開發工具,在「Solution Explorer」視窗,選取Database1.mdf檔案,按下滑鼠右鍵,從快捷選單中選取「Open」,連結並開啟資料庫。在「Server Explorer」視窗,PhotoTable項目上面按右鍵,點選「Show Table Data」就可以看到資料庫新增的資料,請參考下圖所示:

clip_image032

圖 16:檢視資料庫資料。

載入資料庫資料

下一步我們希望在程式一開始執行時,就載入資料庫PhotoTable資料表中的所有資料,並將Id顯示在ListBox上,讓使用者可以點選id來查詢儲存在資料庫中的圖檔。開啟MainWindow.xaml「Design」設計畫面,選取「Window」,雙擊「Properties」視窗的閃電按鈕,切換到事件,雙擊Loaded項目,產生Window_Loaded事件處理常式,請參考下圖所示:

clip_image034

圖 17:產生Window_Loaded事件處理常式。

在Window_Loaded事件處理常式加入以下程式碼,查詢資料庫PhotoTable資料表中所有的id欄位值,並將結果利用資料繫結語法顯示在ListBox控制項上。

private void Window_Loaded( object sender , RoutedEventArgs e ) {
      SqlConnection cn = new SqlConnection( @"Data Source=(LocalDB)\v11.0;AttachDbFilename=C:\temp\WpfApplication1\WpfApplication1\Database1.mdf;Integrated Security=True" );
      SqlCommand cmd = new SqlCommand( "select id from PhotoTable" , cn );

      cn.Open( );
      SqlDataReader dr = cmd.ExecuteReader( );
      DataTable dt = new DataTable( );
      dt.Load( dr );

      lst.ItemsSource = dt.AsDataView( );
      lst.DisplayMemberPath = "id";
      dr.Close( );
      cn.Close( );
    }

 

開啟MainWindow.xaml「Design」設計畫面,選取ListBox控制項,按一下「Properties」視窗的閃電按鈕,切換到事件,雙擊SelectionChanged項目,產生Window_Loaded事件處理常式,並加入以下程式碼,利用SelectedItem取得選到的圖片編號,利用編號查詢出資料庫圖檔的二進位資料出來。

private void lst_SelectionChanged( object sender , SelectionChangedEventArgs e ) {
      int id = int.Parse( ( lst.SelectedItem as DataRowView )[ 0 ].ToString( ) );
      const int BufferSize = 1024;
      byte[ ] image = null;

      SqlConnection cn = new SqlConnection( @"Data Source=(LocalDB)\v11.0;AttachDbFilename=C:\temp\WpfApplication1\WpfApplication1\Database1.mdf;Integrated Security=True" );
      SqlCommand cmd = new SqlCommand( "select photo from PhotoTable where id=@id" , cn );
      cmd.Parameters.AddWithValue( "@id" , id );

      cn.Open( );

      using ( SqlDataReader dr = cmd.ExecuteReader( CommandBehavior.SequentialAccess ) )
      using ( MemoryStream imageStream = new MemoryStream( ) ) {
        long currentIndex = 0;
        byte[ ] buffer = new byte[ BufferSize ];
        int bytesRead;
        while ( dr.Read( ) ) {
          currentIndex = 0;
          bytesRead = ( int ) dr.GetBytes( 0 , currentIndex , buffer , 0 , BufferSize );
          while ( bytesRead != 0 ) {
            imageStream.Write( buffer , 0 , bytesRead );
            currentIndex += bytesRead;
            bytesRead =
            ( int ) dr.GetBytes( 0 , currentIndex , buffer , 0 , BufferSize );
          }
        }

        dr.Close( );
        cn.Close( );

        if ( imageStream.Length > 0 ) {
          image = imageStream.ToArray( );
        }
//solution 1
        string fn = System.IO.Path.GetTempFileName( );
        FileStream s = new FileStream( fn , FileMode.Create , FileAccess.Write );
        BinaryWriter b = new BinaryWriter( s );
        b.Write( image );
        b.Close( );
        s.Close( );


        BitmapImage bi = new BitmapImage( );
        bi.BeginInit( );
        bi.UriSource = new Uri( fn , UriKind.RelativeOrAbsolute );
        bi.EndInit( );
        img.Source = bi;

      }

 

當我們把二進位資料讀出之後,需要把二進位資料轉換成圖片,關於這部分的做法有許多種,上述範例的做法是先利用Path.GetTempFileName( )方法產生一個暫存的檔案名稱,將二進位圖檔資料從資料庫取出之後,直接寫入暫存檔,接著再利用BitmapImage顯示圖檔的內容。此範例的參考執行畫面如下,選取ListBox控制項中圖檔的編號,下方的Image控制項就會顯示圖檔,請參考下圖所示:

clip_image036

圖 18:ListBox控制項顯示圖檔的編號。

這個解法的缺點是,將圖檔存成檔案需要進行硬碟的I/O動作,會導致執行效能較差,若沒有需要將圖檔存檔的需求,我們可以利用Steam物件直接在記憶體中將二進位資料還原成圖檔就好。例如將上述「//solution 1」這行程式碼之後的程式碼改為:

 

//solution 2
        imageStream.Seek( 0 , SeekOrigin.Begin );

        BitmapImage bi = new BitmapImage( );
        bi.BeginInit( );
        bi.StreamSource = imageStream;
        bi.CacheOption = BitmapCacheOption.OnLoad;
        bi.EndInit( );

        img.BeginInit( );
        img.Source = bi;
        img.EndInit( );


 

另一個解法是使用JpegBitmapDecoder類別來處理,將上述「//solution 2」這行程式碼之後的程式碼更改如下,也可以得到相同的效果:

//solution 3

imageStream.Seek( 0 , SeekOrigin.Begin );

JpegBitmapDecoder d = new JpegBitmapDecoder( imageStream , BitmapCreateOptions.PreservePixelFormat , BitmapCacheOption.OnLoad );

img.BeginInit( );

img.Source = d.Frames[ 0 ];

img.EndInit( );

Tags:

.NET Magazine國際中文電子雜誌 | WPF | 許薰尹Vivid Hsu

評論 (64) -

cours de theatre paris
cours de theatre paris United States
2017/9/30 上午 09:32:57 #

I think this is a real great blog post.Much thanks again. Really Great.

回覆

can ho dic phoenix
can ho dic phoenix United States
2017/10/7 上午 12:10:02 #

Say, you got a nice blog.Thanks Again. Awesome.

回覆

tanie leki na potencje
tanie leki na potencje United States
2017/10/9 下午 03:41:44 #

Major thankies for the article post.Really looking forward to read more. Keep writing.

回覆

can ho bien vung tau
can ho bien vung tau United States
2017/10/9 下午 05:58:24 #

Major thanks for the blog article.Much thanks again. Keep writing.

回覆

solarmovie
solarmovie United States
2017/10/10 下午 07:46:49 #

I value the article.Really thank you! Fantastic.

回覆

Sterling Businesses Ltd
Sterling Businesses Ltd United States
2017/10/10 下午 09:53:38 #

Thanks so much for the article. Really Great.

回覆

pirater un compte facebook
pirater un compte facebook United States
2017/10/11 上午 12:12:08 #

Thank you ever so for you blog article.Much thanks again. Keep writing.

回覆

buy hacklink google
buy hacklink google United States
2017/10/12 下午 08:36:53 #

I appreciate you sharing this post.Really looking forward to read more. Awesome.

回覆

see post
see post United States
2017/10/14 下午 03:52:58 #

Muchos Gracias for your post. Really Great.

回覆

dragon city hacks
dragon city hacks United States
2017/10/15 下午 03:21:15 #

A round of applause for your article post. Awesome.

回覆

omega xl
omega xl United States
2017/10/15 下午 08:02:47 #

I think this is a real great post.Really thank you! Awesome.

回覆

read this
read this United States
2017/10/17 下午 02:40:04 #

I truly appreciate this blog. Cool.

回覆

sletrokor
sletrokor United States
2017/10/17 下午 08:11:25 #

Looking forward to reading more. Great article. Fantastic.

回覆

porn
porn United States
2017/10/19 上午 07:15:43 #

Say, you got a nice article post.Thanks Again. Cool.

回覆

see here
see here United States
2017/10/19 下午 06:09:03 #

I value the blog post.Thanks Again. Want more.

回覆

pure slim 1000 review
pure slim 1000 review United States
2017/10/20 上午 03:40:11 #

Say, you got a nice post.Really looking forward to read more. Great.

回覆

can ho son thinh
can ho son thinh United States
2017/10/21 上午 03:23:38 #

Very neat blog. Cool.

回覆

carte grise en ligne
carte grise en ligne United States
2017/10/21 上午 07:01:40 #

Thanks-a-mundo for the post.Thanks Again. Will read on...

回覆

website designing company in Delhi India
website designing company in Delhi India United States
2017/10/24 下午 12:21:21 #

Appreciate you sharing, great blog article. Will read on...

回覆

can ho vung tau
can ho vung tau United States
2017/10/28 上午 10:53:05 #

Very good article.Really looking forward to read more. Really Cool.

回覆

EZ Battery Reconditioning Review
EZ Battery Reconditioning Review United States
2017/10/30 上午 09:48:20 #

Thanks for sharing, this is a fantastic article.Really looking forward to read more. Cool.

回覆

pogoda plock
pogoda plock United States
2017/10/30 下午 05:32:04 #

Im grateful for the blog article.Really looking forward to read more. Really Cool.

回覆

scam
scam United States
2017/11/1 上午 10:01:12 #

Really informative blog article.Thanks Again. Really Great.

回覆

phenocal
phenocal United States
2017/11/1 下午 05:28:21 #

Muchos Gracias for your blog post.Really thank you! Cool.

回覆

phentaslim
phentaslim United States
2017/11/3 上午 11:04:48 #

I loved your article. Want more.

回覆

spinal stenosis surgery failed
spinal stenosis surgery failed United States
2017/11/15 上午 08:04:26 #

Thanks-a-mundo for the blog.Really thank you! Much obliged.

回覆

avocat criminel quebec
avocat criminel quebec United States
2017/11/16 下午 06:39:51 #

I appreciate you sharing this blog article.Really thank you! Keep writing.

回覆

Im obliged for the article.Thanks Again. Awesome.

回覆

ccproxy server
ccproxy server United States
2017/11/22 上午 03:20:11 #

Thanks for the blog post.Much thanks again. Want more.

回覆

fashion
fashion United States
2017/11/23 下午 10:08:31 #

Muchos Gracias for your post.Really thank you! Awesome.

回覆

Search Engine Optimisation New Zealand
Search Engine Optimisation New Zealand United States
2017/11/25 下午 07:06:02 #

A round of applause for your blog post. Fantastic.

回覆

Chad Boonswang and Jeffrey Goodman
Chad Boonswang and Jeffrey Goodman United States
2017/11/26 下午 06:15:24 #

Really informative post.Really looking forward to read more. Much obliged.

回覆

Chad Boonswang SEO
Chad Boonswang SEO United States
2017/11/27 上午 12:21:01 #

Thank you ever so for you post.Really looking forward to read more. Much obliged.

回覆

car wrecker scammer
car wrecker scammer United States
2017/11/29 下午 04:43:40 #

Great blog article. Much obliged.

回覆

The Avila 2
The Avila 2 United States
2017/11/29 下午 11:19:50 #

Really appreciate you sharing this post. Want more.

回覆

Get Business Credit
Get Business Credit United States
2017/12/1 上午 01:08:36 #

Major thankies for the post.Much thanks again. Want more.

回覆

porno
porno United States
2017/12/1 下午 04:49:50 #

I cannot thank you enough for the blog article.Thanks Again. Fantastic.

回覆

mika tan
mika tan United States
2017/12/5 上午 09:54:17 #

I really enjoy the post.Really looking forward to read more. Will read on...

回覆

I truly appreciate this blog. Much obliged.

回覆

Thank you ever so for you blog.Really thank you! Really Great.

回覆

website
website United States
2017/12/10 下午 07:30:48 #

Awesome article.Really thank you! Want more.

回覆

Cristen Samyn
Cristen Samyn United States
2017/12/14 上午 09:38:14 #

Very informative blog.Really looking forward to read more.

回覆

important link
important link United States
2017/12/14 下午 04:30:06 #

I really like and appreciate your article post.Really thank you! Will read on...

回覆

canon drivers
canon drivers United States
2017/12/16 下午 06:11:57 #

Im grateful for the post.Really thank you! Really Great.

回覆

green bean coffee
green bean coffee United States
2017/12/17 上午 12:29:36 #

Muchos Gracias for your blog article.Really thank you! Want more.

回覆

Great, thanks for sharing this article.Really looking forward to read more. Great.

回覆

Consulting
Consulting United States
2017/12/17 下午 05:51:59 #

I truly appreciate this blog post.Really thank you! Really Cool.

回覆

make money online
make money online United States
2017/12/20 下午 07:05:33 #

Enjoyed every bit of your blog article.Much thanks again.

回覆

driver canon
driver canon United States
2017/12/23 上午 08:00:45 #

wow, awesome article.Really looking forward to read more. Great.

回覆

Darwin Horan
Darwin Horan United States
2017/12/23 下午 06:28:19 #

A round of applause for your blog post.Much thanks again. Want more.

回覆

hp driver
hp driver United States
2017/12/25 下午 07:59:37 #

wow, awesome post.Really thank you! Cool.

回覆

Thanks so much for the blog article.Really thank you! Really Great.

回覆

&#216;&#162;&#219;Œ&#217;†&#217;‡ &#216;&#168;&#216;&#186;&#217;„
آینه بغل United States
2017/12/26 上午 07:50:23 #

I appreciate you sharing this blog.Much thanks again. Much obliged.

回覆

SOCCER HIGHLIGHTS
SOCCER HIGHLIGHTS United States
2017/12/26 下午 03:30:05 #

Im thankful for the article post.Really thank you! Much obliged.

回覆

canon drivers
canon drivers United States
2017/12/27 下午 04:06:36 #

Say, you got a nice article post.Really looking forward to read more. Really Cool.

回覆

drivers hp
drivers hp United States
2018/1/2 上午 06:51:13 #

Thanks a lot for the article.Really looking forward to read more. Really Great.

回覆

find out this here
find out this here United States
2018/1/2 下午 02:48:51 #

Appreciate you sharing, great article.Thanks Again. Really Great.

回覆

Muchos Gracias for your blog.Much thanks again. Awesome.

回覆

Epson drivers
Epson drivers United States
2018/1/3 上午 06:09:12 #

A big thank you for your article post.Thanks Again. Great.

回覆

bitcoin casino usa
bitcoin casino usa United States
2018/1/4 下午 04:42:18 #

Very informative blog post.Really thank you! Really Great.

回覆

hp driver
hp driver United States
2018/1/5 下午 04:21:48 #

Thanks-a-mundo for the blog article.Much thanks again. Will read on...

回覆

FBA
FBA United States
2018/1/6 上午 08:26:24 #

Very neat post. Much obliged.

回覆

web hosting
web hosting United States
2018/1/10 下午 01:58:42 #

Enjoyed every bit of your blog post.Much thanks again.

回覆

新增評論




  Country flag
biuquote
  • 評論
  • 線上預覽
Loading






NET Magazine國際中文電子雜誌

NET Magazine國際中文電子版雜誌,由恆逸資訊創立於2000,自發刊日起迄今已發行超過500篇.NET相關技術文章,擁有超過40000名註冊讀者群。NET Magazine國際中文電子版雜誌希望藉於電子雜誌與NET Developer達到共同學習與技術新知分享,歡迎每一位對.NET 技術有興趣的朋友們多多支持本雜誌,讓作者群們可以有持續性的動力繼續爬文。<請加入免費訂閱>

月分類Month List