Developer Blog
Articles about Using Microsoft Developer Tools

SQLite

Saturday, December 13, 2008 9:09 AM by jonwood

MFC programmers wanting to store data to disk have a couple of choices.

Although there are a variety of third-party options available, the choice generally comes down to a home-grown solution that includes code to access raw disk files, or using a major database server.

Home grown solutions can be anything from fprintf() to complex data-handling systems. By coding it yourself, you can add just the code you need and have it work exactly how you want. Of course, it can also be a lot of work, especially if you need something more sophisticated.

Major database servers make it easier to efficiently handle large amounts of data. And, using SQL (structured query language), you have a lot of flexibility in how your application can extract that data. However, this can complicate distribution and installation of your application if it requires access to a bunch of additional modules or perhaps even a database connection.

I was recently writing an application for a client and our needs seemed to fall somewhere between these two choices. We didn’t want the coding and distribution overhead associated with a complete database server, but we did want some flexibility on extracting the data in order to be able to produce a number of reports.

For this project, we decided to go with SQLite. SQLite is a complete SQL database engine but works quite different than those you may be familiar with. For starters, SQLite is completely free of charge. And, although this means no support, there are some forums you can access and I’ve found support there to be pretty good.

Second, the library comes as raw source code. Although you could compile it into a DLL and call the DLL, we simply included the source code in our project. They provide a combined source code so all we had to do was include the one file and a header file and we were good to go.

Of course, SQLite does not run as a separate process as a database server would. It is simply a library of subroutines in our application (which we developed a class library around). But we didn’t need a database server. And because the database is now part of our application, we don’t need to worry about distribution issues, installation problems, version incompatibilities, or anything else of that nature. And we have full SQL support when querying our data.

SQLite has a few oddities. The main one is that it does not enforce referential integrity. If you delete a row that is referenced by a foreign key in another table, you now have an invalid reference. It is possible to use triggers to catch this and perform, for example, a cascading delete. For our purposes, this hasn’t really been an issue.

Another thing is the way it stores data. You can specify a column to be an integer data type, but it doesn’t mean that’s how it will be stored. You can easily assign a string to a field in that column. It will correctly sort those strings based on their integer values. You just don’t know how the actual data will be stored. This has never been an issue for us either.

All in all, I think SQLite is totally cool. I now have all the power of SQL in a stand-alone application. And, being free, the price cannot be beat. You’d be surprised how many applications are using it as well. From desktop applications to cell phones, it contains thoroughly tested code. Depending on your needs, perhaps SQLite is appropriate for your next project too.

Editing Ribbon Bar Images

Saturday, December 13, 2008 8:37 AM by jonwood

If you’ve installed the Visual C++ 2008 Feature Pack or upgraded to Visual Studio 2008 Service Pack 1, you may already be working with the new ribbon bar control.

This control provides many new user-interface elements and cool new images. However, if you are like many people, you may have wondered how you can change those images. Even with all its powerful resource editing features, the current version of Visual Studio does not support editing the type of images used by the ribbon bar. You can open them in Visual Studio, but they will appear with strange black areas and will not be editable.

Ribbon Bar Images

So what’s different about the images used in the ribbon bar compared to those images used in icons, cursors, and regular bitmaps? Two things: The color depth and the alpha channel. Ribbon bar images use 32 bits per pixel to specify color values. This allows for images with a much greater color depth.

The alpha channel is a value that specifies the transparency of a pixel. Color values have historically used a byte each to specify red, green, and blue color values. In a 32-bit integer, this leaves a fourth byte available for the alpha channel. This means that we can specify the transparency of a given pixel with the same resolution we specify any of the other color components.

The alpha channel is great for smoothing the edges of images. Sometimes, bitmap edges can look jagged when dark outlines have awkward correlation with the underlying pixels. These edges can be smoothed somewhat by adding gray pixels to smooth the transition from black image pixels to white background pixels. But what happens if the background is black? The alpha channel allows you to fade the edge of an image into any background color. The result is much smoother outlines and transparent areas regardless of the background.

IconWorkshop

So if you can’t use Visual Studio, you’ll need another tool. Fortunately, Axialis has made arrangements with Microsoft to provide a free version of their IconWorkshop. This is a great little program. In fact, I registered the full version quite some time ago.

