December 6, 2012

C# - WebBrowser Control

1. Introduction to WebBrowser Control

The "Dotnet WebBrowser Control" provides the capability of browsing the web pages to your application. You can also display your own HTML content through the web browser control. That means you can generate all fancy report and display that in a web browser control. In this article, I will show how can you design simple browsing window (When you are browsing using this sample, Do not provide any personal information. This is just a sample that demonstrates the use of WebBrowser control ). At the end, I will show how do you display the user content and hook button click in the user content to the c# methods.

2. About the sample

The screen shot of the sample is shown below:

The user will type the web address in the Address Bar. To navigate to the typed web address, they should click the Go button. The stop and reload to the same job as every browser is doing today. When the sample is navigating and consuming the web content, the progress bar shows how much the current load content is progressed. The information bar shows what part of the page content is currently getting loaded. When you run the sample you may notice for fully downloading a page, there may be multiple navigation start and end and each denotes that to fully construct the page the sample download the content from the different location. OK, let us start developing the sample.

3. Designing the Web Browser using Visual Studio

The browser we are going to create is composed of ToolStrip, StatusStrip, and the WebBrowser control. First, the toolbar is created and then the required controls on the toolbars are placed. This step is shown in the below video:

Video 1: Setting up the toolbar
In the video, you will not see the buttons for user content. We will add that later. Next, we should add the StatusStrip control to the sample application form. Once the status control is added, a Progress bar and a label for displaying the information are placed in the StatusStrip control. This is shown in the below video:
 Video 2: Setting up the status bar.
Finally, we added the WebBrowser control to our sample. Note that when you place the WebBrowser control on the form, it automatically occupies the entire free area in which it is dropped.
Video 3: Place the WebBrowser control.
Tags for coding: //WEB_BR, //WEB_CS
WEB_BR: Coding shows some basic WebBrowser supports
WEB_CS: Coding shows displaying the user content and handling the control events from those scripts

4. WebBrowser basic actions

WebBrowser control provides rich functionalities and in this sample, we will see only the basic actions like loading a web page, stopping the current page load and refreshing the page load.
1) In the Go button click handler, we take the web address provided in the Address box and navigate to that page. In the meantime, we display the status text "Loading". The Navigate method will absorb the content from the web address specified by the user and displays that in WebBrowser control.
private void btnGo_Click(object sender, EventArgs e)
    //Web_BR 01: Navigate to the URL
    Status_Information.Text = "Loading...";
2) Canceling the page load as well as refreshing the loaded/partly loaded page content are straight forward. We should simply call the corresponding methods in the WebBrowser control to do so. Below are the handler functions for Stop and Reload buttons
private void btnStop_Click(object sender, EventArgs e)
    //Web_BR 02: Stop Loading the Page

private void btnReload_Click(object sender, EventArgs e)
    //Web_BR 03: Refresh the Page

5. Designing Status Information for WebBrowser Control

When we load a web page on a browser; say IE; to fully load the document, the browser has to navigate to different web locations. Say, for example, the website having a lot of YouTube videos and review about the video (Page’s own content is review) also contains a FaceBook like a page on the same page. When we browse to that website, the browser will load the review content of the video from the page itself, then it navigates to the YouTube and FaceBook to get other required information. So, to load a document completely, the browser should navigate to three web addresses even though the user specifies only one address in the address bar of the sample browser.
When the browser is navigating to a different site for loading some portion of the document, we will get two events namely "Navigating Event" and "Navigated Event". The first event says that the navigation is about to start and the second event says, navigation is completed. When the web page document is fully loaded, we get the "DocumentCompleted Event".
We handled all the three events to display the status information:
private void frmWebBrowser_Load(object sender, EventArgs e)
    //Web_BR 04: Hide the visibility of the status bar
    Status_PB.Visible = false;

private void Browser_Navigating(object sender, WebBrowserNavigatingEventArgs e)
    //Web_BR 06: Provide some information about the current navaigation
    Status_PB.Visible = true;
    Status_Information.Text = "Navigating " + e.Url.ToString() + " for " + e.TargetFrameName;

private void Browser_Navigated(object sender, WebBrowserNavigatedEventArgs e)
    //Web_BR 07: Provide some information about the current navaigation
    Status_Information.Text = "Navigated..";

