Exploring the TimeCheck Application (Part 10)

Last week you learned about the remaining major elements of the GroupSettings class in the Exploring the TimeCheck Application (Part 9) post. Like many modern applications, you now see how to create an application that relies on both localized user settings (the UserSettings class) and work group-wide settings (the GroupSettings class). Both classes rely on XML data files to create a database of settings that the application relies upon for configuration needs. The use of XML data files makes the application incredibly portable. It’s important to remember the sources of settings information this application:

 

  • The user manually configures some of the settings in the UserSettings class by selecting items such as the default task and project.
  • The administrator manually configures some settings in the UserSettings class by selecting items that only the administrator can see.
  • The application automatically provides default settings in the UserSettings class when the application hasn’t been configured by either the user or the administrator.
  • The administrator manually configures all settings in the GroupSettings class.
  • The application doesn’t automatically supply any defaults for the GroupSettings class and simply displays blank screen areas when there are no values to display.


I wanted to reiterate these settings requirements before moving on because there were some questions on the part of a few readers as to how the settings worked. I’m not showing the security features of this example just yet—for right now, it’s important to focus on the functionality required to perform the desired tasks. However, I do encourage developers creating production applications to include security as part of the application design and development process (as I did when putting this example together). You can’t bolt security on as an afterthought. However, I have chosen to not discuss the security features just now in order to make the example easier to understand.

Because I’m still answering some questions about this application, I’ve decided to keep today’s post simple. Look carefully at frmConfigure (see Exploring the TimeCheck Application (Part 3) for details on the frmConfigure configuration). This form contains two combo boxes and two list boxes that you must fill at various times during the use of the form. For example, you must fill all four of them when initially displaying the data. Any change to the two list boxes must also appear in the two combo boxes. In addition, changes to group data must appear every time the user decides to make any sort of change to the user settings, so you have an opportunity here for interactions between multiple sessions. Because of the potential for problems, the frmConfigure class uses a method named FillList() to fill all four lists any time there is a chance for changes. The following code shows how FillList() works.

private void FillLists()
{
   // Check for a list of tasks.
   if (GroupData.TaskList != null)
 
   // Fill the default and administrator lists with data.
   {
      foreach (Task Item in GroupData.TaskList)
      {
         lstStandardTasks.Items.Add(Item.Name);
         cbWorkType.Items.Add(Item.Name);
      }
   }
 
   // Check for a list of projects.
   if (GroupData.ProjectList != null)
 
   // Fill the default and administrator lists with data.
   {
      foreach (Project Item in GroupData.ProjectList)
      {
         lstStandardProjects.Items.Add(Item.Name);
         cbProjectName.Items.Add(Item.Name);
      }
   }
 
   // Add the "None" entry to the two user lists.
   cbWorkType.Items.Add("None");
   cbProjectName.Items.Add("None");
 
   // Choose the user-supplied default (if any).
   if (UserData.DefaultTask != null)
      cbWorkType.SelectedItem = UserData.DefaultTask;
   if (UserData.DefaultProject != null)
      cbProjectName.SelectedItem = UserData.DefaultProject;
}

Any time there is any change to any of the data that frmConfigure manages, the code calls FillList() to ensure all of the lists are updated. Using a single method like this makes it possible to update the application without any potential problem with method interactions.

In this case, the code works with GroupData.TaskList and GroupData.ProjectList to obtain entries for the two lists associated with a particular data type. The user lists must also contain a None entry, so the code adds this item to the two combo boxes after the lists are filled. Having the None option last helps ensure that the user actually reviews the lists of potential choices before accepting the None option. Using a foreach loop to fill each list is extremely efficient.

The FillList() method must also interact with UserData to automatically choose the user’s selection of a default task and a default project. Remember that the default selection, the one automatically created by the application, is None. When the user hasn’t made a selection, the Configure TimeCheck dialog box displays None for these two entries.

This code provides a subtle form of error handling. Theoretically, GroupData.TaskList, GroupData.ProjectList, UserData.DefaultTask, and UserData.DefaultProject should never be null when this piece of code is called. However, the code makes a quick null check anyway just to ensure it doesn’t attempt to work with a null data value. This quick check will reduce potential errors without affecting performance in any noticeable way. Using this sort of check will help make your applications more robust too.

