首先我必須聲明一下, 以下我要介紹的方法雖然並不是百無一用, 而且看起來還算酷炫, 但是我必須承認這可以算是「無用技術」的一項。因為讓背景圖片不斷流動除了無端耗費 CPU 資源之外, 如果設定得不好, 還會讓你的使用者眼花瞭亂, 甚至可能會產生暈車的症狀, 所以小朋友不要亂學, 叔叔有練過才敢這樣做...
首先我必須聲明一下, 以下我要介紹的方法雖然並不是百無一用, 而且看起來還算酷炫, 但是我必須承認這可以算是「無用技術」的一項。因為讓背景圖片不斷流動除了無端耗費 CPU 資源之外, 如果設定得不好, 還會讓你的使用者眼花瞭亂, 甚至可能會產生暈車的症狀 (更嚴重者可能會躁鬱症發作, 甚至抓狂; 別說我沒警告過你), 所以小朋友不要亂學, 叔叔有練過才敢這樣做。
會想讓背景流動, 主要的原因只是因為我希望找出一個方法, 讓我可以立刻看出我的程式是否處於某種特定的模式之下。至於會挑選讓背景流動這個方法, 則是因為我以為它應該是最簡單的方法(簡單到我甚至不需要多拉一個 Label 出來顯然狀態)。沒想到一動手做之後, 才發現這原來一點都不簡單。
技術上的困難點主要有兩個:
- 如果在 Form 的 OnPaint 事件中對 e.Graphics 畫圖, 這原來是很簡單的一件事情; 然而問題來了! 因為以這種方法畫出來的流動的圖, 對於在 Form 裡面的各個控制項而言, 即使你把它們的 BackColor 設定為 Transparent 或 Empty, 它還是會採用 Form 本身的 BackColor/BackImage 做為它的 BackColor/BackImage (而不是我們要的流動的圖)。這會使得這些控制項的背景區域像是一塊會遮住背景的色板, 看起來就好像是河裡面的沙洲一樣。
- 在 C# 中 Image 和 Bitmap 之間可以做明確的轉換, 但是 Graphics 和 Bitmap 之間卻沒有明確的轉換或匯出功能。我從各種來源中搜尋了很久, 都找不到二者之間的轉換方法。不過最後也是因為找到解決的方法, 所以才會有這篇文章的出現。
解法的關鍵在於, 你不能將任何 e.Graphics 直接轉換成 Bitmap, 但是你可以從 Bitmap 建立 Graphics 物件, 然後再透過 Graphics 去變更 Bitmap (儘管我寫 C# 已將近三年, 這裡的用法仍然是讓我不習慣的地方; 不需要標示 Pointer, 甚至不必透過 ref 或 out 指示詞, 在畫布物件上畫一畫竟然會連原圖形物件都被一併影響到 )。如果你看不懂的話, 沒關係, 我們直接看程式:
using System.Drawing; ... int offset = 0; private void tmr_Tick(object sender, EventArgs e) { Bitmap backImage = Properties.Resources.Stripe; TextureBrush tb = new TextureBrush(backImage); offset = offset % backImage.Width; tb.TranslateTransform(offset*-1, 0); // 把 *-1 拿掉就變成反方向流動 offset ++; Graphics g = Graphics.FromImage(backImage); // 這道指令是本問題的核心解法 g.FillRectangle(tb, 0, 0, backImage.Width, backImage.Height); // 這行指令其實已經變更了原圖 this.BackgroundImage = backImage; this.BackgroundImageLayout = ImageLayout.Tile; g.Dispose(); }
程式中 tmr 是我建立的 Timer, 其 Interval 設定為 100 (你可以自由變動這個值, 最小值為 1 <- 如果你想讓你的使用者暈車到吐出來的話), Properties.Resources.Stripe 則是我在程式的 Properties 中放入的圖片, 你可以自己載入 (不建議使用太大的圖, 否則系統效能會非常急遽的降低; 我自己用的圖是一個只有 16 像素寬的無接縫小圖)。此外, 我們可以讓圖片左右流動, 當然也可以讓它上下流動, 或者上下左右任一方向流動; 視需要修改即可。如果你真的沒事幹的話, 或許你會更進一步, 讓它忽上忽下或忽左忽右, 或是左右攞動等等; 但我勸你別無聊了, 這樣的程式會把使用者搞瘋的! 因為我發現自從寫了這個程式之後, 自己感覺上似乎已經有點不太正常的樣子。 ;-)