Vítejte na blog.vyvojar.cz Přihlásit | Registrovat | Pomoc

C# Projects

Malá řešení pro velké projekty.
GraphicButton: .NET Compact Framework Control

'Nobody is perfect' zaznělo kdysi v jednom slavném americkém filmu. Platí to stále a zaplať pánbuh za to. Nedokonalosti jsou základem pro různorodost a krásu... asi bych měl přestat s filozofií a začít s tím, proč jsem tady dnes. Windows Mobile 5 a .NET Compact Framework 2.0. Tak jako každému vývojáři pro tuto platformu, i mně začal chybět ovládací prvek, který bych pojmenoval stejně jako mnozí přede mnou 'GraphicButton'. Tedy tlačítko, které je schopné zobrazovat místo textu bitmapu.

K vytvoření takového talčítka musíme udělat jen několik věcí:

  • vytvořit třídu která bude poděděna z bázové třídy Control
  • nadefinovat vlastnosti typu Image pro každý stav tlačítka
  • přepsat metodu OnPaint()

Toto jsou v zásadě tři věci, které je potřeba udělat. Zní to jednoduše a věřte, že to skutečně jednoduché je.

V první řadě tedy vytvoříme třídu GraphicButton, která bude poděděna z třídy Control.

   

    public class GraphicsButton : Control

    {

        /// <summary>

        /// Required designer variable.

        /// </summary>

        private System.ComponentModel.IContainer components = null;

 

        public GraphicsButton()

        {

            InitializeComponent();

        }

 

        /// <summary>

        /// Clean up any resources being used.

        /// </summary>

        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>

        protected override void Dispose(bool disposing)

        {

            if (disposing && (components != null))

            {

                components.Dispose();

            }

            base.Dispose(disposing);

        }

 

        #region Component Designer generated code

 

        /// <summary>

        /// Required method for Designer support - do not modify

        /// the contents of this method with the code editor.

        /// </summary>

        private void InitializeComponent()

        {

            this.SuspendLayout();

            //

            // GraphicsButton

            //

            this.Font = new System.Drawing.Font("Tahoma", 9F, System.Drawing.FontStyle.Bold);

            this.Size = new System.Drawing.Size(20, 20);

            this.ResumeLayout(false);

 

        }

        #endregion

    }


 

V podstatě stačí, abyste si vytvořili novou třídu poděděnou z bázové třídy Control a vše ostatní zařídí designer VS 2005. Nyní je zde pár vlastností, které této třídě nemůžeme upřít. Použití těchto vlastností je popsáno pod blokem kódu.      

 

        #region Private properties

        // Graphic environment properties

        private Rectangle clientRectangle;

        private Bitmap offScreenBitmap = null;

 

        // Internal status of the button

        private bool isPressed = false;

 

        // Transparent color

        private Color _TransparentColor = Color.Magenta;

        private System.Drawing.Imaging.ImageAttributes imageAttributes = null;

 

        // Button images

        private Image _Image = null;

        private Image _PressedImage = null;

        private Image _DisabledImage = null;

 

        #endregion

 

        #region Public properties

        public Image Image

        {

            get { return _Image; }

            set { _Image = value; }

        }

        public Image DisabledImage

        {

            get { return this._DisabledImage; }

            set { this._DisabledImage = value; }

        }

        public Image PressedImage

        {

            get { return this._PressedImage; }

            set { this._PressedImage = value; }

        }

 

        public Color TransparentColor

        {

            get { return _TransparentColor; }

            set { _TransparentColor = value; }

        }

        #endregion

 

Rectangle clientRectangle 
slouží k definování oblasti vykreslování našeho ovládacího prvku

Bitmap offScreenBitmap
do této bitmapy která je přítomna pouze v paměti nejprve vykreslíme ovládací prvek a pak jej celý vykreslíme na obrazovku. Je to jeden ze způsobů jak zabránit tomu, aby nám ovládací prvek poblikával, nebo aby bylo patrné jeho postupné vykreslování.

bool isPressed
stavová proměnná, která nás iformuje o tom, zda se talčítko nachází ve stavu stisknuto, či nikoli

Color _TransparentColor
zde definujme barvu, kterou si přejeme zobrazit jako transparentní. To nám umožní vytvářet tlačítka i jiných tvarů než jen klasická obdélníková či čtvercová.

ImageAttributes imageAttributes
tyto atributy budou použity při vytváření bitmapy právě pro zobrazení transparentní barvy.

Image _Image
základní bitmapa tlačítka

Image _PressedImage
bitmapa pro stisknuté tlačítko

Image _DisabledImage
bitmapa pro disablované tlačítko

Jako veřejné pak samozřejmě definujeme pouze ty vlastnosti, u kterých chceme aby byly viditelné mimo třídu.