Well, that’s it for this week. Please keep those questions coming. I realize that this example is a bit more complicated than the ones you’re used to seeing in books, so I want to be sure I don’t leave anyone behind. Be sure to send your questions to me at John@JohnMuellerBooks.com.

 

In Praise of Dried Beans

One of the more amazing vegetables in the garden is the green bean. Green beans typically take little work to grow, produce well, and don’t appear to have many problems (with the exception of mold in wet years). We grow the bush variety because they don’t require a trellis. You can eat green beans in all sorts of ways—raw by themselves, cooked, in salads, and even fried. What most people don’t realize is that the uses of green beans don’t end there. You can also use green beans dried. Simply let the green bean stay on the plant until the shell is completely dry (usually after a few frosts).

Dried beans have a significant advantage over other items you grow. Unlike most items, they require no preparation. You can simply pick them, put them in a bucket, put a lid on the bucket, and then stuff it in a cool, dry place. That’s it! The beans will stay good almost indefinitely. I just finished shelling the last of our dried beans from last year. There was no deterioration of the bean whatsoever. Rebecca will use them in baked beans, soups, and in salads. Dried beans are also quite high in nutrients, making them a great food value. For example, if you make them into baked beans, a single serving supplies 28 percent of your daily requirement of iron.

Before I get e-mail about the relative merits of other vegetables, yes, you can store root vegetables such as potatoes in your basement without doing anything special to them. In addition, winter squash also lasts quite well in the basement without any special preparation. However, in both cases you face the problem of having to use the items by February or (in a good year) March. The winter squash tends to start rotting by that time and the potatoes start to get soft in preparation for sprouting. Dried beans appear to have no such limitation.

Of course, the big thing is to ensure that the bean really is dried. We keep the beans on the plants until late fall after a few frosts have killed the plant completely. The beans should rattle within the shells when shaken. The outside should be a nice tan color in most cases and should feel quite dry. The shells will also be a bit on the hard side, rather than soft as a green shell will be.

Don’t worry if you see a bit of discoloration on the shell. That’s normal. If you see a little discoloration, shell a bean or two to see for yourself that the beans inside are shiny and that the skin is intact. Even if the bean is a little dirty, it’s acceptable to use as long as the skin is intact.

The one thing you must do before using beans you dry yourself is to wash them. The beans do pick up a few contaminants during the drying process. You don’t use soap and water. Just place the beans in a colander and rinse thoroughly. Make sure you move the bean around and get all of the dirt off. When you see that the water is coming out of the colander without any dirt, the beans are probably clean.

The bean is one of those items with a nearly unlimited shelf life that’s both nutritious and delicious. The fact that you can use them green or dry, raw or cooked, makes them exceptionally versatile. Even a small garden has space for some of these marvelous plants. Let me know your thoughts about beans (both green and dried) at John@JohnMuellerBooks.com.

 

Harvest Festival 2012

Last year I told you about some of the things we do for Harvest Festival in Fun is Where You Find It! (Part 3) post. Of course, every year has some similarities. There is the special music and it wouldn’t be Harvest Festival without a few games. Apple cider is always part of our celebration, along with plenty of samples from the garden. Our “kids” (the animals we keep as pets) always play a big role in Harvest Festival too.

As with everything else, this year’s Harvest Festival is a bit different. For one thing, our fruit trees didn’t produce anything. Things started off badly with an early spring that saw the trees bloom well before they should have. A late frost killed off a lot of the blossoms before the pollinators were even out to pollinate them. After that, some heavy winds knocked off a few more blooms. The fruit that did manage to set was killed off during this summer’s drought (we couldn’t even attempt to water all our trees). The result is that we ended up buying two bushels of apples so that Rebecca could make me some apple chips. I know that buying the apples by the bushel was a lot less expensive than buying them in the store, but even so, I wish we hadn’t had to do so.

