Exploring the TypingBuddy Application (Part 7)

In the previous post, Exploring the TypingBuddy Application (Part 6), you learned how the user can create custom messages for TypingBuddy. These messages make it possible for TypingBuddy to suggest activities that interest the user. The form used for this activity, frmAddEdit, can display a single message at a time for either adding or editing a message. The focus of this week’s post, frmMessages, makes it possible to manage the list of configured messages, as well as add new ones. It provides access to the functionality required to add, edit, and delete messages as shown here.

TypingBuddy0701

The setup required to create this form appears in the Exploring the TypingBuddy Application (Part 3) post. Before this form can do anything else, it must have a copy of the current message list. It uses this list to populate the Messages list box when the form loads. Here’s the code needed to perform this task:

// Create a copy of the database.
TBMessages Settings;
 
private void frmMessages_Load(object sender, EventArgs e)
{
   // Initialize the settings.
   Settings = TBMessages.LoadSettings();
 
   // Determine if there are any messages to load.
   if (Settings.MessageList != null)
 
      // If there are messages to process, check them one at a time.
      foreach (TBMessage ThisMessage in Settings.MessageList)
 
         // Display just the title in the listbox.
         lstMessages.Items.Add(ThisMessage.Title);
}

The example begins by creating a TBMessages object, Settings, to hold the list of messages along with the other settings used by the application. In frmMessages_Load(), the application instantiates this object by calling TBMessages.LoadSettings(). When Settings contains one or more TBMessage entries, the application loads them into lstMessages so that the user can see them immediately after the form loads.

The form contains four buttons. The code for Close is very simple—all it does is pass back a DialogResult value of Cancel. The Add button is a little more complex as shown here:

private void btnAdd_Click(object sender, EventArgs e)
{
   // Create the required form.
   frmAddEdit AddMessage = new frmAddEdit();
 
   // Provide the required resources.
   if (Settings.MessageList != null)
      AddMessage.MessageList = Settings.MessageList;
 
   // Display the form.
   if (AddMessage.ShowDialog(this) == DialogResult.OK)
   {
      // Retrieve the new settings.
      Settings.MessageList = AddMessage.MessageList;
 
      // Save the settings.
      TBMessages.SaveSettings(Settings);
   }
 
   // Clear the message list.
   lstMessages.Items.Clear();
 
   // Determine if there are any messages to load.
   if (Settings.MessageList != null)
 
      // If there are messages to process, check them one at a time.
      foreach (TBMessage ThisMessage in Settings.MessageList)
 
         // Display just the title in the listbox.
         lstMessages.Items.Add(ThisMessage.Title);
}

The code begins by creating a frmAddEdit object, AddMessage. It then passes the current list of messages to AddMessage and displays the form on screen where the user will provide values for a new message. When the return value is DialogResult.OK, the code retrieves the updated list of messages and places them in Settings.MessageList. The code then saves the new list of messages to disk.

At this point, the code must update the user’s view of the messages. It does this by calling lstMessages.Items.Clear() first to clear any existing messages. When Settings.MessageList contains messages (it may not because the user may have chosen to click Cancel instead of adding a message), the code adds each of the message titles to lstMessages.

The Edit button is the most complex of the four buttons. It not only must choose which message to edit, but it must also configure the frmAddEdit object to display a message editing dialog box, rather than a message adding dialog box. Of course, this event handler will also need to perform all of the tasks performed when adding a new message. Here’s the code you use to perform these tasks.