IconWorkshop has a recently added feature that displays an image strip bitmap as a collection of separate images, similar to how the toolbar editor does in Visual Studio. This makes it very easy to work with image strips for ribbon bars. I should point out that most of the ribbon images I put together these days are either some of those provided with Visual Studio, created using high-end graphics programs, or even imported from photos. IconWorkshop is great for importing various image types into a single image strip, even if you don’t use it to actually create the original images.

Application Button

There are two primary types of ribbon images that you’ll want to change. The first is the application or “jewel”. This is the image that appears in the round button near the upper left corner of the main window. By default, CMainFrame::InitializeRibbon() initializes this button to use the bitmap resource with the ID IDB_MAIN.

// Init main button:
m_MainButton.SetImage(IDB_MAIN);
m_MainButton.SetText(_T("\nf"));
m_MainButton.SetToolTipText(strTemp);

Default initialization of ribbon application button

Note that when you load main.bmp (IDB_MAIN) in IconWorkshop, it will appear as an image strip with a single image. Don’t let that throw you. Just edit the image and save it and you’ll have a new application button image.

Command Images

The other primary type of images are for the actual ribbon commands. As described before, these images are stored as image strips where a single bitmap contains a number of different images. The image strip is set when a ribbon bar category, or tab, is created. Two image strips are specified: one for large images and one for small images. For example, the default code specifies the image strip bitmaps with the IDs IDB_WRITESMALL and IDB_WRITELARGE when creating the Home category. If you open either of these bitmaps, you can see the images they contain.

// Add "Home" category with "Clipboard" panel:
bNameValid = strTemp.LoadString(IDS_RIBBON_HOME);
ASSERT(bNameValid);
CMFCRibbonCategory* pCategoryHome = m_wndRibbonBar.AddCategory(strTemp,
  IDB_WRITESMALL, IDB_WRITELARGE);

Default initialization of category image strip

When a ribbon element such as CMFCRibbonButton is created, you specify the zero-based index for the image to be used with zero specifying the left-most image. You can specify two indexes, one for the index into the small image strip, and one for the index into the large image strip. If you only specify the index for the small image, then that element will only use the small image.

Conclusion

So there are a few new things to learn here and a few more tool that you’ll need.

Although the ribbon bar can seem a bit awkward to experienced users accustomed to using toolbars and pull-down menus, we’re told that research shows the ribbon bar layout to be much easier for most users to learn.

When done well, ribbon bars can look great and support some really cool images. This is definitely something to get on top of if you are designing modern interfaces in MFC.

Advanced Message Map Techniques

Tuesday, November 25, 2008 9:37 AM by jonwood

NOTE: This article has been updated and moved to: Advanced Message Map Techniques.

Experienced MFC developers will already be familiar with message maps. A message map uses macros to create a table of message or command IDs and the methods that will handle those messages in a particular class. When a command or message is routed to an instance of that class, MFC scans the table for an entry that corresponds to the current message. If the entry is found, the corresponding method is called.

BEGIN_MESSAGE_MAP(CMyDoc, CDocument)
    ON_COMMAND(ID_MYCMD, OnMyCommand)
    // Additional entries can go here
END_MESSAGE_MAP( )

A message map

When you add a C++ event handler in Visual Studio, the Wizards automatically add entries to the message map. So, normally, you don’t need to think much about them. The two types of entries that the Wizards will add automatically are regular message and command handlers, and update handlers, which are used to update the state of a command’s button or menu.

However, there are some advanced message map techniques that are not directly supported by the Wizards. These techniques require you to edit the message map manually.

Command Range Handlers

The first technique I’ll discuss is using command range handlers. Command range handlers allow you to specify a single method that will handle a range of command IDs. For example, you may have a number of commands for setting a color used by your application. In this case, it may be easier to write one method that processes all of these commands since the response to each command will be very similar.

To accomplish this, you can use the ON_COMMAND_RANGE macro. The arguments to this macro are the starting command ID, the ending command ID, and the method handler. The method will be called for each command in the specified range.

// In class header
afx_msg void OnColorCommand(UINT nID);
// In message map
BEGIN_MESSAGE_MAP(CMyDoc, CDocument)
  ON_COMMAND_RANGE(ID_FIRST, ID_LAST, OnColorCommand)
END_MESSAGE_MAP()
// In class implementation
void CMyDoc::OnColorCommand(UINT nID)
{
  switch (nID)
  {
  case ID_FIRST:
    // Handle this command
    break;
  case ID_LAST:
    // Handle this command
    break;
  // Handle any additional commands
  // ...
  }
}

Command Range Handler