Nothing goes to waste when we work with items from the garden. Of course, I use one of the apple peelers that produce the really long strands of apple peels. This year is the first time that we’ve had laying hens, so I was curious to see what they would make of the apple peels. They didn’t disappoint. One hen would grab an end of a peel and fly up to the nest box, while another would grab the other end to try and get the peel from the first hen. The two would then play this silly looking game of tug of war, even though there were lots of peels in the dishes. Both chickens just insisted that they really must have that single peel. By the time the chickens had played with the peels for a while, we had apples strung between the windows, rafters, nest box, and the dishes. It looked like some sort of crazy spider web created by a demented spider. By morning, all of the apple peels were gone, which also surprised me considering I had peeled 30 apples to get them. The chickens certainly like fresh fruit.

We don’t just process fruit during Harvest Festival. Our friends offered us some tomatoes and we gratefully accepted them considering our own tomatoes have had an anemic output this year. Rebecca turned the first bushel into salsa, catchup, and a canned salad. We hope to get two additional bushels for juice, whole canned tomatoes, and a bit for wine making. A lot of people enjoy my tomato wine.

It looks like this is going to be a stellar squash year. We have squash vines growing everywhere. Normally, the vines stay within the 40′ × 60′ area as long as we redirect them a bit. This year we have vines trying to grow into the grass and along the rows. There is a squash plant vying for space in one of the tomato cages and slowly edging the tomato out. I saw one trying to grow up the side of an eggplant and another is heading toward our okra. A vine that might normally produce three really nice squash has produced five, six, or possibly more (it’s such a mess out there that I’m having a hard time counting them all). Rebecca has also made all of the zucchini chips we need for the year. (See the Making Use of Those Oversized Zucchinis post for details.)

As I’ve always said, there is something interesting going on with each year. We never get bored here. One of the rewards of being self-sufficient is that you do see the changes wrought by the weather. What do you find exciting about the fall months of the year? Let me know at John@JohnMuellerBooks.com.

 

Interesting Money Issues for Computer Users

I was reading an article by John Dvorak entitled, “The Secret Printer Companies Are Keeping From You” recently that caused me to think about all of the ways in which I look for ways to reduce the costs of my computing experience without reducing quality. In this article, John discusses the use of less expensive replacements for inkjet printers. I found the arguments for the use of less expensive inks compelling. Then again, I’m always looking for the less expensive route to computing.

I’ve often tried the less expensive solution in other areas. For example, are the white box labels any different than the high end Avery alternatives? I found to my chagrin that this is one time when you want to buy the more expensive label. The less expensive labels often come by their price advantage in the form of less reliable adhesives or thinner paper. This isn’t always the case, but generally it is. When it comes to labels, you often get what you pay for. I tried similar experiments with paper and found that the less expensive paper was a bit less bright or possibly not quite as nicely finished, but otherwise worked just fine. It’s important to look carefully at the cheaper brands when you make a decision to buy them and determine whether there are any actual quality differences and whether you can live with those differences when present.

John is right about more expensive labeled products being passed off as less expensive off brand products. In some cases, I’ve found all sort of items that didn’t quite meet a vendors strict requirements for a labeled product sold as a less expensive off brand product. Sometimes you’d have to look very closely to see any difference at all. I also know that some white box vendors have name brand vendors product equipment with less stringent requirements or possibly not quite as many bells and whistles. The point is, that you can find new products that works almost as well as the name brand for substantially less money if you try.

However, let’s say you’re not willing to take a chance on a white box option. There is also a strong market now in rebuilt and refurbished equipment. Often, this is last year’s model that someone turned back in for the latest product. After a required check of the hardware and possibly a refit of a few items, a company will try to sell it to a new customer at a significantly reduced price. These refurbished items usually work as well as the new products. Because they’re already burned in, there is also less of a chance that you’ll encounter problems with them. Even Apple has gotten into the refurbished product game—I’m planning to buy a refurbished third generation iPad in the near future.

Getting systems designed for expandability is another good way to extend your purchasing power. You might not be able to afford absolutely everything you want today. Get what you can afford and then add onto the system later. This is the route I take quite often. I’ll get a motherboard and other system components that offer room for expansion and then I add what I need until the unit is maxed out. I can then get the next generation setup, move the parts that are still viable, and use the parts that are outdated for some other purpose. Often I’ll take pieces and put them together for a test system or for a unit that I’ll use to run an older operating system.

Some people have asked why I go through all this trouble when you can get a truly inexpensive system from a place like TigerDirect for under $500.00. I’ve looked at this systems closely enough to figure out that they usually won’t work for my needs right out of the box—I always end up adding enough to bring the price near to $1,000.00 and usually more. Once the system is delivered, I find there is little documentation and that the box is too small to accommodate any upgrades. I would have saved money in the long run by getting a better system that has expandability built in. Here is where the trap occurs. There is a point where you have cut costs so much that the PC ends up being a throwaway that proves frustrating. It’s false economy for a power user (the systems often work just fine for students or users who don’t run anything more complex than a word processor).

Getting the most out of your computer purchasing power takes thought and research. What has your best purchasing decision been? How about the worst mistake you’ve made? Let me know your thoughts about computer hardware purchases at John@JohnMuellerBooks.com.

 

Controlling Windows 8 Support Costs

Windows 8 presents a new interface and new methods of working with the computer. Microsoft obviously wants to try to create a new image as a mobile device vendor by putting a tablet interface on a traditional desktop operating system. Whether the strategy will work or not is a significant topic of discussion. Some pundits, such as Wood Leonard, consider the new Metro interface as the worst mistake that Microsoft has ever made. Other authors, like Ben Woods, are a bit more positive. You can find opinions between these two extremes over the entire Internet.

Change is always incredibly uncomfortable. I still remember the ruckus caused by the, now positively viewed, changes in Windows 95. Of course, Microsoft has had a few turkeys to its credit too, such as Vista. My experience has been that time shows whether a significant change will work or not. In this case, Microsoft really does have its work cut out for it. A less aggressive stance might have worked better, but the die is cast and we’ll soon see the results in terms of sales.

Windows 8 isn’t all bad news and you certainly shouldn’t approach the product from that perspective. While writing Windows 8 for Dummies Quick Reference, I was also able to come up with some interesting product features and ways to make Windows 8 a little less of a shock to users. Some of these techniques, such as using a Start menu substitute, appear in my book. In fact, I discuss one of those alternatives in detail, ViStart. My conclusion is that it’s possible to reduce your support costs through careful management of the Windows 8 setup. You may also find that new security features actually reduce support costs by making it less likely that users will corrupt their systems.

Whether you like Windows 8 or not, you’re eventually going to have to support it unless you cut off all user access to other devices. Everything I’ve been reading as of late tells me that enterprises would just love to keep users from bringing in their own devices, but it’s unlikely to happen anytime soon. With that in mind, I wrote an article entitled, “8 Ways to Reduce User Training Costs for Windows 8” that will help reduce the pain for administrators. I’d like to get your feedback on the article at John@JohnMuellerBooks.com. More importantly, tell me your ideas for making the Windows 8 transition a little easier. If I receive enough good ideas, I’ll revisit this topic later.

 

Exploring the TimeCheck Application (Part 9)

The Exploring the TimeCheck Application (Part 8) post helped you explore the first part of the GroupSettings class. As promised, this week’s post will help you complete the GroupSettings class. Of course, one of the most essential tasks you perform with this sort of class is creating a new instance of the class as needed. In many cases, you can use the default constructor, which requires no input and produces no output. However, when working with settings, you need to create a constructor that sets the settings to a known state as shown here.

// Define a constructor to initialize default values.
public GroupSettings()
{
   CustomProject = false;
   CustomTask = false;
   ProjectList = null;
   TaskList = null;
}

Setting CustomProject and CustomTask to false makes sense because you don’t want the user to be able to create custom projects or tasks unless the administrator wants to allow it. You may have heard the phrase principle of least privilege used in regard to application security and this is an example of such a setup. The application assumes that the user doesn’t have the right to perform some special task unless the administrator specifically allows it.

What may concern some people is the setting of ProjectList and TaskList to null. When an administrator uses the application for the first time, there aren’t any projects or tasks assigned for the application, so that’s one reason to set these List objects to null. However, the second reason is to signal an error to the application. When a user starts the application and the application can’t find the network drive containing the group settings, it’s important to signal the application that an error has happened. This error won’t prevent the application from continuing, but it will prevent the user from logging in using standardized projects and tasks. As a result, the application must have some way of signaling this problem and dealing with it. You’ll see how this all works in a later post.

