1. 程式人生 > >C# 實現真正的透明控制元件(Windows桌面程式)

C# 實現真正的透明控制元件(Windows桌面程式)

由於上位機程式的需要,需要大量的異性控制元件,開始不以為然,心想隨著GDI到GDI+的升級,微軟應該會給NET打造了一套高效絢麗的繪圖方案,使用後才知道完全不是我以為的那麼回事.於是開始各種尋找資源,發現網上方法無非使用Web.Transparent作為背景,要麼就完全使用控制元件背後作為控制元件本身背景,然而這些無非都是掩耳盜鈴,真正意義上的透明完全沒有做到。

1.其中使用Web.Transparent的透明方案:

Label使用的的透明方案


如圖,這種透明系統參考的是控制元件所屬控制元件背景色,並不是透明,注意左邊的兩個紅色框,其實是兩個實現真正透明的控制元件

2.而通過背景截圖來作為自己背景圖的方案:

由於需要編寫太多程式碼,就不舉例了,看起來挺不錯,但也經不起推敲,畢竟很多時候,透明者需要蓋住一些與使用者互動的控制元件,可想而知,這種設計將是致命的。

3.因為Form有TransparencyKey屬性,很容易實現真正的透明,就想幹脆繼承Form來做控制元件,使用的時候,直接SetParent豈不美好,先不說資源耗費,使用者體驗.單單位置控制都夠嗆,其訊息佇列性質的不同,帶來的問題,完全不可取,而且在窗體在被指定到窗體後,其透明屬性竟然失效了,大寫的囧

難道就沒辦法了嗎??由於透明控制元件的實現尚未有好的方法,該想法被擱置了半年.

某個偶爾的機會,接觸到了Region物件,關於該物件詳細資訊請參閱微軟的官方文件,但是官方文件也只是機械的介紹了該物件成員而已,看不出什麼蹊蹺的。

我是這麼理解這個物件的,該物件告訴了系統這個控制元件需要佔用的介面UI資訊,而且這個資訊是可以隨意編輯的,,,,好知道這麼多就夠了,如果我把這個物件編輯成我要的形狀呢?是不是其他不需要的就消失了?懷著這樣疑問寫下程式碼:

Region rion = new Region(new Rectangle(0, 0, 20, 20));
Region = rion;
乖乖,控制元件竟然無論我怎麼繪製,在視窗上都只有 20×20 大了,儘管我拖得了很大

內心一陣狂喜,,似乎找到了希望

而情況也恰是如此,通過控制Region的資訊,完全可以控制控制元件需要現實和不現實(透明)的部分,

那麼問題來了,一些規則的透明還好辦,但是如果需要按某種特定無規則來異形呢..難道要一點點去算嗎?那也太不科學了,想到這裡,自然想到通過 Image(Bitemap)轉換成Region

首先看Region的建構函式


使用一個GraphicsPath 物件來構造,通過搜尋影象的每個畫素,來將需要顯示的區域新增的路徑畫布裡,是否可行呢!

由於主題關係,這裡延伸對GraphicsPath物件講解,不熟悉的朋友參考相關資料,謝謝。

根據這一想法,編寫轉換程式碼

/// <summary>
        /// 根據圖片計算GraphicsPath路徑(低效率)
        /// </summary>
        /// <param name="img">影象資源</param>
        /// <param name="TranColor">欲透明掉的顏色</param>
        /// <returns>路徑畫布,已過濾掉了透明顏色</returns>
        public static GraphicsPath ImageToGraphicsPath(Image imgx,Color TranColor)
        {
            if (imgx == null) return null;
            GraphicsPath g = new GraphicsPath(FillMode.Alternate);
            Bitmap bitmap = null;
            if (typeof(Bitmap) == imgx.GetType())
                bitmap = (Bitmap)imgx;
            else
                bitmap = new Bitmap(imgx);

            int ImWidth = bitmap.Width;
            int ImHeight = bitmap.Height;
            Color curColor;
            Rectangle curRect = new Rectangle();
            curRect.Height = 1;
            bool isTransRgn;

            for (int y = 0; y < ImHeight; y++)
            {
                isTransRgn = true;
                for (int x = 0; x < ImWidth; x++)
                {
                    curColor = bitmap.GetPixel(x, y);
                    if (curColor == TranColor || x == ImWidth - 1)//如果遇到透明色或行尾
                    {
                        if (isTransRgn == false)//退出有效區
                        {
                            curRect.Width = x - curRect.X;
                            g.AddRectangle(curRect);
                        }
                    }
                    else//非透明色
                    {
                        if (isTransRgn == true)//進入有效區
                        {
                            curRect.X = x;
                            curRect.Y = y;
                        }
                    }//if curColor
                    isTransRgn = curColor == TranColor;     
                }
            }
            return g;
        }