private void Browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
    //Web_BR 08: Provide some information about the current navaigation
    Status_PB.Visible = false;
    Status_Information.Text = "Web Page Loaded";
Note that we hide the progress in the status bar during the form load. And, we enable that when the Browser starts its first navigation for the page. Once we receive the "doumentcompleted event", we again disable the progress bar. Below given video shows loading the web page in our browser sample:
Video 4: Sample in Action: Navigate to a Page

6. Create Plain custom HTML for WebBrowser Control

When we click the custom button 1 from the tool strip, the browser loads custom script 1 as shown below. We access this HTML button shown in the below picture from the C# code and hook the click event of this button to the c# method.
The ToolStrip of our browser sample application is modified to include two more buttons and each button loads two different user content in the web browser control. The below video shows adding these two buttons to the ToolStrip control:
Video 5: Modify ToolStrip Control
Once the buttons are added, using the Visual Studio IDE, the custom web content (Shown in the Picture Above) is created. The formatted content of the IDE created HTML page is given below:
<html >
   <title>Button Click by ID</title>
 <body>The button placed below is a html button. The button is accessed by C# and a handler method(in C#) is provided at runtime
   <input id='BtnTest' style='z-index: 100; left: 8px; width: 102px; position: absolute;top:60px' type='button' value='button'/>
In the above script, we gave an Id (BtnTest) to the “input” control. In addition, the control type is specified as a button. Notice that we don’t provide handler now. At runtime, the browser sample will get this button from the loaded HTML content, and dynamically specifies the handler to the button click. Video 6 shows creating and formatting the HTML shown above using the visual studio IDE. 
Video 6: Creating the HTML content for the browser
Note that we changed the double quotes as single quotes and this will avoid formatting the HTML content when we assign it as a string to the WebBrowser control later.

7. Hooking the event at runtime

1) In the above script, the HTML designer not aware of which function to be called and that is decided at the runtime. The first thing we should do is adding a private flag customDoc1. This flag will be set when the WebBrowser control is loading the user HTML script.
//Web_CS 01: Declare boolean tag for document 1 and 2
private bool customDoc1 = false;
2) In the button click handler (For the first user script), we assigned the above-created user content 1 to the web browser control by setting the "DocumentText Property".
//Web_CS 02: Place the Html document with a button having proper Id
private void btnUser1_Click(object sender, EventArgs e)
    //Setting this property will load the document and raise document completed
    Browser.DocumentText = "<html ><head><title>Button Click by ID</title></head><body>The button placed below is a html button. The button is accessed by C# and a handler method(in C#) is provided at runtime<input id='BtnTest' style='z-index: 100; left: 8px; width: 102px; position: absolute;top:60px' type='button' value='button'/></body></html>";
    customDoc1 = true;
3) As we specified the new content for the WebBrowser control (By setting the DocumentText), the browser will load the content and fires the Document Completed event once the load is fully done. In the event handler, we get the HTML button by its id and store that as "HtmlElement". Then we do provide the event handler to its click event using the delegate "HtmlElementEventHandler". Finally, we reset the boolean flag customDoc1 to false.
//Web_CS 03: Get the Html button from the loaded document
//           Script. Then attach the C# method as click handler
if (customDoc1 == true)
    //03.1 : Get html Element
    HtmlElement btnElem = Browser.Document.GetElementById("BtnTest");

    //03.2 : Hook the C# method to the event
    if (btnElem != null)
        btnElem.Click += new HtmlElementEventHandler(Html_test_button_Hanlder);
    //03.3 : Reset the Tag
    customDoc1 = false;
4) The handler function for the html button click is shown below:
//Web_CS 04: Create a button handler for Html Button
void Html_test_button_Hanlder(object sender, HtmlElementEventArgs evArg)
    MessageBox.Show("Click event of the Script button Handled");
In this section, we created an HTML content with a button in it. Then the HTML content is loaded into the WebBrowser control. Once the user content is loaded completely, we get the HTML button instance from the loaded document and attached c-sharp method as click event handler. In this case, the HTML document designer not aware of the c-sharp handler. In some cases, the HTML script knows which function to call on the c-sharp code library. In that case, they (The script developer) do provide the click handler during the HTML design itself. We see that in the next section.