In addition to creating new GroupSettings objects, the class must be able to read and write GroupSettings to the network drive as an XML file. This one file contains the settings that everyone will use. There are many permutations to discuss with regard to this sort of file. For example, you need to consider whether multiple administrators could possibly modify the file at one time. If so, you need to implement some sort of locking strategy to ensure that only one administrator can open the file for modification at a time. Of course, you don’t want regular users making modifications to the file. For the purposes of clarity, I chose to make things as simple as possible. I’m assuming that there is only one administrator. In addition, I’m assuming that because of the interface modifications you’ll learn about in a later post, that only the administrator will have access to the interface elements required to make the changes. With these restrictions in mind, here is the code the example uses to save data to the file.

// Create a method for saving the settings.
public static void SaveSettings(GroupSettings Settings)
{
   // Obtain the network path.
   String NetworkPath = UserSettings.GetUserSettings().NetworkPath;
 
   // Exit when the application hasn't been configured yet.
   if (NetworkPath == null)
      return;
 
   // When the path doesn't exist, the group hasn't been set up to
   // use the application, so you need to create the path.
   if (!Directory.Exists(NetworkPath + @"\GroupData"))
      Directory.CreateDirectory(NetworkPath + @"\GroupData");
 
   // Check for the file. If it doesn't exist, create it.
   if (!File.Exists(NetworkPath + @"\GroupData\AppData.Config"))
   {
      FileStream NewFile = File.Create(
         NetworkPath + @"\GroupData\AppData.Config");
      NewFile.Close();
   }
 
   // Create an XML serializer.
   XmlSerializer DataWriter = new XmlSerializer(typeof(GroupSettings));
 
   // Define a data stream.
   StreamWriter Output = new StreamWriter(
      NetworkPath + @"\GroupData\AppData.Config");
 
   // Perform the data write.
   DataWriter.Serialize(Output, Settings);
 
   // Close the stream.
   Output.Close();
}

The code begins by creating a path variable that holds the location of the network storage location. This location is unique for an individual machine and is stored as part of the UserSettings (see Exploring the TimeCheck Application (Part 7)). When NetworkPath is null, it means that the application hasn’t been properly configured and there is nothing for the application to do, so it returns a null value to the caller. The application must also ensure that the GroupData folder exists and that it contains the AppData.Config file. When the AppData.Config file is missing, the application must create it.

 

Remember that this task takes place across a network connection. Normally, I’d include some error trapping code here, but left it out for the sake of clarity. A production version of this application will include the required error trapping and error handling code.


Once the application establishes that there is a file to use to hold the data, it creates an XmlSerializer object, DataWriter, and defines its output as type GroupSettings. The application then creates a StreamWriter to actually output the data and then serializes the data as XML in the output. This entire process is precisely the same one that is used for the UserSettings class, except that it happens across a network connection (with all of the potential for problems that a network connection can cause). In fact, I encourage you to compare this method to the one used for the UserSettings class.

Everyone will use the method to obtain the data from the file. You don’t need to implement much in the way of special handling because the file is read and immediately closed. If there are a lot of users to accommodate, you may possibly need to add file locking features to the application. However, since there is no modification of data taking place, allowing shared access works just fine in most cases. Here is the code used to read data from the AppData.Config file.

// Create a method for obtaining the current settings.
public static GroupSettings GetSettings()
{
   // Obtain the network path.
   String NetworkPath = UserSettings.GetUserSettings().NetworkPath;
 
   // Exit when the application hasn't been configured yet.
   if (NetworkPath == null)
      return null;
 
   // Check for the existence of the group settings file. If it
   // doesn't exist, exit.
   if (!File.Exists(NetworkPath + @"\GroupData\AppData.Config"))
      return null;
 
   // Create an XML deserializer.
   XmlSerializer DataReader = new XmlSerializer(typeof(GroupSettings));
 
   // Define a data stream.
   StreamReader Input = new StreamReader(
      NetworkPath + @"\GroupData\AppData.Config");
 
   // Perform the data read.
   GroupSettings GroupData = (GroupSettings)DataReader.Deserialize(Input);
 
   // Close the stream.
   Input.Close();
 
   // Return the data.
   return GroupData;
}