Pokud máme takto připravenou třídu, potřebujeme pár pomocných funkcí. První z nich vytvoří objekty GDI+, jako jsou atributy s nastavenou transparentní barvou a oblast vykreslení našeho ovládacího prvku.

 

    private void CreateGDIObjects()

    {

        clientRectangle = new Rectangle(0, 0, this.ClientRectangle.Width,  

                                                                 this.ClientRectangle.Height);

        if (imageAttributes == null)

            imageAttributes = new System.Drawing.Imaging.ImageAttributes();

 

        imageAttributes.SetColorKey(this._TransparentColor, this._TransparentColor);

    }

 

 Další funkcí je pak funkce, která zajistí vytvoření tzv. 'offscreenBitmap'. Jak název napovídá, jedná se obitmapu, která není přímo na display zařízení, nýbrž je určená pro práci v paměti. Tato funkce se volá pouze v případě, že se změnila velikost ovládacího prvku tak, abysme si vytvořili 'paměťovou bitmapu' odpovídající velikosti.

 

    public static void CreateOffScreenBitmap(ref System.Drawing.Bitmap offScreenBitmap,  

                                             System.Windows.Forms.Control control)

    {

            // Only create if don't have one and the size hasn't changed

            if (offScreenBitmap == null || offScreenBitmap.Width != control.Width || 

                offScreenBitmap.Height != control.Height)

            {

 

                offScreenBitmap = new System.Drawing.Bitmap(control.Width, control.Height);

            }

    }

 

Následující 'overriden' metody zajišťují změny stavové proměnné a volání metody Refresh pro překreslení ovládacího prvku.

 

        protected override void OnMouseDown(MouseEventArgs e)

        {

            this.isPressed = true;

            this.Refresh();

            base.OnMouseDown(e);

 

        }

        protected override void OnMouseUp(MouseEventArgs e)

        {

            this.isPressed = false;

            this.Refresh();

            base.OnMouseUp(e);

        }

 

        protected override void OnResize(EventArgs e)

        {

            base.OnResize(e);

            this.Refresh();

        }

 

A dostáváme se k poslední, avšak nejdůležitější metodě a tou je metoda OnPaint. Tato metoda zajišťuje vykreslení našeho ovládacího prvku. Popis jednotlivých kroků naleznete přímo v kódu.

 

    protected override void OnPaint(PaintEventArgs pe) 

    {

        // Příprava prostřední - vytvořenéí GDI+ objektů a paměťové bitmapy

        CreateGDIObjects();

        CreateOffScreenBitmap(ref offScreenBitmap, this);

        Graphics offScreenGraphics = Graphics.FromImage(offScreenBitmap);

 

        // smazání oblasti - barvou pozadí prvku

        offScreenGraphics.Clear(this.BackColor);

 

        // Pokud není deklarována bitmapa, vypiš text - veškeré vykreslování se porovádí do

           paměťové bitmapy

        if (this._Image != null)

        {

            if (this.Enabled)

            {

                if (this.isPressed && this._PressedImage != null)

                    offScreenGraphics.DrawImage(this._PressedImage, clientRectangle, 0, 0, _PressedImage.Width, _PressedImage.Height, GraphicsUnit.Pixel, imageAttributes);

 

                if (!this.isPressed)

                    offScreenGraphics.DrawImage(this._Image, clientRectangle, 0, 0, _Image.Width, _Image.Height, GraphicsUnit.Pixel, imageAttributes);

            }

            else

            {

                if (this._DisabledImage != null)

                    offScreenGraphics.DrawImage(this._DisabledImage, clientRectangle, 0, 0, _DisabledImage.Width, _DisabledImage.Height, GraphicsUnit.Pixel, imageAttributes);

            }

        }

        else

        {

            SizeF stringSize = offScreenGraphics.MeasureString(this.Text, this.Font);

            float stringPosX = (clientRectangle.Width / 2) - (stringSize.Width / 2);

            float stringPosY = (clientRectangle.Height / 2) - (stringSize.Height / 2);

 

            offScreenGraphics.DrawString(this.Text, this.Font, new SolidBrush(this.ForeColor), stringPosX, stringPosY);

        }

 

        // vykresli paměťovou bitmapu na pozici ovládacího prvku

        pe.Graphics.DrawImage(offScreenBitmap, 0, 0);

    }

 

Poslední metodou kterou doporučuji implementovat při každé tvorbě vlastního ovládacího prvku je metoda OnPaintBackground. Tato metoda která neobsahuje v těle žádný kód zajistí, že váš ovládací prvek nebude poblikávat při každém překreslování formuláře na kterém je umístěn.

   

    protected override void OnPaintBackground(PaintEventArgs e) {  }

 

Nyní už jen stačí kód zkompilovat do knihovny a tu pak naimportovat do panelu nástrojů ve VS 2005.

Po pravdě řečeno jsem původně chtěl psát o tom, jak tuhle věc nedělat, ale přišlo mi hloupé začít bez toho, aniž bych to základní. Tedy příště se podíváme na to, jak se nepokoušet počítat stavové bitmapy pro vlastní ovládací prvek a naopak jak to by to jít mohlo. Vlastně budeme hledat řešení, jak se zbavit nutnosti vytvářet bitmapu pro každý stav jednu a zkusíme tlačítku podstrčit jen jednu a nechat ho dopočítat bitmapy pro stav 'pressed' i pro 'disabled'.

Pro dnes je to vše.

Mirek

 

Posted: 14. srpna 2007 11:28 by MirekE
Vedeno pod:

Komentář

endys place napsal:

GraphicButton: .NET Compact Framework Control - jak to nefunguje

# listopadu 25, 2011 23:47
Neregistrovaní uživatele nemužou přidávat komentáře.
Vyvojar.cz na prodej!