Part 5 of 5 - Introduction
Links to Other Parts of the Series
The following text is identical to Part 1. If you haven't already read that article, this article will be useless to you, so by all means, catch up. We'll wait here. If you have read the Part 1 article, you can skip these intro sections.
This article series is another in my series of "code we really use" articles. There is no unnecessary discussion about theory, no expounding on technique, and no chest-thumping because I thought it all up myself. It's just a bunch of stuff I did to stand one of our applications up. MOST of the stuff in this article is based on other code that I got from CodeProject, and what follows describes the basis for a project I am actively developing and how I integrated articles and help I got from CodeProject.
Rant
I've been a member on CodeProject for over six years (as of this writing), and I've come to discover some disturbing trends regarding articles. First, article authors tend to post an article and as time goes by, the author essentially abandons the article and people posting questions are greeted with either silence from the author, or a response that says something like "I don't code in this/that language any more". Let's face it, you can't blame them. Many of the articles I use are three or four years old, and I understand that programmers need to move on and that often means completely abandoning older code.
On the other side of the fence are the people that download the source code and samples associated with a given article. Many times, someone will post a question in an article that has absolutely nothing to do with the article itself, but the subject will be related to a certain aspect of the article. As an example, I posted an article about dynamically building a menu. Recently, someone posted a message in that article that asked about adding winhelp to their dynamically built menu. Then there's the people that encounter an issue (real or imagined) with an article, and expect someone else to fix it for them. These people really annoy me. After all, we're all supposed to be programmers here.
So, What's the Point of This Article?
The entire point of this article is to illustrate real-world use of code-snippets, classes and techniques I gleaned from CodeProject over the last six years, including work-arounds to fit code into my sometimes bizarre requirements. Many times, I'll use the VC++ forum to ask a question that will help me understand an article, or massage the article's code for my own use.
Assumptions
The original version of this article started out as a kind of detailed tutorial describing how to use the IDE, and other inane items like that. After a while, I realized this created a huge amount of overhead as far as the article's weight was concerned. Beyond that, I was starting to become bored with the whole thing and I could plainly see that the quality of my writing was beginning to suffer as a result.
The only solution was to start over and make the assumption that you, the user, have a working knowledge of the VS2005 IDE, especially as it relates to creating VC++/MFC applications. This way, we can talk more about the important stuff than suffer through stuff you should already know. I also assume that you have a decent working knowledge of MFC. I'm not saying you have to be an expert, but I assume you can move around in a MFC project without bumping your head on the intricacies of CMainFrame.
Other Stuff
Sprinkled throughout the article, you'll find "Coding Notes". These simply describe the way I do things when coding, and why I do them. They are certainly not requirements by any stretch of the imagination, but they often concern code readability and maintainability. I'm sure that many of you have your own ways of doing things, but please keep comments regarding these issues to a minimum. Afterall, this article is not about style.
The total process of coding the complete demo app requires just a couple of hours (if you know all the steps ahead of time). Writing this article series has taken me DAYS, so don't be put off by it's length.
The html and images for this article is included in the project download, but doesn't include the pretty CodeProject formatting. If you can mentally handle that, you can simply refer to this .HTML file and get on with your programming.
Finally, I know there are folks out there that vote my stuff a 1 simply because it's, well, something I wrote. I request that you be mature and professional and restrict your politics to the soapbox when voting. Remember, you're voting on the article, not on the author.
What We've Already Done
In Part 1 of this article series, we went through the steps of creating a MFC SDI application and making the view a little more interesting by adding the MFC Grid Control to it. In Part 2, we added a splitter window and the ability to swap views within one of the splitter panes. In Part 3, we added a custom status bar class and some simple multi-threading capabilities that update the contents of the status bar panes. In part 4, we created a MFC extension DLL to contain all of the views, and created two new applications that each use the new extension DLL.
In this article, I'm going to zip through some of the last few things I added to the application. Since most of these items are fully described in their respective articles, I'm just going to gloss over them. Besides, I've been working on this article series long enough (and I'm starting to go a bit bug-eyed). By now, I hope you've gotten the idea I've been trying to drum into your brain - CodeProject is a great place to start, but you usually need to put in a moderate amount of work to make the articles work for your application, and in the end, the only person you can really rely on is yourself.
Cleaning Up Some Loose Ends
So here we sit with our fancy little applications, smug in our ability to deliver any feature that might be demanded by management nazis. While gazing upon the brilliance that *is* our application, we start to notice small things that we ignored (or as I prefer to say - "deferred our attention from") during the flurry of new development.
The first thing we wanted to handle is cleaning up the title bar. It contains the text "Untitled", and since we don't really load "documents" (this is the front-end for a database, remember?), we can safely eliminate it from our titlebar.
Since I didn't remember specifically how to do this, I searched CodeProject for the word "Untitled", and found a link to the C++ FAQ by Michael Dunn. Specifically, I was interested in this FAQ item. So, for each of our three applications, add the following code to the PreCreateWindow
function:
cs.style &= ~FWS_ADDTOTITLE;
Optional change - One of our requirements was to start the application as a maximized window. The reason is that these apps are intended to be used via Terminal Services and run from an application server. To make your application window display initially maximized, change this line in your app's InitInstance()
function:
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
m_pMainWnd->ShowWindow(SW_MAXIMIZE);
m_pMainWnd->UpdateWindow();
Finally, I added some code in the CPrimaryView
class so that the grid would have some rows to print. It's just a for
loop that adds ten rows to the grid, but if you simply must see the code, look in CPrimaryView::OnInitialUpdate()
.
An XML-based Application Profile Class
To keep this article CodeProject-specific, I looked around on the site and couldn't find anything that I thought was suitable, so I wrote my own class (see, I'm a programmer, and this is how programmers do things), and then posted an article about it. Sure, there's another XMLprofile article here, but it just plain didn't compile. If you haven't already, check out my XML Application Profile Class artcile.
The CProfile
class allows you to create/use XML files that resemble INI files in their usage. It's not an all-emncompassing XML file reader, but it does what it's designed to do pretty well. I used this class to read program settings for the thread intervals. It only took three lines of code in this sample application to set it up.
Adding Printing Support
For The CPrimaryView
Since the MFC Grid Control has printing support bult-in, all we have to do is turn call the function. Since I created the CPrimaryView
manually, I had to copy the printing stuff from the SDIClientView.cpp
file into the CPrimaryView
class, and overrode OnFilePrint
. From OnFilePrint
, I called the grid control's Print
function.
For The CSecondaryView
For the CSecondaryView
, things were a bit tougher. Again, since I created the class manually, it didn't come with the necessary printing functionality, so I once again copy/pasted from the SSDIClientView
class.
I couldn't find ANYTHING on CodeProject that filled the need, so I had to go "off the reservation" so to speak. On the off chance that I can't find what I need on CodeProject, I look on CodeGuru. I eventually found a class on CodeGuru called PrivatePrint
, by Rob A. Fraydl (no link because it's not a CodeProject article). I was looking for something that would allow me to print headers and footers as well as the contents of the current window, and this class was almost perfect. The ony thing I had to add was a way to print a line of text that exceeded the width of the paper (see the PrintEx()
function in this class).
My add-on function added the ability to break lines on word boundaries (everywhere there was a space) so I could wrap text on the printed page. In order to break the line of text, I utilized my QStringParser article.
Adding To The System Menu
Since the display application doesn't have a menu, we may want to add some menu functionality to the system menu. The system menu is the menu that's displayed when you click on the application's icon in the title bar. The act of changing the system menu is fairly simple. Just add the following code to your application's InitInstance
function:
HMENU pSysMenu = ::GetSystemMenu(m_pMainWnd->GetSafeHwnd(), FALSE);
if (pSysMenu)
{
::InsertMenu(pSysMenu, 0, MF_BYPOSITION | MF_STRING, ID_SHOW_MESSAGE1,
"Show Message 1");
::InsertMenu(pSysMenu, 1, MF_BYPOSITION | MF_STRING, ID_SHOW_MESSAGE2,
"Show Message 2");
::InsertMenu(pSysMenu, 2, MF_BYPOSITION | MF_SEPARATOR, 0, "");
}
After you retrieve the menu, simply add your new menu items to it. In the code above, we added our custom menu items to the top of the menu, and separated them from the original menu items with a menu separator. Easy stuff.
Next, we need to add our menu item IDs to the resource.h file. The easiest way to do this is to simply add a new menu to the application's resources, and create the menu items in the new menu. Well, it's kind of easy. When I did this, and then tried to add handlers via the Properties view for the CMainFrame
message map, the ID's showed up as 32771 and 32772 instead of the names I gave them. I had to go back and re-display the menu resource and change the ID names in order for them to show up properly in the message map. Sometimes VS2005 just pisses me off. In any case, I then went back and added the handlers in CMainFrame
.
The final step is to add a handler for the Windows WM_SYSCOMMAND
message. Once you've added the handler, replace the contents of the function with the following code:
void CMainFrame::OnSysCommand(UINT nID, LPARAM lParam)
{
switch (nID)
{
case ID_SHOW_MESSAGE1 :
case ID_SHOW_MESSAGE2 :
PostMessage(WM_COMMAND, nID, 0);
break;
default :
CFrameWnd::OnSysCommand(nID, lParam);
break;
}
}
This version of the function handles our own commands by reflecting them back to CMainFrame
. Any message that wasn't one of ours is assumed to be a system command, and is handled in the "default" case. If you want to perform some custom processing before passing the system command to the base class, refer to MSDN's documentation regarding the system command ID's that are sent to this function. It doesn't list ALL of the commands, so you may need to fish around on the web for more detailed info.
If you want the full low-down on what I discovered regarding VS2005 while writing this code, go look at my Modifying the System Menu article. Like a lot of the rest of MFC, changes were made to the OnSysCommand
function that you should be aware of those changes if you plan on implementing system menu modifications in your own applications.
End of Part 5
Due to the length of this article, I've decided to break it up into several parts. If the site editors did what I asked, all of the subsequent parts should be in the same code section of the site. Each part has it's own source code, so as you read the subsequent parts, make sure you download the source code for that part (unless you're doing manually all the stuff I'm outlining in the article in question).
In the interest of maintaining some cohesiveness (and sanity), please vote on all of the parts, and vote the same way. This helps keep the articles together in the section. Thanks for understanding.