It’s a good idea to compare this method to the one used with the UserSettings class to read the user data. The basic concept is the same. The only difference is that this class works across a network connection, so the production version of the application will likely contain additional error trapping and error handling code.

Well, that’s it for this week. Next week we’ll discuss any remaining internal elements or possibly move on to a few of the user interface elements depending on what sorts of questions I receive about the application. In the meantime, please let me know about any questions or concerns you have about the application at John@JohnMuellerBooks.com. You can find the next post in this series at Exploring the TimeCheck Application (Part 10).

 

Picking a Delicious Ear of Corn

Nothing is quite so good as a delicious ear of corn picked fresh from the garden. A freshly picked ear of corn is sweeter and more delicious than any ear of corn you’ll ever taste. The fresher the ear, the more delicious the taste. Of course, that delicious ear of corn starts with the correct planting technique and choice of corn variety. We happen to prefer the Bodacious variety because it produces evenly colored corn with a great taste. The ears are normally full (indicating good pollination), the stalks don’t seem to break quite as often, it’s a little less susceptible to pests, and we find that the ears are often larger. We’ve also tried a number of other varieties such as Kandy Corn (somewhat sweeter) and Serendipity Bi-color Corn (interesting color combinations and ripens somewhat earlier). So far, we like Bodacious the best, but you need to choose a corn variety that works well in your area. Take factors such the type of soil, variety of pests, and weather into account when making your choice.

Planting the seeds correctly is also important. We have quite a bit of high wind in this area, so we plant the seeds one foot apart in rows and each of the rows two feet apart. If you plant the corn seeds too closely together, the corn won’t ever produce a strong stalk. In fact, a worst case scenario is that the corn won’t produce any ears. Planting the corn too far apart makes the stalks more susceptible to wind damage and reduces pollination. You may get full sized ears, but you won’t get ears that are full of kernels. You may have to plant your corn differently depending on your area to get optimal results.

The tough part is figuring how when to pick the corn. Yes, you see the ears pop out sometime after the corn tassels (corn cross pollinates through wind action—it doesn’t depend on a pollinator to pollinate it). The tassels are the male flowering member of the plant, while the kernels (ovules) are the female flower member of the plant. These female members reside in a husk and sent out silks to receive the pollen. Pollen travels down the silks to the ovules and pollinates them. Each ovule requires individual pollination, which is why you can see ears with only a few kernels or you can see one or two ovules that didn’t pollinate in a given ear. The point is that the pollination occurs, the kernel grows, and then there is a magical period when the kernels are full of delicious sugar-filled liquid that is absolutely delightful to ingest. After that, the sugars begin to turn to a less tasty starch.

The silks are part of the key to discovering when to pick the corn. When the silks whither and turn black, you know they have done their job—the kernels are pollinated (or at least as pollinated as they’ll get). However, the kernels aren’t instantly fully sized. The dying silks tell you that pollination is over and that you’ll soon have tasty corn to eat.

The next clue is to feel the ears. Gently place your hand around an ear and you can feel the kernels growing. It takes a while, but you’ll eventually developer a touch that tells you that the kernels are getting larger. At some point, you’ll stop feeling any growth. In addition, the ears will feel solid, without any gaps between kernels.

At this point, you can peak at the ears. Gently pull the husk back to reveal the tip of the ear. The kernels at the tip develop last, so the kernels at the bottom are always riper and fuller than the kernels at the tip. When the last few rows start the look the right color and fullness, try sticking a thumbnail into one of the kernels. If you see a liquid come out, the corn is ready to pick.  If there is no liquid, carefully smooth the husk back over the ear. It should ripen normally within a day or two.

Of course, sometimes the kernels at the tip of the ear aren’t pollinated or may not grow right for other reasons. Sometimes a corn borer ruins your day. Earwigs are also a problem at times (and beneficial at others). Never allow the corn to stay on the stalk for more than a week after you feel full ears. If you have doubts, pull one ear, fully husk it, and evaluate the results. Cutting the kernels from the ear and trying a few raw will tell you quite a bit about the status of the corn.

Sweetcorn—it’s the stuff of summer. What are your experiences with corn? Do you grow it yourself or get it from a roadside stand? Let me know at John@JohnMuellerBooks.com.

 

The Bionic Person, One Step Closer

