MobileDrawing Continuous Lines in WinCE - Etch-A-Sketch Revisited

Drawing Continuous Lines in WinCE – Etch-A-Sketch Revisited

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Here’s an improved version of EtchASketch, called Etch2. You may recall that EtchASketch drew dashed lines in response to the user’s commands, leaving a visible space in between line segments (in Porting Graphical Apps to Windows CE, Part 2.) The dashed lines weren’t because of the pen-they were artifacts of inflating the invalid rectangle. You’ll notice that the lines drawn by Etch2 are solid rather than dashed like those in EtchASketch. We accomplish this by taking a new approach to the invalidation and painting process in Etch2. The one significant difference between EtchASketch and Etch2, and its what you don’t see in the message switch code:

Message Switch Code for Etch2

	switch (message) 
    {
        case WM_COMMAND:
            wmId    = LOWORD(wParam); 
            wmEvent = HIWORD(wParam); 
            // Parse the menu selections:
            switch (wmId)
            {
                case IDM_FILE_LEFT:
                   //set the endpoint of the line segment
                   ptLineSeg[1].x = ptLineSeg[0].x - iSegLen;
                   ptLineSeg[1].y = ptLineSeg[0].y;
                   DrawLines( hWnd );
                   break;

                case IDM_FILE_RIGHT:
                    //set the endpoint of the line segment
                   ptLineSeg[1].x = ptLineSeg[0].x + iSegLen;
                   ptLineSeg[1].y = ptLineSeg[0].y;
                   DrawLines( hWnd );
                   break;

                case IDM_FILE_UP:
                  //set the endpoint of the line segment
                   ptLineSeg[1].x = ptLineSeg[0].x;
                   ptLineSeg[1].y = ptLineSeg[0].y - iSegLen;
                   DrawLines( hWnd );
                   break;

                case IDM_FILE_DOWN:
                   //set the endpoint of the line segment
                   ptLineSeg[1].x = ptLineSeg[0].x;
                   ptLineSeg[1].y = ptLineSeg[0].y + iSegLen;
                   DrawLines( hWnd );
                   break;

                case IDM_FILE_EXIT:
                   DestroyWindow(hWnd);
                   break;

                default:
                   return DefWindowProc(hWnd, message, wParam, lParam);
            }
            break;

        case WM_CREATE:
            hwndCB = CommandBar_Create(hInst, hWnd, 1);            
            CommandBar_InsertMenubar(hwndCB, hInst, IDM_MENU, 0);
            CommandBar_AddAdornments(hwndCB, 0, 0);
            break;
        case WM_DESTROY:
            CommandBar_Destroy(hwndCB);
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
   }
   return 0;
}

Etch2 omits processing of the WM_PAINT message. This may seem strange, but it has a couple of important advantages. First, it ensures that when a drop menu or dialog control has covered part of the client area, the affected screen real estate will get default invalidation and repainting. Second, it is completely reliable and adds nothing to your application’s code footprint. For our application specific drawing in Etch2, we don’t use the WM_PAINT message at all, and therefore don’t have to invalidate anything in order to draw or to keep existing drawing in the client area from being erased.

All we do in response to the user’s command to add a line segment is call the DrawLines function:

 void DrawLines( HWND hWnd )
{
    HDC     hdc;
    RECT    rcClientPoints;

      hdc = GetDC(hWnd);

    //make a copy of the points
    CopyRect( &rcClientPoints, (LPRECT)&ptLineSeg );

    //convert to client area coordinates
    MapWindowPoints( hWnd, HWND_DESKTOP,(LPPOINT)&rcClientPoints, 2 );
    Polyline(hdc, (LPPOINT)&rcClientPoints, 2 );

    ReleaseDC(hWnd, hdc);
    
   //update the starting point of the next 
   //line segment
   ptLineSeg[0].x = ptLineSeg[1].x;
   ptLineSeg[0].y = ptLineSeg[1].y;

}

DrawLines() uses the global array ptLineSegment to calculate where and how long to draw the new segment. ptLineSegment[0] gives the x and y coordinates of the starting point, and ptLineSegment[1] gives the end point. The end point is calculated by adding or subtracting iSegLen to a starting point’s x or y, depending on which way the user wants the line segment to move. iSegLen is a global variable that is initialized to 1/25 of the x dimension of the screen in the InitInstance() function. We copy the ptLineSegment point array to rcClientPoints using CopyRect() before calling MapWindowToPoints() in order to avoid type casting the global array and using it as a parameter to the point mapping function.

Last but not least, notice that we use Polyline() in our DrawLines() function. Polygon() and Polyline() are both supported by CE, and are essentially the same, except that Polygon() automatically draws a closing line segment between the last point on the last line and the first point on the first line. You won’t have to add closing line segments to existing Polygon() coordinates.

There are just a couple of points to make about line drawing before moving on. You may have noticed that I didn’t mention using CreateDC(), SaveDC(), or RestoreDC(), all of which are supported by Win CE. Here’s why. CE maintains a small pool of DCs, and you get access to one of these when you call GetDC(). When you call ReleaseDC() its returned to the pool and available to any other process. CreateDC allocates memory and builds you a DC. Unless you have a compelling reason for creating your own DC, this is wasteful. Likewise, SaveDC() pushes your DC onto a stack, where it unproductively takes up space until you need it again. Again, this is probably wasteful.

Porting Tip: To draw incrementally, without the client area being erased, write an application specific drawing ( or painting ) function that uses GetDC() and ReleaseDC.

Source Code

Download: Etch.zip (7 kb)

Looking Ahead:

Next we’ll explore the use of bitmaps, stating with device dependent bitmaps. We’ll examine the effects of ROP codes, color depth, and size translations under CE.

About the Author

Nancy Nicolaisen is a software engineer who has designed and implemented highly modular Windows CE products that include features such as full remote diagnostics, CE-side data compression, dynamically constructed user interface, automatic screen size detection, entry time data validation.

In addition to writing for Developer.com, she has written several books including Making Win 32 Applications Mobile. She has also written numerous articles on programming technology for national publications including Dr. Dobbs, BYTE Magazine, Microsoft Systems Journal, PC Magazine; Computer Shopper, Windows Sources and Databased Advisor.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories