\

Školení Návrhové vzory, OOP a UML


 Wednesday, 18 November 2009
Vynucení si překreslení celé obrazovky v Compact .Net Frameworku

Při snaze kreslit grafické objekty na celou obrazovku PDA, a ne pouze na vlastní formulář, se můžete velmi často setkat s dotazem, jak zajistím, že předchozí nakreslené dílko, rozprostřené většinou přes vícero formulářů a mimo naši přímou kontrolu, smažu před vykreslením dalšího dílka. Následující příklad je reakcí na takový dotaz. Za “celou obrazovkou” budeme v článku považovat grafický kontext vrácený voláním metody GetDC s argumentem NULL. Po celé obrazovce PDA je postupně vykreslován kruh (s velkou fantazií míč) pohybující se z levé strany displeje na pravou. Před vykreslením “míče” na další pozici musí být míč vykreslený v předcházejícím kroku smazán. Následující kód je ukázkou použití “brutální síly”, protože žádné slečinkovské, sexy ani elegantní konstrukce s překreslováním pouze části obrazovky k výsledku nevedly. Hlavní trik, ke kterému jsem dospěl po sundání bílých vývojářských rukaviček, spočívá v rekurzivním vynucení si překreslení všech oken v metodě DoWork s využitím přímého volání mnoha nativních API funkcí, které mají paradoxně v aplikacích cílených na Microsoftem macešsky spravovaný a rozvíjený Compact .Net Framework (i ve verzi. 3.5) stále privilegovanou pozici.

 using System;
 using System.ComponentModel;
 using System.Drawing;
 using System.Threading;
 using System.Windows.Forms;

namespace RedrawScreenTest
{
    

    partial class Form1 : Form
    {

        #region constants
        
        public const int POSITION_INCREMENT = 10;
        public const int MAX_POSITION = 200;
        public const int MAX_THREAD_WAIT = 5000;
        public const int CIRCLE_RADIUS = 30;

        #endregion constants
        
        #region delegates
        public delegate void InvokeDelegate();
        #endregion delegates

        #region Properties
            public IntPtr WindowsHDC { get; set; }        
            public Thread Worker { get; set; }        
            public bool ShouldWork { get; set; }        
            public bool IsWorking { get; set; }
        #endregion Properties


         #region Constructors
            public Form1()
        {
            InitializeComponent();  
            WindowsHDC = IntPtr.Zero;
            ShouldWork = false;
            IsWorking = false;
        }
       #endregion Constructors

            #region methods
            public void DoWork()
        {
            IsWorking = true;
            int x = 0;
            int y = 0;
            

            bool firstDraw = true;


            while (ShouldWork)
            {
                WindowsHDC = ApiWrapper.GetDC(IntPtr.Zero);

                if (WindowsHDC == IntPtr.Zero)
                {
                    break;
                }

                


                if (!firstDraw)
                {

                    Invoke((InvokeDelegate) (() =>
                                                 {

                                                     IntPtr hwnd = ApiWrapper.GetForegroundWindow();

                                                     hwnd = ApiWrapper.GetWindow(hwnd, (int)ApiWrapper.GWConstants.GW_HWNDFIRST);


                                                     while (hwnd != IntPtr.Zero)
                                                     {

                                                         ApiWrapper.RedrawWindow(hwnd, IntPtr.Zero, IntPtr.Zero,
                                                                      ApiWrapper.RDW_ERASE | ApiWrapper.RDW_INVALIDATE | ApiWrapper.RDW_ALLCHILDREN);

                                                         
                                                         EnumChild(hwnd, 0);

                                                         hwnd = ApiWrapper.GetWindow(hwnd, (int)ApiWrapper.GWConstants.GW_HWNDNEXT);

                                                     }
                                                 }));
                }
                else
                {
                      
                       firstDraw = false;       

                }



                using (Graphics g = Graphics.FromHdc(WindowsHDC))
                using (Brush b = new SolidBrush(Color.Yellow))
                {
                    g.FillEllipse(b, new Rectangle(x, y, CIRCLE_RADIUS, CIRCLE_RADIUS));                    
                    
                }
                    
                
                
                    ApiWrapper.ReleaseDC(IntPtr.Zero, WindowsHDC);
                    

                    Thread.Sleep(1000);

                    x += POSITION_INCREMENT;
                    y += POSITION_INCREMENT;

                    if (x > MAX_POSITION || y > MAX_POSITION)
                    {
                        x = 0;
                        y = 0;
                    }
                }

                IsWorking = false;
            }
        
        private void EnumChild(IntPtr hwnd, int level)
        {

            if ((hwnd == IntPtr.Zero))
            {
                return;
            }

            IntPtr childHwnd = ApiWrapper.GetWindow(hwnd, (int)ApiWrapper.GWConstants.GW_CHILD);            

            while (childHwnd != IntPtr.Zero)
            {
                EnumChild(childHwnd, level + 1);
                ApiWrapper.RedrawWindow(childHwnd, IntPtr.Zero, IntPtr.Zero,
                          ApiWrapper.RDW_ERASE | ApiWrapper.RDW_INVALIDATE | ApiWrapper.RDW_ALLCHILDREN);

                childHwnd = ApiWrapper.GetWindow(childHwnd, (int)ApiWrapper.GWConstants.GW_HWNDNEXT);

            }
            

            

        }
        private void Form1_Load(object sender, EventArgs e)
        {
            
        }

        
        private void Form1_Closing(object sender, CancelEventArgs e)
        {
            buttonStop_Click(this, new EventArgs());


            if (WindowsHDC != IntPtr.Zero)
            {
                ApiWrapper.ReleaseDC(IntPtr.Zero, WindowsHDC);
            }
                
        }

        private void buttonStart_Click(object sender, EventArgs e)
        {
            if (IsWorking)
            {
                return;
            }
            
            ShouldWork = true;
            Worker = new Thread(DoWork);
            Worker.Start();
        }

        private void buttonStop_Click(object sender, EventArgs e)
        {
            if (!IsWorking)
            {
                return;
            }
            
            ShouldWork = false;
            Worker.Join(MAX_THREAD_WAIT);
        }

           

        private void Form1_Paint(object sender, PaintEventArgs e)
        {

        }

        #endregion methods
    }   
  }  

Zde jsou potřebné deklarace API funkcí, konstant a struktur


using System;
using System.Drawing;
using System.Runtime.InteropServices;

namespace RedrawScreenTest
{
    class ApiWrapper
    {
        [DllImport("coredll.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool RedrawWindow(IntPtr hWnd, IntPtr lprcUpdate,
                IntPtr hrgnUpdate, uint flags);

        public const int RDW_INVALIDATE = 0x0001;
        const int RDW_INTERNALPAINT = 0x0002;
        public const int RDW_ERASE = 0x0004;

        const int RDW_VALIDATE = 0x0008;
        const int RDW_NOINTERNALPAINT = 0x0010;
        const int RDW_NOERASE = 0x0020;

        const int RDW_NOCHILDREN = 0x0040;
        public const int RDW_ALLCHILDREN = 0x0080;

        const int RDW_UPDATENOW = 0x0100;
        const int RDW_ERASENOW = 0x0200;

        const int RDW_FRAME = 0x0400;
        const int RDW_NOFRAME = 0x0800;



        [DllImport("coredll.dll", SetLastError = true)]
        public static extern IntPtr GetWindow(IntPtr hWnd, int uCmd);

        public enum GWConstants
        {
            GW_HWNDFIRST = 0,
            GW_HWNDLAST = 1,
            GW_HWNDNEXT = 2,
            GW_HWNDPREV = 3,
            GW_OWNER = 4,
            GW_CHILD = 5,
            GW_ENABLEDPOPUP = 6
        }



        [DllImport("coredll.dll")]
        public static extern IntPtr GetForegroundWindow();

        [DllImport("coredll.dll", EntryPoint = "GetWindowDC")]
        public static extern IntPtr GetDC(IntPtr ptr);
        
        [DllImport("coredll.dll", EntryPoint = "ReleaseDC")]
        public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDc);
        
        [DllImport("coredll.dll", EntryPoint = "GetDesktopWindow")]
        public static extern IntPtr GetDesktopWindow();
        
        [DllImport("coredll.dll", EntryPoint = "UpdateWindow")]
        public static extern bool UpdateWindow(IntPtr hWnd);
        
        [DllImport("coredll.dll", EntryPoint = "UpdateWindow")]
        public static extern int SendMessage(IntPtr hWnd, uint msg, int wparam, int lparam);
        
        [DllImport("coredll.dll")]
        public extern static void InvalidateRect(IntPtr handle, Rectangle dummy, bool erase);

        [DllImport("coredll.dll", EntryPoint = "FindWindowW", SetLastError = true)]
        public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);



    }
}


Wednesday, 18 November 2009 15:02:51 (Central Europe Standard Time, UTC+01:00)       
Comments [0]  Compact .Net Framework