8. Setting the click handler from External source

Look at the second custom script shown below:
<html >
         <title>Button Click Hooked to External Method of C#</title>
      <body>The scripting person knows which c# function to call. So the well known c# method is tied in the OnClick Attribute
          <input style='z-index: 100; left: 8px; width: 102px; position: absolute;top: 44px'
          type='button' onclick='window.external.ShowMessageE()'  value='button'/>
In this script, the input button is not specified with the id. In the OnClick event, the handler function external.ShowMessageE is specified as an event handler. The external here specifies that the function ShowMessageE() is available from the external source. In our case, the external source is C# as the WebBrowser control is going to load it.
1) In the WebBrowser sample, first, we included the required namespace. This is to make the class "HtmlSupportingMethods" that we are going to implement soon as a COM visible class.
//Web_CS 05: Declare the required namespace
using System.Runtime.InteropServices;
2) In the button click the second user content is specified as the "DocumentText". This document is loaded just the same way we loaded the first script. The only difference is, here we used the verbatim string (@stringcontent) to retain the format of the string as it is.
//Web_CS 06: Set the Html as document content of the browser control.
private void btnUser2_Click(object sender, EventArgs e)
    string htmldoc = @"<html >
    <title>Button Click Hooked to External Method of C#</title>
    <body>The scripting person knows which c# function to call. So the well known c# method is tied in the OnClick Attrubite<input
            style='z-index: 100; left: 8px; width: 102px; position: absolute;top: 44px'
            type='button' onclick='window.external.ShowMessageE()'  value='button'/>

    //Setting this property will load the document and raise document completed
    Browser.DocumentText = htmldoc;
3) The class HtmlSupportingMethods contains only one public method, which is accessed by our custom script 2 as Window.external.ShowMessageE(). As this method is accessed by the external scripting world, we mark the class as COM compatible by declaring the class with the "[ComVisible(true)] Attribute" 
//Web_CS 07: External scripting methods. This class is set with the ComVisible attribute
//           as it's methods are going to be accessed by the html document content that
//           will be loaded by the Web Browser control
public class HtmlSupportingMethods
    public void ShowMessageE()
        MessageBox.Show("External Method Accessed");
4) There is one final step. How does the content loaded on the WebBrowser control knows where should it get the External Source? We should link the C# HtmlSupportingMethods with the HTML script. We do that in the form load as shown below:
//Web_CS 08: Set the Scripting Object. This should a com visible object
Browser.ObjectForScripting = new HtmlSupportingMethods();
The browser sample in action can be found in the below video. The video shows both the user HTML document and hooking the HTML button click to C# code.
Video 7: Testing the scripting support
Source Code: Download

December 5, 2012

[ MFC ] - Creating Owner Draw Menu in VC++ using WM_DRAWITEM, WM_MEASUREITEM

1. Introduction

When an owner window of the menu item decides how the menu item should look, then the menu item is known as Owner Drawn menu item. The default windows provided menu has a standard look and feel. We cannot add Red and blue boxes as menu item through the resource editor. In this article, we will see how can we display only color boxes as a menu item under the color menu.

2.  Create the SDI Application

The first thing is creating the MFC SDI Application. In the app wizard, select Single Document Interface option and make sure to uncheck the Document/View architecture support check box. This is shown in the below picture.

In the class files provided by the application wizard, we are going to draw the owner-draw menu items through CMainFrame class. Once the application is created, remove the unwanted menus from the menu bar of the mainframe window. Then add the color menu at the end of the menu bar. To this color menu, add three menu items named Red, Blue, and Green. This is shown in the below video:

Video 1: Adding the new menu items

Note that the menu items added in the above video are not an owner draw menu. We will make these menu items as an owner-draw menu item at runtime. Once the three menu items are added, name it as ID_COLOR_RED, ID_COLOR_BLUE, and ID_COLOR_GREEN. Assigning the ID to the menu item Green is shown in the below picture.

At this stage, running the application will display the standard menu items with texts Red, Blue, and Green. Let us move to coding part. To follow with me, search inside the downloaded sample with the search tag //Sample <no>