private void btnEdit_Click(object sender, EventArgs e)
{
   // Create the required form.
   frmAddEdit AddMessage = new frmAddEdit();
 
   // Configure the form.
   AddMessage.Text = "Edit Existing Message";
   AddMessage.btnAdd.Text = "&Edit";
   AddMessage.btnAdd.AccessibleDescription =
      "Edits an existing item in the list.";
   AddMessage.toolTip1.SetToolTip(AddMessage.btnAdd,
      "Edits an existing item in the list.");
 
   // Set up for an edit.
   AddMessage.IsEdit = true;
 
   // Make sure there are records to edit.
   if (Settings.MessageList != null)
   {
      // Provide the required resources.
      AddMessage.MessageList = Settings.MessageList;
 
      // Set the record number.
      AddMessage.RecordNumber = lstMessages.SelectedIndex;
   }
   else
   {
      // Display a message and exit when there are no records to edit.
      MessageBox.Show(
         "There are no messages to edit!",
         "Editing Error",
         MessageBoxButtons.OK,
         MessageBoxIcon.Error);
      return;
   }
 
   // Make sure the user has selected an item to edit.
   if (lstMessages.SelectedIndex == -1)
   {
      // Display a message and exit when there are no records to edit.
      MessageBox.Show(
         "You must select a message to edit!",
         "Selection Error",
         MessageBoxButtons.OK,
         MessageBoxIcon.Error);
      return;
   }
 
   // Display the form.
   if (AddMessage.ShowDialog(this) == DialogResult.OK)
   {
      // Retrieve the new settings.
      Settings.MessageList = AddMessage.MessageList;
 
      // Save the settings.
      TBMessages.SaveSettings(Settings);
   }
 
   // Clear the message list.
   lstMessages.Items.Clear();
 
   // Determine if there are any messages to load.
   if (Settings.MessageList != null)
 
      // If there are messages to process, check them one at a time.
      foreach (TBMessage ThisMessage in Settings.MessageList)
 
         // Display just the title in the listbox.
         lstMessages.Items.Add(ThisMessage.Title);
}

This part of the example begins by creating a frmAddEdit object, AddMessage (I simply recycled the name from the previous event handler). The first two changes configure the form so that the user sees the right messages. However, because we’re also focusing on accessibility in this example, the application must also change the settings that affect accessibility. For example, this changes would modify what a user hears through a screen reader. When making your application accessibility friendly, you need to keep these sorts of changes in mind.

Because the application is editing an existing message, rather than editing an existing message, it must also set AddMessage.IsEdit to true. As mentioned in the previous posting, this setting affects how the form reacts to user input and also controls initial configuration.

At this point, the application must pass the list of messages along to AddMessage, just as it did when adding a new message. However, notice the error trapping code that is added in this case. For example, you can add a message when the message list is null, but you can edit a message that doesn’t exist. Likewise, a user must actually select a message in the list in order to edit it—you can’t simply assume which message the user intended to edit.

Only after the conditions are right can the application display the editing dialog box. When the user makes changes and clicks Edit, the application updates the message list and updates the list of messages, just as it does when adding a new message.

The Delete button is unique in that it does change the message list, but it doesn’t rely on frmAddEdit to do it. All that really needs to happen is that the application removes the required record from the message database as shown here:

private void btnDelete_Click(object sender, EventArgs e)
{
   // Make sure the user has selected an item to edit.
   if (lstMessages.SelectedIndex == -1)
   {
      // Display a message and exit when
      // the user hasn't selected a record.
      MessageBox.Show(
         "You must select a message to edit!",
         "Selection Error",
         MessageBoxButtons.OK,
         MessageBoxIcon.Error);
      return;
   }
 
   // Create a temporary message database.
   TBMessage[] Temp = new TBMessage[Settings.MessageList.Length - 1];
 
   // Create a counter for the temporary database.
   Int32 Count = 0;
 
   // Check each of the records for the defined message and delete
   // the selected message by not copying it to the message list.
   foreach (TBMessage ThisMessage in Settings.MessageList)
      if (ThisMessage.Title != (String)lstMessages.SelectedItem)
      {
         // Copy the record.
         Temp[Count] = ThisMessage;
 
         // Update the counter.
         Count++;
      }
 
   // Update the settings.
   Settings.MessageList = Temp;
    
   // Save the new message list.
   TBMessages.SaveSettings(Settings);
 
   // Update the message list.
   lstMessages.Items.RemoveAt(lstMessages.SelectedIndex);
}

The main requirement that the application must adhere to in this case is that the user must select a record to delete. Again, the application can’t assume anything about what the user really intended to do. Good design practices dictate that you make this check and then ask the user to make a selection when there isn’t any selection.

There are many different methods you can use to delete a record from MessageList. The example chooses to use a simple approach that works quite fast. It simply creates a temporary list of messages, Temp, of type TBMessage[] that contains one less record than the original array. It then copies all of the original records to the temporary array, except for the one record that matches the one that the user has selected. After it completes the copying process, the application saves the message list and updates lstMessages so the user sees that the entry is deleted.

We’ve moved up to frmMain. This particular form will require a little more work than you’ve done so far because it’s the focal point of everything that TypingBuddy does. Next week we’ll start a series of posts about frmMain. In the meantime, let me know if you have any questions at John@JohnMuellerBooks.com. You can see the next post in this series at Exploring the TypingBuddy Application (Part 8).

 

Enjoying the Spring Flowers

Spring has come incredibly early to Wisconsin this year. I’m amazed at just how fast everything has budded and flowered. We’ll actually make it to Easter this year after my spring flowers have blossomed and reached their peak. Given that we’re hustling to fit everything in, I haven’t taken a lot of spring flower pictures, but here are a few showing my tulips, daffodils, and grape hyacinth (amongst others).

SpringFlowers01

The flowers that appeal the most this spring are the grape hyacinth, which are especially fragrant for some reason. The odor is downright overpowering at times. It must be the unusually high temperatures that we’ve been experiencing. Interestingly enough, our crocus came up, bloomed, and are already gone for the season.

We have a number of different varieties of daffodils. A favorite of mine this year have white petals with yellow insides:

SpringFlowers02

They look incredibly happy. Of course, the plum trees are blooming as well. This year they’re just loaded with blooms.

SpringFlowers03

I’m just hoping at this point that we actually get to keep some of the fruit. Wouldn’t you know it, the trees just start blooming well and the weatherman has to ruin everything with a prediction of frost. Our weather hasn’t been quite as nice the last few days as it was earlier in the month. That’s part of the problem with an early spring—the trees start blooming early, which exposes them to a greater risk of frost.

Fortunately, the pear trees are just starting to get ready to bloom. The buds have started to burst open a little, but they’re still closed enough that a light frost won’t hurt them.

SpringFlowers04

The apples aren’t even as far along as the pears, so there aren’t any worries with them. I really do hope my plums survive the night. A frost would probably ruin our harvest at this point. It doesn’t pay to worry. The weather will do what the weather will do whether I worry or not, so it’s best just to let things go the way they will. Every year brings it’s own special set of challenges.

So, are you experiencing an early spring this year? If so, what sorts of challenges are you facing? How do you plan to use the early spring to your advantage? Let me know at John@JohnMuellerBooks.com.

 

Moving the Chicken Coop Parts

In my previous post about the chicken coop, Starting a Chicken Coop, I talked about some of the requirements I had looked at when getting the parts for the chicken coop I wanted to build. Three of us worked together to start taking the chicken coop at my friend’s house apart. We worked carefully because some of the parts really didn’t require any deconstruction. Here we are sitting in front of the car used to transport one of the walls intact:

ChickenCoop0201

Cody, our intern, is standing on the left. Kevin, an ex-Seabee and also the brains of this operation, is standing in the middle. I’m on the right. The car was most definitely overloaded with that piece of wall on its back:

ChickenCoop0202

You can’t see it very well in the picture, but the car is definitely riding low. We also loaded up my Explorer and eventually we used my uncle’s truck. It took us a day and a half to break down the chicken coop and move it over to the house. We also obtained some corrugated roofing material from another friend. He had removed it from his house and saved the better looking pieces. By the time we were finished, we ended up with three distinct piles of parts:

ChickenCoop0203

This pile contains some walls we thought we could use intact, the nesting box, some sheet goods, and a bit of fencing. There are also some other bits and pieces that we probably won’t use. For example, the feeding trough it way too long. I’ll deconstruct it and use the wood for another project—absolutely nothing goes to waste around here.

ChickenCoop0204