結果如圖:


達到目的...似乎任務完成了,,但是回頭想想不對呀!為什麼要通過GraphicsPath來中間轉換呢?為什麼不直接把座標填充到Region 裡呢,

因為Region 有一個方法是:


通過GraphicsPath來轉換,可能帶來其他資源類問題,直接使用這方法估計是不錯的選擇,於是增加函式:

/// <summary>
        /// 根據圖片計算Region路徑(低效率)
        /// </summary>
        /// <param name="img">影象資源</param>
        /// <param name="TranColor">欲透明掉的顏色</param>
        /// <returns>一個離散的路徑資訊</returns>
        public static Region ImageToRegion(Image imgx, Color TranColor)
        {
            if (imgx == null) return null;
            Region rRegion = new Region();
            rRegion.MakeEmpty();

            Bitmap bitmap = null;
            if (typeof(Bitmap) == imgx.GetType())
                bitmap = (Bitmap)imgx;
            else
                bitmap = new Bitmap(imgx);

            int ImWidth = bitmap.Width;
            int ImHeight = bitmap.Height;
            Color curColor;
            Rectangle curRect = new Rectangle();
            curRect.Height = 1;
            bool isTransRgn;

            for (int y = 0; y < ImHeight; y++)
            {
                isTransRgn = true;
                for (int x = 0; x < ImWidth; x++)
                {
                    curColor = bitmap.GetPixel(x, y);
                    if (curColor == TranColor || x == ImWidth - 1)//如果遇到透明色或行尾
                    {
                        if (isTransRgn == false)//退出有效區
                        {
                            curRect.Width = x - curRect.X;
                            rRegion.Union(curRect);
                        }
                    }
                    else//非透明色
                    {
                        if (isTransRgn == true)//進入有效區
                        {
                            curRect.X = x;
                            curRect.Y = y;
                        }
                    }//if curColor
                    isTransRgn = curColor == TranColor;
                }
            }
            return rRegion;
        }
結果同樣成功,就不在上圖..

然而似乎沒什麼問題了,但是細心的朋友可能感覺到了,這種方法讀取影象資源是一種極度效率低下的方法,這種直接

GetPixel怎麼都不像用在如此大量影象處理上的,,,如果用來展示動畫類,豈不夠嗆!

這裡不對C#指標,影象處理的知識擴充套件,不安全程式碼等知識擴充套件。儘管我們在接下來的函式使用到相關知識,如果有興趣的請自行參閱相關文件

修改相關函式提升效率,減少資源耗費