3. Change the Menu items as Owner Drawn

Video 1 shows that we already added three menu items to the color menu. However, these menu items are not owner drawn. To make it owner draw we should modify the menu items by calling the ModifyMenu on the CMenu. Add the below-specified code (Specified in blue color. Tag is Sample 01) in the int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)

         if (!m_wndStatusBar.Create(this) ||
                 TRACE0("Failed to create status bar\n");
                 return -1;      // fail to create
         //Sample 01: Modify the Menu as OwnerDrawn
         CMenu * pMainFrame_menu = this->GetMenu();
         pMainFrame_menu->ModifyMenu(ID_COLOR_RED, MF_BYCOMMAND | MF_OWNERDRAW, ID_COLOR_RED);
         pMainFrame_menu->ModifyMenu(ID_COLOR_BLUE, MF_BYCOMMAND | MF_OWNERDRAW, ID_COLOR_BLUE);
         // TODO: Delete these three lines if you don't want the toolbar to be dockable

The code added (Sample 01) above will modify three menu items of the “color” as owner drawn menu. The first parameter says for example ID_COLOR_RED specifies the menu item that we are modifying. In the second parameter, we specified that the first parameter given is a command id of the menu item by supplying the flag MF_BYCOMMAND. You can also specify a menu item by its position in that case a first parameter is a position number like 0,1,2 etc and the second parameter supplies the flag MF_BYPOSITION. In the second parameter, we also supplied one more flag MF_OWNERDRAW that makes the menu item as an owner-drawn menu item. In the third parameter, we can change the command id of the menu item after the modification. In our case, we specified the change in the second parameter and kept the same id.
Here, we created the menu first, then used the ModifyMenu function call to change the menu items as owner drawn. You can also use the AppendMenu or InsertMenu with the MF_OWNERDRAW flag. In both the cases, we are creating the Menu at runtime.

4. One Handler – Three menu items

We are going to add single handler function for all three modified menu items. First thing is checking the order of the menu item ids that is lower to higher order with continues id numbers. In my case, ID_COLOR_RED is lower id and ID_COLOR_GREEN is higher id. You can check the ID ranges from Resource.h as shown in the below picture.

Once we have the ID sequence, make an entry in the message map as shown below:

         //Sample 02: Hanlder for the Owner drawn menu

Here, we specified that the handler for all the commands in between the range from ID_COLOR_RED to ID_COLOR_GREEN, the handler function is OnOwnerDMenuClick.

In the MainFrame.h header file add the handler function declaration shown below:

//Sample 03: Declare the Handler function
void OnOwnerDMenuClick(UINT cid);
Below is the implementation for the handler function:
//Sample 04: Handler for the Menu Items
void CMainFrame::OnOwnerDMenuClick(UINT cid)
         if (cid == ID_COLOR_RED)
                 AfxMessageBox(_T("Red Button Clicked"));
         if (cid == ID_COLOR_BLUE)
                 AfxMessageBox(_T("Blue Button Clicked"));
         if (cid == ID_COLOR_GREEN)
                 AfxMessageBox(_T("Green Button Clicked"));

The above handler function displays the message boxes, which corresponds to the clicked menu item. In the above function we checked the cid and that carries the command id of the clicked menu item.


When a menu item is specified as owner drawn menu, the owner that is the CMainFrame window will receive the WM_DRAWITEM and WM_MEASUREITEM window messages. The WM_MEASUREITEM message will be sent only once for each owner draw menu item that means for the first time the menu containing the menu items is opened. The handler for the measure item will set the dimensions required for drawing those menus.
The window message WM_DRAWITEM will be sent for every owner draw menu item whenever the menu containing the owner draw menu item is opened. Therefore, the owner of the menu items actually draws the menu item here by making the measurements taken in the handler for the WM_MEASUREITEM.
For example, let us take that user clicks the color menu item three times. For the first time the CFrameWnd will receive both the window messages. For second and third times, it will receive only the WM_DRAWITEM windows message.

The Below video shows providing the handler function in CFrameWnd for both the window messages:

Video 2: Providing the Handler functions

6. Implementing OnMeasureItem

In the previous video, we saw the handler function for both the window messages discussed in the previous section of this article. Add the below piece of code in the OnMeasureItem Handler.

void CMainFrame::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
         //Sample 05: Provide Height and widths
         UINT height = 20;
         UINT width = 40;
         lpMeasureItemStruct->itemHeight = height;
         lpMeasureItemStruct->itemWidth = width;
         CFrameWnd::OnMeasureItem(nIDCtl, lpMeasureItemStruct);

In the above handler function, we specified the width and height of our owner drawn menu item with a hard-coded value in the LPMEASUREITEMSTRUCT.  Therefore, in our case, the three menu items will have same width and height. In some cases the width and height changes for each menu item. For Ex: displaying the name of the font in the same Font. This changes the font width and height based on the displayed font even though the text displayed is same.

7. Implementing OnDrawItem

Look at the handler function signature of the OnDrawItem function shown below:

void CMainFrame::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
The measurement done for each menu item in the OnMeasureItem() can be retrieved here using the lpDrawItemStruct. Before we write the code inside the OnDrawItem(), we explore some important members of the lpDrawItemStruct.

·       The itemId member of this structure holds menu item id. Using this we can know what menu item we are drawing now. If we want to draw some special effect for a particular menu item, this member will be useful.
·       The rcItem member provides the rectangular structure, which can be used as the dimension required for drawing the menu item.
·       The hDC member is used for Drawing the shapes using the GDI objects.
·       The itemState member informs the state of the menu item. We can check the state using the bitwise & operator with constants ODS_CHECKED, ODS_DISABLED, ODS_GRAYED and ODS_SELECTED.

1) First thing we did was creating a device context for the drawing. Once the device context dc is created in the stack, we attach this MDC object (dc) to the win32 handle hDC taken from the lpDrawItemStruct.