In addition, you can use the ON_UPDATE_COMMAND_UI_RANGE macro to specify a range update handler. This allows you to control the command state from a single update handler. This macro takes the same arguments as ON_COMMAND_RANGE and the handler method takes an additional argument, which is a pointer to a CCmdUI object. You can use this argument to control the state of the corresponding menu/button command.

User Message Handlers

In some cases, it may be necessary to define your own message and then add a handler for that message. This can be accomplished using the ON_MESSAGE macro.

// In class header
#define WM_CUSTOMMESSAGE (WM_USER + 100)
afx_msg LRESULT OnCustomMessage(WPARAM wParam, LPARAM lParam);
// In message map
BEGIN_MESSAGE_MAP(CMyDoc, CDocument)
  ON_MESSAGE(WM_CUSTOMMESSAGE, OnCustomMessage)
END_MESSAGE_MAP()
// In class implementation
LRESULT CMyDoc::OnCustomMessage(WPARAM wParam, LPARAM lParam)
{
  return 0;
}

User Message Handler

Registered Message Handlers

Sometimes you need to handle a user message, but you don’t know the value of that message until run time. For example, you may be using RegisterWindowMessage() to obtain the message value to ensure different instances of your application will be using the same value.

This can be accomplished using the ON_REGISTERED_MESSAGE macro. Instead of taking an explicit command ID, this macro references an integer variable that will hold the ID value at run time.

// In class header
UINT m_nRegMsg;
afx_msg LRESULT OnRegisteredMessage(WPARAM wParam, LPARAM lParam);
// In message map
BEGIN_MESSAGE_MAP(CMyDoc, CDocument)
  ON_REGISTERED_MESSAGE(m_nRegMsg, OnRegisteredMessage)
END_MESSAGE_MAP()
// In class implementation
UINT CMyDoc::m_nRegMsg = RegisterWindowMessage(_T("MyRegMsg"));
LRESULT CMyDoc::OnRegisteredMessage(WPARAM wParam, LPARAM lParam)
{
  return 0;
}

Registered Message Handler

Conclusion

MFC defines a few additional message map macros that I have not covered here. However, you probably won’t run into many cases where you need them. As I mentioned before, most of the time the Wizards will add these entries automatically. But understanding the techniques I’ve described above will add some helpful techniques to your bag of tricks.

Tags:  
Categories:   C++/MFC
Actions:   E-mail | del.icio.us | Permalink | Comments (0) | Comment RSSRSS comment feed

MFC Ribbon Bar Doesn't Implement ID_CONTEXT_HELP

Tuesday, November 25, 2008 2:48 AM by jonwood

Overview

One of the predefined command IDs historically supported by MFC is ID_CONTEXT_HELP. When this command is selected, the mouse pointer changes to an arrow with a question mark and, when you click on a control or window, MFC displays help associated with the item clicked.

However, as of Visual Studio 2008 SP1, this command does not work for the ribbon bar. The command is still implemented but it does not obtain the correct value and so help fails to load.

Details

CMFCToolBar derives from CBasePane, which derives from CWnd. The handler for ID_CONTEXT_HELP ends up calling CBasePane::OnHelpHitTest() to obtain the ID of the element that was clicked. To accomplish this, CBasePane::OnHelpHitTest() calls CWnd::OnToolHitTest(), which is virtual.

Classes like CMFCToolBar override OnToolHitTest() to return the ID of the button at the specified location. But CMFCRibbonBar does not override OnToolHitTest() and so CWnd::OnToolHitTest() tries to find the child window at the specified point. Since ribbon elements are not actual windows, CWnd::OnToolHitTest() finds no child windows and returns -1. As a result, CBasePane::OnHelpHitTest() simply returns the ID of the ribbon window itself, which is useless here.

Resolution

Someone from Microsoft confirmed with me this issue and said it was not supported because it is not supported in the Microsoft Office 2007 applications.

In all fairness, the ribbon bar has a fairly sophisticated tooltip system than can be used to display brief help for any element by simply hovering the mouse over that element. This makes the ID_CONTEXT_HELP command far less important than it may have been in the past. Still, I ended up wasting a bit of time trying to determine why I couldn't get ID_CONTEXT_HELP to work with the ribbon bar. The best fix may simply be to avoid its use in MFC applications that have a ribbon bar.

Tags:   ,
Categories:   C++/MFC
Actions:   E-mail | del.icio.us | Permalink | Comments (0) | Comment RSSRSS comment feed