/*
         * 為了鼓勵學習研究精神,該函式,僅能用於本示例
         * 如果使用到其他專案,可能會存在錯誤
         * 如果您確實需要正確程式碼,請學習點陣圖相關資訊
        */
        /// <summary>
        /// 取得一個圖片中非透明色部分的區域。
        /// </summary>
        /// <param name="Picture">取其區域的圖片。</param>
        /// <param name="TransparentColor">透明色。</param>
        /// <returns>圖片中非透明色部分的區域</returns>
        public unsafe static Region ImageToRegionPx(Image Picture, Color TransparentColor)
        {
            if (Picture == null) return null;
            Region rgn = new Region();
            rgn.MakeEmpty();

            Bitmap bitmap = null;
            if (Picture.GetType() != typeof(Bitmap))
                bitmap = new Bitmap(Picture);
            else
                bitmap = (Bitmap)Picture;

            int width = bitmap.Width;
            int height = bitmap.Height;
            BitmapData bmData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            byte* p = (byte*)bmData.Scan0;
            int offset = bmData.Stride - width * 3;

            int p0, p1, p2;         // 記錄透明色
            p0 = TransparentColor.R;
            p1 = TransparentColor.G;
            p2 = TransparentColor.B;

            Rectangle curRect = new Rectangle();
            curRect.Height = 1;

            int start = -1;
            // 行座標 ( Y col ) 
            for (int Y = 0; Y < height; Y++)
            {
                // 列座標 ( X row ) 
                for (int X = 0; X < width; X++)
                {
                    if (start == -1 && (p[0] != p0 || p[1] != p1 || p[2] != p2))     //如果 之前的點沒有不透明 且 不透明 
                    {
                        start = X;                            //記錄這個點
                        curRect.X = X;
                        curRect.Y = Y;
                    }
                    else if (start > -1 && (p[0] == p0 && p[1] == p1 && p[2] == p2))      //如果 之前的點是不透明 且 透明
                    {
                        curRect.Width = X - curRect.X;
                        rgn.Union(curRect);
                        start = -1;
                    }

                    if (X == width - 1 && start > -1)        //如果 之前的點是不透明 且 是最後一個點
                    {
                        curRect.Width = X - curRect.X;
                        rgn.Union(curRect);
                        start = -1;
                    }
                    p += 3;//下一個記憶體地址
                }
                p += offset;
            }
            bitmap.UnlockBits(bmData);
            bitmap.Dispose();
            return rgn;
        }

到此,真正的C#桌面程式透明控制元件設計完成...

關於其他雙快取,半透明,請參考其他相關知識安靜

相關原始碼(VS2008)開發

http://download.csdn.net/detail/yangshengchuan/9749122

相關推薦

C# 實現真正透明控制元件(Windows桌面程式)

由於上位機程式的需要,需要大量的異性控制元件,開始不以為然,心想隨著GDI到GDI+的升級,微軟應該會給NET打造了一套高效絢麗的繪圖方案,使用後才知道完全不是我以為的那麼回事.於是開始各種尋找資源,發現網上方法無非使用Web.Transparent作為背景,要麼就完全使用

如何用VB實現透明控制元件

    本程式碼演示半透明控制元件的實現過程。如果是自定義控制元件,實現起來非常簡單,如果是系統控制元件,則要複雜一些。如果系統控制元件支援屬主畫,跟自定義控制元件思路完全是一樣的,只不過程式碼是寫在子類化的過程裡。      先建一個標準EXE工程,然後新增一個使用者控制

C#】透明控制元件實現

  轉載 http://wtunuiyf.blog.163.com/blog/static/18003200871951529580/   最近做的一個任務中需要用到透明控制元件,結果在網上找了好久,也沒有一個能真正實現的。當然,講透明form的到是很多,需要呼叫Window

C#如何用 Timer控制元件 實現倒計時

如圖,如何用C#實現Windows Forms上10分鐘倒計時?   第一步: 拖動“Timer”控制元件到Windows Forms中。   第二步: 雙擊“Timer”控制元件,程式碼如下: private void

C# WinForm 透明控制元件 PictureBox透明

1.要實現C# WinForm中的控制元件與背景的透明,可以通過設定控制元件的BackColor屬性為Transparent,同時設定其父控制元件。因為在C#中,控制元件的透明指對父窗體透明。如果不設定Parent屬性,那麼控制元件將只對Form透明,顯示的時候都會把For

C# 任意形狀按鈕控制元件 實現簡單實用