//6.1: Get the device context from the Draw Item structure and attach that to a dc object
CDC menu_dc;

2) When the menu item is selected, we are going to draw a border in black color around it. In addition, we will draw the border around non-selected menu items in the background color of the menu. The Win32 API call, GetSysColor(COLOR_MENU); will give use the background menu color.  Drawing around the Non-Selected menu item is required to clear the already drawn black rectangle. To draw the rectangle FrameRect function is called on the device context. The ODS_SELECTED constant is checked against the itemstate of the menu item to decide the color of the brush.

//6.2: Test item state. When the item is selected, draw a black border over it.
//          When item is not selected draw the border in menu's background color
//          (Clears previous drawn border)
CBrush * brush;
RECT menu_item_rct = lpDrawItemStruct->rcItem ;
if ( lpDrawItemStruct->itemState & ODS_SELECTED )
         brush = new CBrush(RGB(0,0,0));
         DWORD color_index = ::GetSysColor(COLOR_MENU);
         brush = new CBrush(color_index);
menu_dc.FrameRect(&menu_item_rct, brush);
delete brush;

3) We check the itemID member with the menu item id constant for creating the color brushes matching the selected menu item. Once the required color brush is created, we fill the slightly diminished rectangle, measured in the OnMeasureItem with the color brush we created by checking the menu item id. The DeflateRect function will reduce the dimension of the rectangle and FillRect paints the rectangle using the brush specified. Once we are done with the drawing, the Win32 handle is detached from CDC object. Below is the code:

//6.3: Create the Item color and draw it
if (lpDrawItemStruct->itemID == ID_COLOR_RED)
         brush = new CBrush(RGB(255,0,0));
if (lpDrawItemStruct->itemID == ID_COLOR_BLUE)
         brush = new CBrush(RGB(0,0,255));
if (lpDrawItemStruct->itemID == ID_COLOR_GREEN)
         brush = new CBrush(RGB(0,255,0));
CRect menu_rct(menu_item_rct);
menu_dc.FillRect( menu_rct, brush );
delete brush;
//6.4: Detach win32 handle
CFrameWnd::OnDrawItem(nIDCtl, lpDrawItemStruct);

Video 3 : Sample in action

Source Code: Download

November 4, 2012

[ MFC ] - Display Context Menu On right button click using CMenu TrackPopupMenu