This pile contains an additional wall that we thought we might need, but weren’t sure about. It also contains some bricks (we probably won’t need them) and the 2 X 4 stock used to put everything together. In addition to 2 X 4 stock, we were able to salvage some 4 X 4s, 2 x 2s, 2 X 6s, and a number of other sizes of lumber.

ChickenCoop0205

This pile contains some fencing parts and the corrugated metal roofing. Actually, we’ll use that metal to surround the entire chicken coop, making it quite durable. The only new materials that the chicken coop will have are some screws (we’re reusing as many as possible) and some tar paper. Otherwise, this chicken coop is made up of pieces salvaged from everywhere, including my own basement (pieces from other projects). This is how recycling should work. Nothing will end up in a landfill anywhere—every component you see in these pictures will be used for something (even if it isn’t in this particular project).

Now that the pieces are assembled, we can start building the new chicken coop. I’ll fill you in on the details in a future post. In the meantime, let me know if you have any questions about the process we’re using at John@JohnMuellerBooks.com.

 

Getting CodeBlocks to Work – An Update

Quite a while ago, at least in computer technology terms, I wrote the Getting CodeBlocks to Work post to make it easier for readers to obtain a good copy of CodeBlocks to use with C++ All-In-One Desk Reference For Dummies. It seems that some of the information I provided at that time has become outdated.

While the http://www.codeblocks.org/downloads/5 link still works fine, some of the other links may not. A reader recently pointed out that the automated installer location has changed. When you go to the original automated installer site, you now see a number of links, including one listed as Download mingw-get-inst-20111118.exe (591.9 kb ).

Clicking the new link will automatically start a download of the latest automated installer. When the download is complete, you can install your copy of CodeBlocks. Another link to the same download is http://sourceforge.net/projects/mingw/. The point is that there is a download that will work with your system, no matter what system you’re using.

If anyone encounters a problem with these latest downloads, please let me know at John@JohnMuellerBooks.com. I want to be certain that you have a great learning experience when working with my books.

 

Configuring Visual Studio for IronPython Use

Last week I discussed a small bit of information I left out of my book in the procedure that begins on page 26 (see Professional IronPython Chapter 2 Step-by-Step Procedure Update for details). It turns out that the small bit of information isn’t the only thing you need to worry about with that procedure. A reader wrote in to mention that the basic settings you choose can also make a difference. If you have Visual Studio configured for Web Development, the procedure won’t work. That’s because of the way Visual Studio treats Web development. The problem is that you won’t have a solution (.SLN file) to save. I encountered this problem once before, but forgot about the issue until the reader wrote to me. Use the following procedure to check your settings:

  1. Choose Tools→Import and Export Settings. You’ll see the Import and Export Settings Wizard dialog box shown here.
    VisualStudioSetup01
  2. Choose Reset All Settings and click Next. The wizard will ask you to save your current settings as shown here. It’s always a good idea to save your settings before even looking at the configuration.
    VisualStudioSetup02
  3. Click Next. You’ll see the Choose a Default Collection of Settings page shown here. This is where the problem lies. I normally choose General Development Settings because I work in a lot of different languages. If your dialog box shows that Web Development is selected, then the procedure won’t work.
    VisualStudioSetup03
  4. Choose a setting other than Web Development (I highly recommend the General Development Settings as being the most versatile) and click Finish. Visual Studio will reconfigure itself for whatever settings you chose. This change will make it possible to save the project solution file for the examples in Professional IronPython.

 

If anyone encounters any other problems with this procedure, please be sure to let me know at John@JohnMuellerBooks.com. I’m always happy to hear from readers about issues regarding my books and want to make things as easy as possible for you.

Exploring the TypingBuddy Application (Part 6)

The previous post, Exploring the TypingBuddy Application (Part 5), describes the technique for displaying messages of various types. A user can choose to use the default message or provide any of a number of randomly selected custom messages. This post assumes that the user has chosen the custom message route. Of course, that means having a database of messages to choose from. The act of adding and editing messages requires a special form, one that contains all of the fields required to perform these two tasks. The TypingBuddy application relies on frmAddEdit to perform this task. You saw this form described in the Exploring the TypingBuddy Application (Part 3) post. As described by its name, this is a dual use form that allows for either adding or editing message records to the XML file that contains the user’s data for this application.

A developer has a number of techniques available to create forms used for multiple purposes. In this case, the caller exercises control over public members of frmAddEdit to modify its behavior. This approach is actually the easiest way to do things. In order to make configuration easier, frmAddEdit defines three special properties:

// Create a resources property that contains the message database.
public TBMessage[] MessageList { get; set; }
 
// Make it possible to edit a particular record.
public Int32 RecordNumber { get; set; }
 
// Check whether this is an add or an edit.
public Boolean IsEdit { get; set; }

These properties contain the message database, the selected record within that database, and a simple Boolean value that determines whether the form is in adding or editing mode. Because this form is created with flexibility in mind, you could probably add other properties to better control precisely how the form appears. However, you don’t require the level of flexibility described for frmMessage, so the use of multiple constructors (as in that case) is unnecessary.

The task of configuring the form falls to the frmAddEdit_Load() event handler. There are actually two levels of configuration required—the appearance of the user interface and the data shown within the form. The appearance is handled by the caller at the time the form is created, before it gets displayed. The data is configured as shown here:

private void frmAddEdit_Load(object sender, EventArgs e)
{
   // Fill the related sound list with the correct values.
   cbSound.Items.AddRange(
      Enum.GetNames(typeof(TBMessages.SystemSoundList)));
   cbSound.SelectedIndex = 0;
 
   // When this is an edit, update the form with the correct
   // data.
   if (IsEdit)
   {
      txtTitle.Text = MessageList[RecordNumber].Title;
      chkShowTitle.Checked = MessageList[RecordNumber].ShowTitle;
      txtMessageText.Text = MessageList[RecordNumber].MessageText;
      cbSound.SelectedItem = MessageList[RecordNumber].Sound.ToString();
      chkDateSpecific.Checked = MessageList[RecordNumber].UseDates;
 
      // Don't provide date values unless the message uses dates.
      if (MessageList[RecordNumber].UseDates)
      {
         dtStartDate.Value = MessageList[RecordNumber].StartDate;
         dtEndDate.Value = MessageList[RecordNumber].EndDate;
      }
      else
      {
         dtStartDate.Enabled = false;
         dtEndDate.Enabled = false;
      }
   }
}

When the form is in adding mode, the frmAddEdit_Load() event handler only has to add the list of available sounds to cbSound, and then select one of the items on the list. Because the form is already configured for adding mode, you don’t need to do anything to the user interface.

In editing mode, the frmAddEdit_Load() event handler must perform some additional tasks. It begins by configuring all of the common message data on the form. Then, if the UseDates property is checked, the event handler must also configure the starting and ending date values.

Handling the use of dates is an important consideration. You don’t want to set any dates unless the user says to use dates. Consequently, the application also requires the chkDateSpecific_CheckedChanged() event handler shown here.

private void chkDateSpecific_CheckedChanged(object sender, EventArgs e)
{
   // Determine the checked state.
   if (chkDateSpecific.Checked)
   {
      // Enable the dates when using date information.
      dtStartDate.Enabled = true;
      dtEndDate.Enabled = true;
   }
   else
   {
      // Otherwise, disable the dates.
      dtStartDate.Enabled = false;
      dtEndDate.Enabled = false;
   }
}

This event handler enables or disables the date fields as needed. Notice that the date fields are always enabled and disabled as a pair. The user must set both a starting and ending date in order for the date system to work properly.

The most complex piece of code for this part of the example appears in the btnAdd_Click() event handler. This code must incorporate a number of safety check to ensure the user doesn’t enter an invalid record into the database. In addition, the code must ensure that dates are only recorded when the user wants to employ dates as a filtering mechanism as shown here.

private void btnAdd_Click(object sender, EventArgs e)
{
   // Verify that the fields are correct.
   if ((txtTitle.TextLength == 0) || (txtMessageText.TextLength == 0))
   {
      // Display a message and exit when the user
      // hasn't provided the right information.
      MessageBox.Show(
         "You must provide values for both the Message Title " +
         "and Message Text fields!",
         "Entry Error",
         MessageBoxButtons.OK,
         MessageBoxIcon.Error);
      return;
   }
 
   // Create a temporary array.
   TBMessage[] Temp;
 
   // Determine the sort of change taking place.
   if (IsEdit)
   {
      // Initialize the temporary array.
      Temp = MessageList;
 
      // Add information to the new element.
      Temp[RecordNumber].Title = txtTitle.Text;
      Temp[RecordNumber].ShowTitle = chkShowTitle.Checked;
      Temp[RecordNumber].MessageText = txtMessageText.Text;
      Temp[RecordNumber].Sound =
         (TBMessages.SystemSoundList)Enum.Parse(
            typeof(TBMessages.SystemSoundList),
            cbSound.SelectedItem.ToString());
      Temp[RecordNumber].UseDates = chkDateSpecific.Checked;
 
      // Add dates only when required.
      if (chkDateSpecific.Checked)
      {
         Temp[RecordNumber].StartDate = dtStartDate.Value;
         Temp[RecordNumber].EndDate = dtEndDate.Value;
      }
 
      // Update the permanent message list.
      MessageList = Temp;
   }
   else
   {
      // Initialize the temporary array.
      if (MessageList != null)
      {
         // Create a temporary array that's one longer than the
         // existing array.
         Temp = new TBMessage[MessageList.Length + 1];
 
         // Copy the messages from the existing array to the
         // temporary array.
         for (Int32 Record = 0; Record < MessageList.Length; Record++)
            Temp[Record] = MessageList[Record];
 
         // Add a new record to the end of the temporary array.
         Temp[Temp.Length - 1] = new TBMessage();
      }
      else
      {
         Temp = new TBMessage[1];
         Temp[Temp.Length - 1] = new TBMessage();
      }
 
      // Add information to the new element.
      Temp[Temp.Length - 1].Title = txtTitle.Text;
      Temp[Temp.Length - 1].ShowTitle = chkShowTitle.Checked;
      Temp[Temp.Length - 1].MessageText = txtMessageText.Text;
      Temp[Temp.Length - 1].Sound =
         (TBMessages.SystemSoundList)Enum.Parse(
            typeof(TBMessages.SystemSoundList),
            cbSound.SelectedItem.ToString());
      Temp[Temp.Length - 1].UseDates = chkDateSpecific.Checked;
 
      // Add dates only when required.
      if (chkDateSpecific.Checked)
      {
         Temp[Temp.Length - 1].StartDate = dtStartDate.Value;
         Temp[Temp.Length - 1].EndDate = dtEndDate.Value;
      }
 
      // Update the permanent message list.
      MessageList = Temp;
   }
}

As a minimum, a message must include a title and some message text. If the user doesn’t include any other information, the application will either rely on defaults or not provide that information as part of the output. Consequently, txtTitle.Text and txtMessageText.Text are the only two required entries. If the user doesn’t provide these two entries, the application displays an error message and exits.

When the required input is correct, the code moves on to creating a temporary array to hold the updated data. This is a safety feature to ensure that the entire transaction takes place before being made permanent. If the application should suddenly fail between steps, the initial array will still hold valid information.

In editing mode, the btnAdd_Click() event handler begins by copying all of the records from MessageList (the permanent array) to Temp (the temporary array). It then changes each of the required values in Temp. Notice how the code handles dates. Again, it only saves dates when the user wants to use dates as a filtering mechanism. The final step is to make the change permanent by copying Temp to MessageList.

In adding mode, the application can’t assume there are any existing records. As a result, the first check is to determine whether this is a new list of messages or an addition to an existing list. When it’s an addition, the code simply adds a new entry to Temp and copies the required records from MessageList to Temp. However, when this is a new list, the code simply creates a single entry array in Temp. At this point, the process is much like editing an entry. The new values are added to the empty array element and then Temp is copied to MessageList.

Next week we’ll move one level up in the form hierarchy and examine frmMessages. In the meantime, let me know if you have any questions about this part of the example at John@JohnMuellerBooks.com. You can see the next part of this series at Exploring the TypingBuddy Application (Part 7).

 

Starting a Chicken Coop

Sometimes life throws an opportunity your way and taking it is the only logical choice. Normally, we could never afford to buy all of the parts for a chicken coop at a price that would actually provide a payoff. Normally I try to get a five year payoff plan for anything we add to our property and a chicken coop simply requires too many parts to provide such a payoff when used in a self-sufficient manner.

A friend of ours is selling her house, which actually sits on a small farm. In her pole shed is a chicken coop that she’s willing to sell for a reasonable price—a price that should provide us with that five year payoff plan we need. It’s not meant for outside use, but it does have many of the items that an outside chicken coop would require. Of course, that begins with a double door with the screen on the inside, rather than the outside, so that the chickens can get some fresh air during the summer months.

ChickenCoop0101

Inside the existing chicken coop, there is a roost and nest boxes. The roost provides a place for the chickens to sleep above floor level. The nest boxes provide a place to lay eggs.

ChickenCoop0102

At one end of the existing chicken coop is a chicken-sized door. It leads outside to a ramp where the chicken can go into a fenced area for exercise and fresh air. We plan to make the fenced area large enough to ensure the chickens can get plenty of safe exercise (we’ve seen a few chickens get hit by passing cars when they’re not fenced in, not to mention foxes, weasels, racoons, hawks, and other predators).

ChickenCoop0103

The existing chicken coop even includes a window so the birds get sunshine during the winter months. Given that we’re on a south facing slope, the window will allow not only sunshine, but a certain amount of heat during the winter months. Of course, we’ll need to include a heat lamp for really cold winter nights.

ChickenCoop0104

Of course, the first task is to take the chicken coop down and move it from its current location to our house. We’ll have to use the components we obtain to rebuild the chicken coop in a form that will work better outside (including the addition of a roof). I’ll keep you updated on our progress. In the meantime, let me know if you have any questions about our project at John@JohnMuellerBooks.com.

 

Welcome to My Italian Readers!

There are many reasons that I stay in this insane business of technical book writing. For one thing, it really makes my day when I hear from you about the ways in which you’re using the books I write to improve your lives. However, I must admit that I’m always thrilled to see one of my books in another language. It gives me a sense of not only helping my fellow English speakers, but a worldwide audience. I truly want to help as many people as I can. So, it was with great celebration that I received the Italian edition of Start Here! Learn Microsoft Visual C# 2010 Programming this past week. Rebecca and I made it a point to tell our friends and some family members about the new addition. I want to warmly welcome any Italian-speaking readers who might be reading this post—I plan to do my best to serve your needs too!

So, just how is it that I handle e-mail from other countries? An amazing number of people in other countries speak enough English that we can usually communicate at some level. I wish I were fluent in other languages, but I’m not. So, anyone who speaks two languages, even if they don’t speak the second language particularly well, has done something amazing as far as I’m concerned.

Unfortunately, my readers don’t always speak any English, which makes it really hard for us to communicate with each other. In these situations, I’ll try to find a reader who speaks both English and the other language to act as a translator for me. By the way, if you speak two languages and wouldn’t mind me contacting you from time-to-time for help answering e-mail in other languages, please write me at John@JohnMuellerBooks.com.

Having a translator to help me really does make a difference. If I don’t have a reader who has volunteered to help with translations, then sometimes I contact the publisher. It doesn’t pan out for me very often, but I really want to provide my readers with the best service I can, so I do at least try to find a translator somewhere. Because people value timely responses, finding a translator just doesn’t work sometimes.

At this point, I’m down to using some sort of translator application. The one I use most commonly is Google Translate. The translation I receive is more like pidgin English than something truly usable, but again, I do my best to figure out what the reader wants. I then create a response and use Google Translate to turn it into the reader’s language. The first paragraph of my response is always an apology for the poor quality of the response and explanation of how I created it. Even so, most readers seem to take the terrible translation in stride and some even with a bit of humor—something I always appreciate .