上圖的6變形按鈕,是用下面的程式碼是通過多邊形繪製出來的按鈕形狀,可以修改 Point[] 引數實現任意形狀的按鈕。 private void Form1_Load(object sender, EventArgs e)         {             in

C#自動實現Dll(OCX)控制元件註冊的兩種方法

      儘管MS為我們提供了豐富的.net framework庫,我們的程式C#開發帶來了極大的便利,但是有時候,一些特定功能的控制元件庫還是需要由第三方提供或是自己編寫。當需要用到Dll引用的時候,我們通常會通過“新增引用”的方式將它們納入到專案中,然後就可以像使用自

element.ui-Qt實現之時間控制元件

時分秒滾動控制元件 廢話少說,直入主題,今天我們來實現一個時分秒滾動控制元件,類似前端元件 element時間控制元件 Qt實現的時間控制元件效果,因為不會傳動態效果,所以沒有滾動效果。 注意本文只介紹了時分秒滾動區域的實現,只是當前日期元件的一部分,整個日期控制元件在後面的部落

MFC透明控制元件

void GetBKimageVisibleRgn(CString filepath, int width, int height, CRgn& Rgn) { //獲取圖片寬高 Bitmap *pimage = Bitmap::FromFile(filepath); pimage-&g

整理的C#螢幕截圖,控制元件截圖程式

程式碼基本從網上搜集而來,整理成以下檔案: 包括螢幕截圖(和螢幕上看到的一致); 以及控制元件截圖(只要該控制元件在本視窗內顯示完全且不被其他控制元件遮擋就可正確截圖)  using System;   using System

Android實現顏色選擇控制元件

Android實現顏色選擇控制元件 一、實現效果 二、使用方式 三、設計目標 四、程式碼分析 1. 控制元件佈局結構 2. 定義`CircleColorButton` 3. 核心屬性

c#窗體學習——常用控制元件介紹(一)

偷懶,轉自若雲流風,原文:https://blog.csdn.net/ruoyunliufeng/article/details/72874691  一.常用控制元件 Lable標籤→僅顯示文字; TextBox文字控制元件→文字框; Button按鈕控制元件

(轉)C# 自定義使用者控制元件

C# 自定義使用者控制元件   轉:https://blog.csdn.net/xiongxuanwen/article/details/2605109 本例是製作一個簡單的自定義控制元件,然後用一個簡單的測試程式,對於初學者來說,本例子比較簡單,只能起到拋石引玉的效

C#如何在各類控制元件中輸入/輸出資料

textBox 控制元件 輸入:只需要在textBox框中直接輸入資料即可,如圖 輸出:程式碼實現對textBox框的text屬性賦值 textBox.text="這裡輸入要輸出的值"** 得出結果如圖 label 控制元件 輸出:可以直接在label控制元件的text

Unity使用UGUI實現某個UI控制元件進入某個區域後按格移動

最近專案需求,需要實現類似於暗黑破壞神揹包那樣的按格存放功能,在此之前先實現物體按格進入揹包,然後觸發相應的事件,減少後續的歸位操作。 圖片控制程式碼: using System.Collections; using System.Collections.Generic; using Uni

Ext 6.5.3 classic版本,自定義實現togglefield開關控制元件

1,在Ext 6.5.3的classic版中沒有提供開關控制元件,參照modern版中 togglefield開關的實現,繼承滑動器(sliderfield),自定義一個開關按鈕。支援value繫結和點選切換狀態以及表單提交。 2,完成後效果如圖:   3, js程式碼如下:

C#】WPF音樂控制元件

一、使用SoundPlayer SoundPlayer 只能支援WAV格式的檔案 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.

WinForm中如何實現在容器控制元件中嵌入form窗體(panel與子窗體)

今天在做專案時候遇到一個問題,窗體分為左右兩部分,要求在左邊欄點選按鈕時,右邊動態載入窗體最後想到用panel實現,經歷幾次失敗,並查詢資料後,終於搞定說明:如果多次切換需加入 panel.clear();清空原有panel內容環境:C# VS2008 方法一:通過給panel載入子元素實現

C#根據Form大小控制元件自動更改大小(自適應)

長話短說,直接上程式碼  C#的 1.軟體Form中加入ResizeBegin 或ResizeEnd 事件,或下面兩行加入Form_Load裡面 this.ResizeBegin += new System.EventHandler(this.Form1_ResizeBe

基於MFC的CListCtrl實現虛擬列表控制元件

1. 在建立的工程的對話方塊裡拖入一個ListCtrl控制元件,然後將控制元件的屬性View改成“Report”,“所有者資料”改成True。如下圖所示: 2. 在我們的程式中需要在ListCtrl的父視窗的類裡面響應CListCtrl的跟虛擬控制元件相關的幾個訊息事件