When The Six Million Dollar Man first arrived on the scene in January of 1974, most people thought it was simply another science fiction television show. The addition of The Bionic Woman in January 1976 was just more good entertainment. The only problem is that these shows really aren’t just entertainment anymore. I’ve already discussed the use of exoskeletons to help those who have lost use of their legs in Exoskeletons Become Reality. No, none of the people using these devices can run 60 mph or make incredible leaps—that part is still science fiction, but I’m beginning to wonder for how long. (Just in case you’re interested, there is also a bionic arm in the works.) Today I read an article entitled, “Australians implant ‘world first’ bionic eye” that appears to take the next step in the use of bionics with humans. Never in my wildest dreams did I imagine that these things would happen when I originally wrote Accessibility for Everybody. I’m happy that they have !

Of course, the bionic eye of today is quite limited. Early bionic eyes have relied on a camera built into a pair of glasses to help someone see. You need a lot of hardware to make these eyes work and the best you can hope to achieve in many cases is to see light and dark. The part I find interesting about this new bionic eye is that the apparatus is actually inserted into the person’s living eye on top of the retina (yes, you still need the glasses, but just for the camera part of the technology)! This is a true innovation because it means that we’re headed in the direction of bionics becoming nearly impossible to detect. Once this technology leaves the laboratory, the doctors envision the person being able to see a 1,024 × 1,024 image. OK, that’s not HDTV standard, but it’s a lot better than someone who is blind has today.

In many respects, the technology advances we’re seeing today are both amazing and a bit scary at the same time. Scientists are literally probing every element of the human body, discovering how they work well enough to help people live better lives, and then using technology to fill in the gaps. I see a time coming when no one will have to suffer with a devastating loss that significantly limits the enjoyment of life. What do you think about the coming of the real bionic person? How far do you think this technology might go? Let me know your thoughts at John@JohnMuellerBooks.com.

 

Displaying a Telephone Number in VBA

A number of readers of VBA for Dummies have now asked me how to insert a telephone number into documents using a macro. It turns out that this is a common topic online. Microsoft should have added this feature some time ago, but apparently hasn’t (and probably won’t at this point). Most of the solutions I’ve seen are complex when this really isn’t a complex task. This post discusses three techniques:

 

  • Insertion as text
  • Insertion in a field
  • Insertion in a control on the document.


Insertion as Text

The first approach is to insert the telephone number as text. You obtain unformatted input from some source, use the Format() function to format it, and then insert it at the current cursor location.

The first thing you need to do is gain access to the developer tools if you’re using Office 2010. The VBA in Office 2010 post tells you how to perform this task. Create a new module for your document or template using the instructions I’ve provided in the Adding a Module section in Chapter 3 of VBA for Dummies. At this point, you can create a new macro for inserting the telephone number. Here’s some simple code you can try.

Sub InsertTelephoneNumber1()
    ' Obtain the telephone number.
    Value = InputBox("Type the telephone number", "Telephone Number")
     
    ' Insert the telephone number directly.
    Selection.InsertAfter Format(Value, "(###) ###-####")
End Sub

The InputBox() call simply provides the means for obtaining a telephone number. You can use any source desired. The Selection.InsertAfter call inserts any text you provide after the current cursor location. The Format() function takes the input you received as the first argument (Value) and formats it using the picture you provide. You can find information about the Format function on MSDN at http://msdn.microsoft.com/library/office/gg251755.aspx.

Insertion as Field