In this article, we are going to create Main menu with four menu items in it. The last menu item is going to open a sub menu. The menu will be displayed when the mouse is right-clicked in the client area of the window and at the location of the mouse pointer.

2. About the sample

The below screenshot shows the application sample:

The sample is an SDI Application without document and view architecture support. The client area is marked in yellow border and when the mouse pointer is inside the client area of the window, the right client will display a pop-up menu.

Here we are creating the menu items at runtime and displaying the popup menu as shown in the screenshot.

The video given below shows default setting overridden for MFC SDI Application.

Video: Creating Project


When the mouse is right clicked inside the client area of the window, the window will get a notification message WM_CONTEXTMENU. This message will come with the window in which the mouse is right clicked and pointer position in screen coordinate in which the mouse pointer lies when the click happened. This notification message will be used to display the popup menu.
The video given below shows providing the handler for the WM_CONTEXTMENU notification message. The notification is handled by the class CChildView.

Video: Adding Handler

In the video, you saw view class provides the handler for the notification message that we are discussing. The handler provided looks like below:
void CChildView::OnContextMenu(CWnd* pWnd, CPoint point)
Here, pWnd the pointer to the window in which the right client is done. The second param, point is cursor location in screen co-ordinate.

4. Display Context Menu by Handling OnContextMenu

The menu is created in side the handler provided for the WM_CONTEXTMENU.

1) First a CRect class declared to get the client window dimensions. Next, SubMenu and MainMenu instance of type CMenu is created.
//Sample 01: Declarations
CRect client_rect;
CMenu SubMenu, MainMenu;

2) We first get the client area of the window in client_rect structure. Then we convert this structure into Screen co-ordinate, which from the Top left of your monitor. We do this because the point parameter given to the handler as second argument is in screen co-ordinate.

//Sample 02: Get Mouse Click position and convert it to the Screen Co-ordinate

3) We will display the popup context menu only when the mouse is right clicked inside the client area of the window. Therefore, we should check the mouse click position lies inside the client rectangle dimension. Note that as we get the mouse position in screen co-ordinate, so we converted the rectangle dimension client_rect into screen co-ordinate before doing the PtInrect check.

//Sample 03: Check the mouse pointer position is inside the client area
if( client_rect.PtInRect(point))

4) First, the Sub Menu for the context menu is created by calling the CreatePopupMenu function of the CMenu object. Then the menu items are added to it using the append menu function call.  The first parameter passed as MF_STRING says that we adding the string menu item. The second parameter is ID value that we give to the menu item. This Id will be used later when we need to process Command Message (Not covered in this article). The last parameter is display string of the menu item.

Once the Sub menu is created, we start creating the Main Menu. This menu is created the same way the sub menu is created. However, the last item on the Main menu is a sub menu that we already created. Note that we added the sub menu to this main menu; we passed the MF_POPUP as the first parameter to the function call AppendMenu. This will indicate the append menu that unlike the normal menu item the function should create the cascading menu for the menu item shown as Line Thickness.

//Sample 04: Create the sub Menu First
SubMenu.AppendMenu(MF_STRING, 4001, _T("1"));
SubMenu.AppendMenu(MF_STRING, 4002, _T("2"));
SubMenu.AppendMenu(MF_STRING, 4003, _T("4"));
SubMenu.AppendMenu(MF_STRING, 4004, _T("8"));
//Sample 05:Create the Main Menu
MainMenu.AppendMenu(MF_STRING, 4005, _T("Line"));
MainMenu.AppendMenu(MF_STRING, 4006, _T("Circle"));
MainMenu.AppendMenu(MF_STRING, 4007, _T("Polygon"));
MainMenu.AppendMenu(MF_POPUP, (UINT)SubMenu.GetSafeHmenu(), _T("Line Thickness"));

5) Finally, we call TrackPopupMenu to display the context that we created at runtime. The first parameter TPM_LEFTALIGN tells that the displayed popup menu should left align with the cursor location. The x and y position tells where we want to display the MainMenu as popup menu.

//Sample 06: Display the Popup Menu
MainMenu.TrackPopupMenu(TPM_LEFTALIGN, point.x, point.y, this);
The video given below shows the sample in action.

Video: Running the Sample

Source Code: Download

Like this site? Tell it to your Friend :)