So, if you’re reading my book in Italian, thank you for taking the time and expense to buy it. Please do contact me with any questions you might have. Be assured that I’ll do my best to answer any question you might have and bear with my poor translations. Let me know if something comes out too absurd and I’ll send a report to Google about it.

 

Trimming the Trees (Part 3)

In Trimming the Trees (Part 2) I discussed some of the specifics of pruning trees. At this point, my trees are all pruned. However, there is still work to be done. For one thing, this is the time of year when I examine the trees for egg masses of the Eastern Tent Caterpillar. Despite what you may have heard, repeated infestations will definitely kill a fruit tree, especially if the infestation is severe enough. It only makes sense. If you remove all of the leaves from a tree that only gets one set of leaves per season, the tree can’t store energy for the winter months.

Our experience has been that they’re a nuisance with apple trees. Yes, the tent caterpillars will cause a problem, but if you get in there and squish all of the caterpillars in the tent (or better yet, get rid of those egg masses in the spring), the apple hardly notices. However, plum trees seem to attract tent caterpillars like magnets. All four of our plum trees were in danger from dying at one point because we simply couldn’t keep the caterpillars under control.

We did try a number of sprays—all of which proved ineffective. Spaying the trees with a dormant oil spray in the spring helps only a little. By far the best strategy is to hunt down the egg clusters and destroy them. The secondary strategy is to look for the tents absolutely every day in the spring and summer after the trees have leafed out and destroy them by individually squishing the caterpillars by hand. We actually had two of our plum trees stripped of leaves in a single day by these pests.

While we’re looking for tent caterpillar egg clusters, we also look for other problems in the trees, such as disease, insect infestations, and so on. It’s easier to find problems after you’ve pruned the trees and there are fewer branches to check. Taking time now to check the trees will save you a lot of effort later.

Of course, now we have a pile of branches to deal with. This year we pruned our pear trees heavily because they’ve become a little overgrown. If pear trees get too overgrown, they’ll tend to prune themselves in heavy winds—usually not in a way you would have chosen. The pile of branches from all of our trees is quite high this year.

TreeTrimming0301

We’ll put all of these branches through the chipper and then use them for mulch. A lot of people would probably burn the branches up, but using them for mulch does save at least some money. I’ve been trying to figure out the environmental balance in this case. On the one hand, burning the branches would produce a lot of particulate smoke that would pollute the air for at least a while. However, using the chipper also produces pollutants, and some of those pollutants are harsher on the environment than the smoke from burning would cause.

If we had burned the branches, I would have placed the pile in the middle of the garden. That way we could have plowed the ashes into the ground where they would have provided fertilizer on top of the winter rye you can see growing in the background of the picture. So, either way, the branches wouldn’t have gone to waste. However, we really need the mulch more than the ashes, so we’re creating the mulch.

Our orchard is ready for spring at this point. Let me know about your tree pruning and bug eradication experiences at John@JohnMuellerBooks.com.

 

Professional IronPython Chapter 2 Step-by-Step Procedure Update

Every once in a while, a small error gets past me that really is almost microscopic, yet ends up causing readers woe. Errors of this sort (or any sort at all) are the reason I wrote the Errors in Writing post so long ago. In this case, there is a small mistake in Professional IronPython that might not seem like such a big deal at first, but it has caused problems for readers, so I’m providing an update here.

In Step 7 on page 26 I ask you to choose File→Save All, after which, you’re supposed to see the dialog box shown in Figure 2-4. It turns out that the step doesn’t quite work and for an important reason. Back in step 4 I asked you to right click the IPY entry in Solution Explorer. Of course, that step highlights IPY, selecting it. As a consequence, step 7 can’t work as written because you can’t save IPY.

To make Step 7 work as printed in the book, first highlight the Solution ‘ipy’ (1 project) entry in Solution Explorer (see Figure 2-2). Selecting this entry means that you’re asking Visual Studio to save the solution, not IPY. Now when you choose File→Save All, you’ll see the Save File As dialog box shown in Figure 2-4 as expected.

Please let me know if you encounter any other errors of this sort and I’ll be more than happy to discuss them in my blog. For this, and any other book-related concerns, contact me at John@JohnMuellerBooks.com.