The second approach is working with a field. It’s a little harder to create a field that contains a telephone number, but only slightly. Formula field codes are somewhat misunderstood and this particular example uses a trick that I haven’t seen others use online for some odd reason. This is one time when using the Macro Recorder will save you time. I discuss using the Macro Recorder in the Using the Macro Recorder section in Chapter 3 of VBA for Dummies. I used these steps in Office 2010 to create the example that follows later.

 

  1. Start the Macro Recorder. Call your macro InsertTelephoneNumber2 or anything else appropriate and add a comment.
  2. Choose Insert | Quick Parts | Field. You see the Field dialog box shown here.
    Telephone01
  3. Select the = (Formula) option in the Field Names list and then click Formula. You see the Formula dialog box shown here.
    Telephone02
  4. Type any ten digit number in the Formula field as shown.
  5. Type (###) ###’-‘#### in the Number Format field as shown. Notice especially the use the single quotes (‘) around the dash. This addition is where most people make their mistake in working with these formula fields. Any text you surround with a single quote like this is treated as text. Otherwise, the dash will simply disappear.
  6. Click OK. Word will add the field to the display. Notice that the field is formatted precisely as you’d expect, including the dash.
  7. Stop the Macro Recorder. Word will create the macro for you.


At this point, you have a macro that will insert a field containing a specific telephone number. To make the field more generic, you’ll need to modify the code to accept input. Here’s a simple version that you can try.

Sub InsertTelephoneNumber2()
    ' Obtain the telephone number.
    Value = InputBox("Type the telephone number", "Telephone Number")
     
    ' Use a field to display the telephone number.
    Selection.InsertFormula Formula:="=" + Value, NumberFormat:= _
        "(###) ###'-'####"
End Sub

Again, the code obtains a telephone number using an InputBox. You can use any source desired. It then calls Selection.InsertFormula with the content you want to use as the first argument and the format for that content as the second argument. Notice that that call relies on named arguments to avoid any potential problems. In addition, notice that the equals sign (=) is in double quotes (“), but that Value is outside of those double quotes because you want the value it contains, and not the word “Value” as input to InsertFormula.

Insertion as Control

The third technique is to create a control, allow the user to input the data, and then reformat the text in the control. Controls are different because they require a little better understanding of how Word works. When you have a control, it generates events. The event handler listens for those events and then does something about them. Think about a house fire. There is a fire in your home. So, you go out to the woods and yell, “There is a fire in my home!” Nothing happens. That’s because there is no handler in the woods for the event of a fire in your home. When you call 911, the 911 operator, the event handler, listens to you yell, “There is a fire in my home!” Now something happens. The 911 operator calls the fire trucks and sends them to your house to put out the fire.

It’s time to see how all of this works in the real world. I’m assuming that you’re using Word 2010 and that you have saved your document in .DOCM (document with macros) format. The following steps get you started.

 

  1. Choose Developer | Plain Text Content Control. You see a new Plain Text Content Control added to your document.
  2. Choose Developer | Properties. You see the Content Control Properties dialog box shown here.
    Telephone03
  3. Type Telephone Number in the Title field and TN in the Tag field. Click OK. Word changes the properties of the Plain Text Content Control.
  4. Choose Developer | Visual Basic. You see the Visual Basic editor open.
  5. Right click the Project (<Document Name>)\Microsoft Word Objects\ThisDocument object in the Project Explorer window and choose View Code from the context menu. You see a module window open. (If you don’t see the Project Explorer window, choose View | Project Explorer to display it.)
  6. Select Document from the Object drop down list box in the module window (the one on the left). Word may automatically create an event handler for the New event. Remove this code because you don’t need it.
  7. Select ContentControlOnExit from the Procedure drop down list box in the module window (the one on the right). Word automatically creates the Document_ContentControlOnExit() event handler for you.


Now that you have an event handler, you have to tell it to do something. Just as the 911 operator needs to know what to do, so does Word. Here is an example of the code you could use to automatically format user input as a telephone number.

Private Sub Document_ContentControlOnExit( _
    ByVal ContentControl As ContentControl, _
    Cancel As Boolean)
 
    ' Verify the control's name.
    If ContentControl.Tag = "TN" Then
     
        ' Format the data in the control.
        ContentControl.Range.Text = _
        Format(ContentControl.Range.Text, "(###) ###-####")
    End If
End Sub

The Document_ContentControlOnExit() event handler receives two variables when Word calls it. The first contains all the information you need about the control that generated the event. The second lets you cancel the action when you detect a problem.

You only want to change controls that have a Tag value of TN. So, the first task is to detect controls of this type. The code then uses the ContentControl.Range.Text property to change the control’s text using the Format() function.

Bottom Line

These are three simple methods for adding a formatted telephone number to a Word document. However, you can extend these methods to format any sort of content. Just remember some of the rules you’ve learned here about working with formatted fields and then spend a little time with the Microsoft documentation to learn what else you can do. Please let me know if you have any questions about these techniques at John@JohnMuellerBooks.com.