Notifying the Developer
So far, in notifying the developer, I've simply written a message to the status bar. However, that's only really appropriate for messages that provide information about ongoing processing. Where the add-in is unable to continue processing, it's more appropriate to write the message to the TaskList, where it will appear in the Add-In and Macros category.
Defining the Output Utility
To handle output, I use a single method that, when passed a message and a severity level, either updates the status bar or adds an item to the task list, depending on the severity level. Updating the status bar not only lets the developer using your utility know what's going on, it's also helpful in debugging—if your add-in abends, the status bar will display the last message sent to it, giving you a clue as to where in your add-in you stopped processing. Because I use this method in a variety of code-generation projects, I put it in its own class library project called CodeGenerationUtilities (this project needs references to both EnvDTE and EnvDTE80).
My utility also includes an enumeration, which I call GenerationLevel. I use it to specify the error level of the message. As a minimum, you need to support two severity levels: one for messages to be written to the status bar and one for messages to be written to the Task List. The two levels that I use are called "information" and "severe":
namespace CodeGenerationUtilities { public enum GenerationLevel { information, severe }
In order to update the status bar and the Task List, my utility needs to access the DTE2 object used by the add-in. I pass that reference to the utility in its constructor.
Handling the Task List
In the utility's constructor, I delete all related messages that may be in the Task List from previous code generations. In order to avoid deleting messages created by other code-generation utilities, I use the TaskList's SubCategory: When adding messages I set the SubCategory to a value unique to the particular code-generation solution. (All my code-generation solutions set the TaskList's Category to "Code Generation.") As a result, I can use the SubCategory to delete messages from previous executions of this code-generation solution. I pass the SubCategory to be used when adding or deleting messages into the utility's constructor and store it in a field. As a result, the constructor for the utility looks like this:
string subcategory; public Utilities(DTE2 ApplicationObject, string SubCategory) { applicationObject = ApplicationObject; subCategory = SubCategory; TaskList tl = applicationObject.ToolWindows.TaskList; foreach (TaskItem ti in tl.TaskItems) { if (ti.SubCategory == SubCategory) { ti.Delete(); } } }
My utility includes a WriteOutput method that accepts the message text to display and a GenerationLevel flag. If GenerationLevel is set to severe, the message is added to the TaskList; if GenerationLevel is set to information, the message is used to update the status bar:
public class Utilities { public void WriteOutput(string Message, GenerationLevel Level) { if (Level == GenerationLevel.severe) { TaskList tl = applicationObject.ToolWindows.TaskList; TaskItems2 tis = (TaskItems2)tl.TaskItems; TaskItem ti = tis.Add2("Code Generation", subCategory, Message, (int)vsTaskPriority.vsTaskPriorityHigh, (int)vsTaskIcon.vsTaskIconCompile, true,"",0,true,true,false); } else if (Level == GenerationLevel.information) { applicationObject.DTE.StatusBar.Text = Message; } }
Using the Output Method
With the CodeGenerationUtilities object created, I can add a reference to the add-in so that it can use the class. To simplify code, the add-in will need a using statement (or an Imports statement in Visual Basic) that points to the new project:
using CodeGenerationUtilities;
My code-generation solution also needs a class-level variable that can hold a reference to the utility:
Utilities util;
In my add-in's constructor, I create a reference to the CodeGenerationUtilities object:
util = new Utilities(applicationObject, "ConnectionStringGenerator");
With that work done, I can use the WriteOutput method to send messages to the developer running the code-generation solution. As an example, the following call adds a message to the Task List:
util.WriteOutput("Unable to create Connection Manager", GenerationLevel.severe);