Categories: VBA Posted by chris on 10/27/2009 3:54 PM | Comments (0)

MS Access Delete Table Function:

 

Private Function DeleteTables() As Boolean
'Function to delete linked tables
'By: Chris Hayes
'Date: 10-21-09
'Notes: Use with care!
'------------------------
Dim blWorked As Boolean
Const TableList As String = "" 'enter the list of tables you want to delete delimited by ';'
blWorked = True
Dim tableNames
tableNames = Split(TableList, ";")
Dim t As String
Dim db As DAO.Database
Set db = CurrentDb
Dim td As DAO.TableDef
Dim i As Integer

For Each td In db.TableDefs
    For i = 0 To UBound(tableNames)
        If td.Name = tableNames(i) Then
            DoCmd.RunSQL "DROP TABLE [" & td.Name & "]"
        End If
    Next
Next
DeleteTables = blWorked
End Function

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Tags: , | Categories: General Posted by chris on 10/22/2009 10:14 PM | Comments (0)

I don't know how many people will relate to this.  While working on a project with someone today I found that my mind works on projects like a mouse in a maze.  My co-worker kept asking me what I was thinking so she could follow along with my approach to a problem.  I found that I had a hard time explaining what I was doing because I felt unsure that the direction I was going was correct.  I was unable to tell her where I was going because deep down I knew that until I got to the solution, where I was going could be wrong.  What I never noticed about my method was that I don't necessarily construct solutions as a whole and implement them.  What I do is start working a problem from a certain avenue until I come to a roadblock.  If I find I can't overcome the roadblock I backtrack to another avenue and see if that approach removes the roadblock.

I'd be very interested in other people's opinion on this thought process. 

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Posted by chris on 10/7/2009 12:22 AM | Comments (0)

I recently watched Kirill Osenkov's screencast introduction to Visual Studio 2010 and C# 4.0.  The coolest thing I walked away with from that video was C#'s new implementation of 'named optional arguments.'  I've spent a lot of time programming VBA (Visual Basic for Applications) and one of the things I really like about VBA methods is being able to set optional argument values by name.  This dramatically increases readability.  Let's take the extremely familiar 'Message Box' example:

MsgBox("The process has finished", vbInformation +vbOK, "Message")

 

In VBA, the Message Box has 4 optional arguments and one non optional.  The above line can be deciphered with close examination but let's look at it with 'named' arguments.

MsgBox(prompt: "The process has finished", _
       title: "Message", _
       buttons: vbInformation + vbOK)

This has been around ever since I've know VBA.  The arguments can be in any order when named unlike present C# parameters.  Also, the parameters can be omitted because they are 'optional.'  Which means, there is a default value assigned to them in the method.

MsgBox("The process has finished")

 

The 'title' and 'buttons' arguments can be omitted because they are optional and assigned default values.  In C# you would have to have an 'overload' for every optional argument structure.  

Take for instance the following examples

    class mockMessageBox
    {
        private string prompt;
        private string title;
        private MessageBoxButtons buttons;

        //here's one variant with all the paramenters requested
        public mockMessageBox(string prompt, string title, MessageBoxButtons buttons)
        {
            this.prompt = prompt;
            this.title = title;
            this.buttons = buttons;
        }
        //here's one variant with only the 'prompt' paramenter
        public mockMessageBox(string prompt)
        {
            this.prompt = prompt;
            this.title = "My Application Title";
            this.buttons = MessageBoxButtons.OK;
        }
        //and yet another...
        public mockMessageBox(string prompt, string title)
        {
            this.prompt = prompt;
            this.title = title;
            this.buttons = MessageBoxButtons.OK;
        }
        //... and on and on

As you can see, the variations of overloads could go on and on depending on how many arguments you need. So the benefit of the new technique is twofold. 'Readability' and 'Flexibility.'

The implementation is basically the same:

    class mockMessageBox
    {
        private string prompt;
        private string title;
        private MessageBoxButtons buttons;

        //here's C# 4.0's version
        public mockMessageBox(string prompt, string title = "My Message Title", MessageBoxButtons buttons = MessageBoxButtons.OK)
        {
            this.prompt = prompt;
            this.title = title;
            this.buttons = buttons;
        }

    }

And here's the implementation of that method:

    class testMessageBox
    {
        mockMessageBox MyMsgBox as new mockMessageBox(prompt: "Here's my prompt", 
                                                      title: "Here's My Title", 
                                                      buttons: MessageBoxButtons.YesNo);
        //or maybe just a prompt with no 'named' parameters
        mockMessageBox MyMsgBox2 as new mockMessageBox("Here's a prompt only, all else will be default values.");
    }

That is pretty cool! AND I do not see a need for this replacing overloads but as of now I don't see how 'named' arguments could be used interchangeably with overloads. ???

This is just one feature. There are plenty more, like IDE upgrades such as the 'Class Hierarchy' window, a great search and find tool for code. Other cool things too, too much to write in this one blog! Please check out the video, Kirill did a great job.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Posted by chris on 8/16/2009 2:11 PM | Comments (0)

Believe In Your Product

When you believe in it you can sell it.

Any one selling anything knows that you must believe in your product.  If you sell Fords but believe in Chevrolets it's unlikely you will go far.  If you sell cookware then it's a good idea to buy a set for yourself.  Not all salesmen can buy what they sell but they must fully believe in the item they sell.  It would be hard for a Boeing airplane salesman to buy a 747 Boeing passenger jet.  If he needs to travel he should select an airline that flies Boeings and not one that flies Airbuses.

This 'belief in product' can be carried over into job hunting.  You can start by taking interest in your product, yourself.  You are a special individual.  There is no one else like you.  There was this young man who joined the army for a special job.  I don't remember what the job was but it was very technical and required strong math skills.  While in school he found he was not able to make the cut for that job.  The army placed him in another position that did not require the same level of math skills.  This young man was very depressed in his failure to pass the school.  He explained to me how down he was on himself for not making the cut.  I explained to him that, despite the school, he was now in a job that he could excel to the top in.  He was in a place where he could do better than the average individual in his unit.  He had the acumen to learn every aspect of his current job in detail and do better than the others.  All of us can't be doctors but we could be nurses or even nurse assistants.  You can still stay in the field you are interested in and keep shooting.  What's the difference between a big shot and a little shot?  The big shot is just a little shot who kept shooting.  I am in not telling you to give up your goals but you shouldn't beat yourself up for failures along the way.  It's better to fail then to never try.

You have something special to offer.  You have interests that others don't.  I don't like working on cars but some people do.  If you are someone who likes working on cars then you are one up on me for a mechanic job.  You are someone who likes to do something that someone else doesn't.  You are someone who can do something that someone else can't and that makes you special.  You are capable of excelling in areas where others can't.  It's all determined by desire. 

In my last job I always kidded people about my work.  I explained to them that I didn't do anything.  I joked about how easy my job was.  This was a one year contract job and I loved it.  In a going away lunch I joked with some of my colleges about my great success in doing nothing for a whole year.  The girl that I was training was not fooled.  She told everyone at the lunch that I was fully dedicated to my work and that there was nothing about my work that was easy.  She explained that all my 'favorites' links were to websites solely devoted to my work.  What she found was that I really liked what I did.  What was work for others was fun for me.  When you are looking for a job that you enjoy doing it's not hard to convince someone that you are the right person.

You may not like the work you have been doing.  Let me suggest something, "Stop selling Fords if you like Chevrolets."  Start doing something you like even if it's at the beginning.  If you can't think of something you like then do something.  Just do something.  You may fall in love with it.  Try learning about the line of work you are in.  What is the difference between the grill cook and the manager at a fast food restaurant?  The manager should be able to tell you what temperature the grill should be set at, how long it takes to prepare any item on the menu and other details about the business.  They should be someone who can tell you the details of every aspect of the company.  If you work at McDonalds try reading Ray Croc's book "Grinding it Out."  If you work at Wendy's then read "Dave's Way."  If you work at Wall-Mart then read Sam Walton's book.  If you are a security guard then read books on corporate security.  Learn something about the work and place you work at.  You may become interested in it.  You will become more effective and valuable and sellable.

Diamonds are found as rough rocks.  They must be chiseled and shaped into the valuable bright and shiny gems that people desire.  Inside of you is a diamond but it must be chiseled and shaped into a valuable gem that people desire.  Start shaping your job skills and people will desire those skills.  There is a wealth of information that you can find to do this.  Libraries carry information on any topic you desire.  The world wide web has information on any topic you desire.  Dig up those diamonds of information and fit them to your life and you will become more marketable.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Posted by chris on 8/16/2009 2:07 PM | Comments (0)

Looking For A Sales Job?

How looking for a job is a sales job in itself.

Right before I left the army in 1996 I attended a job search seminar.  The trainer started out by saying, "Are you looking for a sales job?  Well, that's what you'll be doing because looking for a job is a sales job."  That statement stuck with me and it has helped me many times in my job search endeavors.

When you think of a sales job you probably think of the guy who sells used cars down the street or the person who knocks on your door selling pest control services (someone actually came by my place tonight selling pest control.)  You may think, "I really don't want to go door to door selling 'pest control.'  That's all well and good because I really don't want to either, but if you are going to get a job you are going to have to start thinking like the 'pest control' guy.  You're going to have to get out your briefcase and fedora and start acting like the person who's trying to convince you to buy 'pest control' services or a $1,500.00 vacuum cleaner.  Actually, modern door to door salesmen don't wear fedoras or carry briefcases.  Some of them are salesladies and they'd look pretty silly wearing a fedora.

Your goal is to do the same thing the salesman is trying to do, convince the other party that they need your service/product. What are you selling?  You are selling your services.  You are trying to convince the prospective employer to 'purchase' your work services.  You want to make a sale to the customer for work services rendered.  You goal is to convince the 'prospect' i.e. the employer, that they should 'buy your work service.'  We'll get into the mechanics of 'making the sale' in this book but for now start convincing yourself that you are a 'temporary' salesman and that you 'will' make the sale.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Posted by chris on 6/18/2009 2:20 PM | Comments (0)

I recently had the opportunity to create a windows tray application.  The goal application was to copy files daily from one place to many.  As many may understand we don't always have access or permission to a server to run jobs like this.  Office automation is a big need even when you don't have permission to the big toys.  With a little investigation I was able to build this little app in a few days.  I'd like to give credit to the one who showed a simple implementation of a tray application, Mitchel Sellers.

A few basics about the application.  First, it's a Windows Form application.  The big difference is that the application does not initiate with the origin form.  The origin form is deleted.  The next step is to replace the 'program.cs' start up code to reflect that there is no originating form.  Mitchel creates a 'ApplicationContext' class for the initialized object.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
 
namespace TestTrayApp
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Daily_Customer_File_Copy_App_ApplicationContext oContext = 
                new Daily_Customer_File_Copy_App_ApplicationContext();
            Application.Run(oContext);
        }
    }
}

As you can see, there is no form1 that loads when the application starts. What happens is our class is initiated. Here's the code to the class:

namespace TestTrayApp
{
    class Daily_Customer_File_Copy_App_ApplicationContext : ApplicationContext
    {
        #region Private Members
        private System.ComponentModel.IContainer mComponents;
        private NotifyIcon mNotifyIcon;
        private ContextMenuStrip mContextMenu;
        private ToolStripMenuItem mDisplaySettingsForm;
        private ToolStripMenuItem mDisplayLogForm;
        private ToolStripMenuItem mRunJob;
        private ToolStripMenuItem mExitApplication;
        private Settings mSettingsForm;
        private Log mLogForm;
        private Timer mTaskTimer;
        private bool mJobFailed;
        #endregion

        public Daily_Customer_File_Copy_App_ApplicationContext()
        {
            //Instantiate the component Module to hold everything
            mComponents = new System.ComponentModel.Container();


            //Instantiate the NotifyIcon attaching it to the components container and 
            //provide it an icon, note, you can imbed this resource 
            mNotifyIcon = new NotifyIcon(this.mComponents);
            mNotifyIcon.Icon = new System.Drawing.Icon(Assembly.GetExecutingAssembly().GetManifestResourceStream("TestTrayApp.clock.ico"));
            mNotifyIcon.Text = "MS Voice Daily Customer File Copy App";
            mNotifyIcon.Visible = true;

            //Instantiate the context menu and items
            mContextMenu = new ContextMenuStrip();
            mDisplaySettingsForm = new ToolStripMenuItem();
            mDisplayLogForm = new ToolStripMenuItem();
            mRunJob = new ToolStripMenuItem();
            mExitApplication = new ToolStripMenuItem();

            //Attach the menu to the notify icon
            mNotifyIcon.ContextMenuStrip = mContextMenu;

            //Setup the items and add them to the menu strip, adding handlers to be created later
            mDisplaySettingsForm.Text = "Settings";
            mDisplaySettingsForm.Click += new EventHandler(mDisplaySettingsForm_Click);
            mContextMenu.Items.Add(mDisplaySettingsForm);

            mDisplayLogForm.Text = "Show Log";
            mDisplayLogForm.Click += new EventHandler(mDisplayLogForm_Click);
            mContextMenu.Items.Add(mDisplayLogForm);

            mRunJob.Text = "Run Job";
            mRunJob.Click += new EventHandler(mRunJob_Click);
            mContextMenu.Items.Add(mRunJob);

            mExitApplication.Text = "Exit";
            mExitApplication.Click += new EventHandler(mExitApplication_Click);
            mContextMenu.Items.Add(mExitApplication);

            //Create a timer to run the copy job
            mTaskTimer = new Timer();
            mTaskTimer.Enabled = true;
            mTaskTimer.Interval = 1000;
            mTaskTimer.Tick += new EventHandler(mTaskTimer_Tick);

            //initiate jobfailed indicator
            mJobFailed = false;
        }
    //.... Extra code that runs events
    }
}

The Private Memebers:

        #region Private Members
        private System.ComponentModel.IContainer mComponents;
        private NotifyIcon mNotifyIcon;
        private ContextMenuStrip mContextMenu;
        private ToolStripMenuItem mDisplaySettingsForm;
        private ToolStripMenuItem mDisplayLogForm;
        private ToolStripMenuItem mRunJob;
        private ToolStripMenuItem mExitApplication;
        private Settings mSettingsForm;
        private Log mLogForm;
        private Timer mTaskTimer;
        private bool mJobFailed;
        #endregion

The 'Component Container' and 'Notify Icon' are basically our application. We create a container, add the notify icon and show it:

            //Instantiate the NotifyIcon attaching it to the components container and 
            //provide it an icon, note, you can imbed this resource 
            mNotifyIcon = new NotifyIcon(this.mComponents);
            mNotifyIcon.Icon = new System.Drawing.Icon(Assembly.GetExecutingAssembly().GetManifestResourceStream("TestTrayApp.clock.ico"));
            mNotifyIcon.Text = "MS Voice Daily Customer File Copy App";
            mNotifyIcon.Visible = true;

In line 2 you see that the icon is added. One thing that helped me get this live in the executable was to ensure that the code could find the embedded icon. Ensure that the icon's properties show it as Embedded Resource and that it's copied.

Step two in this 'finding icon' mission is to use the 'System.Reflection' method for finding this resource in the dll.

            mNotifyIcon.Icon = new System.Drawing.Icon(Assembly.GetExecutingAssembly().GetManifestResourceStream("TestTrayApp.clock.ico"));

If you don't do this, the application fails because it can't find the icon.

Let's look at the context menu that gives the application life:

            //Instantiate the context menu and items
            mContextMenu = new ContextMenuStrip();
            mDisplaySettingsForm = new ToolStripMenuItem();
            mDisplayLogForm = new ToolStripMenuItem();
            mRunJob = new ToolStripMenuItem();
            mExitApplication = new ToolStripMenuItem();

            //Attach the menu to the notify icon
            mNotifyIcon.ContextMenuStrip = mContextMenu;

            //Setup the items and add them to the menu strip, adding handlers to be created later
            mDisplaySettingsForm.Text = "Settings";
            mDisplaySettingsForm.Click += new EventHandler(mDisplaySettingsForm_Click);
            mContextMenu.Items.Add(mDisplaySettingsForm);

            mDisplayLogForm.Text = "Show Log";
            mDisplayLogForm.Click += new EventHandler(mDisplayLogForm_Click);
            mContextMenu.Items.Add(mDisplayLogForm);

            mRunJob.Text = "Run Job";
            mRunJob.Click += new EventHandler(mRunJob_Click);
            mContextMenu.Items.Add(mRunJob);

            mExitApplication.Text = "Exit";
            mExitApplication.Click += new EventHandler(mExitApplication_Click);
            mContextMenu.Items.Add(mExitApplication);

We instantiate our private members for the context menu:

  1. mContextMenu: Our Context Menu
  2. mDisplaySettingsForm: A menu item to open the 'Settings' form
  3. mDisplayLogForm: A menu item to open the 'Log' form
  4. mRunJob: This action allows the user to run the job outside of the timer
  5. mExitApplication: An action to exit the application

Each one of these 'menu items' has it's own event handler. You can see that each item has three lines:

  1. '.Text' which is the menu text. Pretty straight forward.
  2. '.Click' we add an event to the 'Click' property.
  3. 'mContextMenu.Items.Add()' we stick the 'menu item' to the 'context menu'

We need a timer to trip the job at the specified time. Here's our first look the Settings.settings properties. You have to love these because they are simple variables for holding application defaults. When analyzing how I wanted to approach application settings I concidered an XML file that was embedded but I found that altering such a file created issues for the dll. The Settings.settings variables work awesome for this application. I just added some variables giving the user flexiblity:

These 'Variables' can be accessed from 'Properties.Settings.Default' Let's look at the timer implementation here:

            //Create a timer to run the copy job
            mTaskTimer = new Timer();
            mTaskTimer.Enabled = true;
            mTaskTimer.Interval = 1000;
            mTaskTimer.Tick += new EventHandler(mTaskTimer_Tick);

We instantiate our 'mTaskTimer' add a few properties and then add an event handler. It's that simple. I'd like to take a look at the timer's event handler right now:

        void mTaskTimer_Tick(object sender, EventArgs e)
        {
            if (sender == mTaskTimer)
            {
                int hour = DateTime.Now.Hour;
                int minute = DateTime.Now.Minute;
                int second = DateTime.Now.Second;
                if (hour == Properties.Settings.Default.jobHour
                    && minute == Properties.Settings.Default.jobMinute
                    && second == 0)
                {
                    mRunUpload();
                }
            }
        }

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Tags: , | Categories: VBA Posted by chris on 5/29/2009 9:52 AM | Comments (0)

I don't know how many time's I've wrote this function. I'm keeping it here for easy access. Feel free to use it, it's based on the MSDN VBA library example.

Function GetFilePath(Optional DlgType As MsoFileDialogType = msoFileDialogFilePicker, _
                     Optional DlgMultiSelect As Boolean = True, _
                     Optional DlgTitle As String = "Please Select a File", _
                     Optional DlgFilters As String = "", _
                     Optional DlgInitialFile As String = "")
Dim dlg As Office.FileDialog
Dim ThisFilters
Dim i As Integer
Dim itm
Dim FileItms As String
Set dlg = Application.FileDialog(DlgType)

With dlg
    .AllowMultiSelect = DlgMultiSelect 'set the mulitselect
    
    .Title = DlgTitle 'add the title
    
    If DlgFilters <> "" Then 'if there are filters then add them
        ThisFilters = Split(DlgFilters, ";") 'filters is a string delimited by semicolon ';'
                                             'split it into an array
        .Filters.Clear 'clear the current filter
        For i = 0 To UBound(ThisFilters) Step 2 'loop through array of filters and add them
            .Filters.Add ThisFilters(i), ThisFilters(i + 1) 'filters should be entered as:
                                                            '"Filter Name;Filter Type;Filter Name;Filter Type;..."
                                                            'i.e. "Excel File;*.xls;XML Files;*.xml"
        Next
    End If
    
    If DlgInitialFile <> "" Then .InitialFileName = DlgInitialFile 'if there's an initial path get it
    
    If .Show = True Then 'run the dialog
        For Each itm In .SelectedItems 'loop through selections
            FileItms = FileItms & itm & ";" 'and build string of paths delimited by semicolon ';'
        Next
        If DlgMultiSelect Then
        GetFilePath = Split(Left(FileItms, Len(FileItms) - 1), ";") 'return array of file paths
        Else
        GetFilePath = Left(FileItms, Len(FileItms) - 1) 'return the single file path as string
        End If
    Else
    GetFilePath = ""
    End If
    
End With
End Function

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Tags: , , | Posted by chris on 5/26/2009 10:25 AM | Comments (0)

Everyone has pet peeves.  I know a guy who gets upset over people who leave toilet seats down.  It really disturbs him.  But I understand his frustration because I too have pet peeves that drive me nuts.  One in particular is people CC’ing me when they intended to speak to me directly.

CC the history

CC stands for 'Carbon Copy.'  Years ago, before we had copy machines and laser printers people used a product called carbon paper.  Carbon paper was, or still is, made of a thin piece of paper covered with wax and pigment.  It's original date of creation is uncertain but we see its use in the early 1800's.  This pigmented paper was used to create a 'copy' of the original document being typed.  The Carbon paper would be placed between the original paper document and the paper intended for copy.  The impression made on the original would be transferred by the carbon paper to the copy.  The basic purpose of Carbon paper was to create copies, not originals.  Originals were intended for the direct party but copies were intended for others with an interest in the original information.

CC the Concept

This concept continues today in the CC field of emails.  The use of this field is to send a 'copy' of a message intended for someone else.  When someone sends an email to someone via the CC field they are, in essence, saying "This is a copy of information I sent to someone else that may be of interest to you."  They are in no wise communicating directly to the CC'ed recipient.  CC'ed recipients should be regarded as third parties that are overhearing the conversation.  I'll use a few examples to help illustrate.

Good Example: John is the manager of a small web team.  John sends an email to Brenda the team designer to create some graphics for their site.  John addresses Brenda in the TO box of the email he is about to send.  John likes to keep his secretary, Richard, involved in what is going on in the team so he puts his address in the CC box.  John never mentions his secretary in his message, nor does he direct any comments to him.  By adding Richard he is keeping him abreast of the project but does not intend for him to act on any of this information.  John also realizes that Mark in development will need to create some animation programming on these graphics and he wants him to work with Brenda on coordinating this design.  John places Mark in the TO box and addresses him in the letter with instructions on the graphics.  What is right about this message?  John needed to communicate to Brenda and Mark of his need for the project.  Even though Mark is brought in as a third recipient he is still being asked to do something and he is being spoken to.  He wants to keep Richard involved but he has no action items for him.

Good Example: Angela is the Senior Service Manager in X company.  Angela receives a message from a Gary, one of her service representatives, that his customers are having a problems with one of their web services.  Angela emails Michael in IT of this new situation and asks him to investigate the issue.  Angela adds Michael to the TO box.  Angela also wants Gary to know that she is following up on his request so she adds him to the CC box.  In her message Angela mentions Gary and his customer as the origin on the issue but Angela does not address Gary in the message, nor does she expect him to act in any way regarding the message sent to Michael in IT.  She only speaks about Gary in the third person. What's right about this message?  The message is addressed to Michael but Angela wants to make sure Gary is kept in the loop as to what is going on.  Angela mentions Gary but she does not speak to him in the message because the message is not intended for him nor is there any action required of Gary.  Gary is kept in the 'know' but not in the conversation.

Bad Example:  Bob the IT mananger receives an escalation request from Melinda, a service manager.  Melinda's customers are experiencing a service outage and she needs some help.  Bob forwards the message to his lead IT tech Ralph.  In his message asks Ralph to send him a situation update immediately.  He places Ralph in the TO box.  Bob then adds Ken, who is handles networking, to the CC field and asks him to also provide an update.  Bob addresses Ken in the messages and requires him to send an investigation report as soon as possible.  What's wrong with this message?  Bob may concider Ken a second party to the message but he is still addressing him directly and he is asking for some kind of action.  Anytime someone is required to act on a message they should be added to the TO box.  Ken may have a rule on his email that forwards CC messages to a special folder for him to review during a lower volume time of his day.  Ken may miss the message that required immediate attention because Bob failed to use the email tool as it was intended.

As you can see, the CC box can come in handy for communicating to others and keeping people informed.  The key thing to remember is that you should not CC someone you are directly communicating to.  Use CC to keep people informed but don't use CC to speak to someone directly.  CC'ed recipients should not be mentioned in emails unless as a third party.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Posted by chris on 5/13/2009 3:13 PM | Comments (0)

INTRODUCTION

This post is intended to discuss the WordInWord application and how it's built.  There are a few major building blocks that I had to tackle to get a working copy.  Here's a quick summary of those parts:

  • XML: The Bible I chose is in XML format.  The application requires a Bible in a data format for querying.  I needed a portable version that could be embeded in the application.  Another concideration was the engine needed for querying.  The only other concievable free data model would have been SQL Server Express and that would require the user to install it.  I wanted to avoid this so I went with XML.  I'll be discussing XML and C#, how they work together to accomplish this task.  I'll also talk about XPath for querying the document and how that's implemented.
  • SmartTag: We need a way for Word to identify a scripture and give the user options in reference to it.  A context menu might work but it really doesn't let the user know they have an option.  I really liked the SmartTag approach because it instantly informs the user "hey, you can do something with this text!"  I'll be discussing how the SmartTag is implemented in this application.
  • Regular Expressions: The SmartTag requires either exact text to search for or a regular expression.  Due to the fact there are so many books, chapters and verses and variations of scripture references the obvious choise was to create a regular expression.  I'll discuss what they are and how I arrived at the current expression.

The XML Bible

Try searching for "XML Bible" and guess what you get?  It's not going to be the Holy Bible in XML format, I can tell you that much.  You'll get numerous references to technical books on XML that have the ubiquitous title "Bible."  Early on, I found a copy of the Bible in XML format that I really liked.  It contained all the information needed to get the job done and more.  Altough the version I was looking at was comprehesive there were some major issues I had with it's structure.  One of the major requirements of this tool was the ability to query the Bible.  The XML version I was dealing with had the proper reference information but that information didn't help with querying.  Here's an example:

<p>
<c id = "1" />
<v id = "1" />In the beginning God created the heaven and the earth.
<v id = "2" />
And the earth was without form, and void; and darkness
<add>was </add> upon the face of the deep. And the Spirit of
God moved upon the face of the waters.
</p>
<p>
<v id = "3" />And God said, Let there be light: and there was light.
<v id = "4" />
And God saw the light, that <add>it was</add> good: and God divided
the light from the darkness.
<v id = "5" />
And God called the light Day, and the darkness he called Night.
And the evening and the morning were the first day.
</p>

You'll notice that the paragraph tags enclose groups of verses. Further into the document you'll even notice verses that are split inbetween paragraphs. Verse and chapter tags just float along in the document but don't encapsulate the text.  This was not going to work for querying.  Scripture references contain Book name, Chapter, and verses.  The convention doesn't change.  So I continued on my merry way looking for another version.

Search engines are great, for some things.  The work involved in trying to find a version that matched exactly what I wanted and creating one that worked for me was becoming equal.  With some text alterations I was able to switch the paragraph tags to floating and close all the chapter and verse tags.  Who ever said scrubbing data wasn't a valuable skill? Here's a portion of what I finally ended up with:

<p />
<c id = "1" >
<v id = "1" >In the beginning God created the heaven and the earth. </v>
<v id = "2" >
And the earth was without form, and void; and darkness <add>was </add>
upon the face of the deep. And the Spirit of God moved upon the face
of the waters. <p />
</v>
<v id = "3" >And God said, Let there be light: and there was light. </v>
<v id = "4" >
And God saw the light, that <add>it was</add> good: and God divided the
light from the darkness.
</v>
<v id = "5" >
And God called the light Day, and the darkness he called Night. And
the evening and the morning were the first day.
<p />
</v>

As you can tell the chapter and verse tags are enclosing text which makes it readily available.  This was accomplised with a lot of find and replace steps.

Another part of the XML that wasn't to be found were Book name alias.  Anyone familiar with Bible references know that there's more than one way to annotate a Book name.  Take for example 'I John'.  This could be noted as I John, I Jn, 1 Jn, 1 John, First John, etcetera.  When searching for the reference I needed a place to find what book the user was looking for.  What I did was add a new attribute to the <book> tag called 'alias'.  I just add these aliases like metadata and then search on it.  I did a little hunting on line and someone had a pretty comprehesive list of bible aliases that I cleaned up and dropped into the XML.

XPath Or Linq?

When I started I concidered using Linq to query the XML.  I'm familiar with SQL so I figured I would be able to handle Linq.  I was wrong.  Linq, from what I can tell, is a great tool for programmers but not a novice like me.  I needed something straight forward.  I had initially investigated XPath with C# and found it a little 'archaic' in fashion but after using it in this application I feel it's easy to figure out and implement.

XPath is like a URL you navigate to the node you want by parent child relationship. Let's say your XML is something like this:

<Families>
  <Family>
    <Family_Name>Flintstone</Family_Name>
    <Father nickname="Fred" >Frederick</Father>
    <Mother>Wilma</Mother>
    <Son>BamBam</Son>
  </Family>
  <Family>
    <Family_Name>Rubble</Family_Name>
    <Father nickname="Barney" >Barnabas</Father>
    <Mother>Betty</Mother>
    <Daughter>Pebbles</Daughter>
  </Family>
</Families>

To navigate to the "Father" tag you would write it as such "/Families/Family/Father"  That's pretty simple.  If you are searching for a particular 'Father' you can enter criteria such as "/Familes/Family [father='Fred']/Father"  If you are searching for information in an attribute you do it this way: "/Families/Family/Father [@nickname='Barney']"

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Tags: , , , | Posted by chris on 4/24/2009 1:52 PM | Comments (0)

In my endeavors to learn C# and Visual Studio Tools for Office I decided to build my own Smart Tag add-in.  I wanted to pick something that would interest me so I would be motivated to finish the project.  I've never been a big fan of smart tags in general because I never find a use for them and most of the time they are annoyingly popping up in my way.  I decided to build a Bible Reference Smart Tag Add In.  "What kind of smart tag tool would I want?"  I spent 3 years in Bible Seminary and we had all these scripture references.  I would type my notes and say, "I'd like to have these references in my document but I don't want to look them up OR type them out."  This was years ago but people are still going to Bible Seminary and I believe this would be a great tool for them.  Anyone noting scripture in their work could benefit from this tool.  The prerequisits are: Word 2007 (it doesn't work on 2003 or below)  I'm adding the source code for anyone interested and viewing what I did OR interested in enhancing the application.

Add In: http://myofficevideos.com/sourcefiles/wiw%20pub.zip

Source: http://myofficevideos.com/sourcefiles/wordinword.zip

 

 

Type reference.

Select the tag button and 'insert scripture'

Scripture inserts after reference (no matter what you have selected)

It will also insert disconnected verses and continuous references (i.e. 3:4-5 or 4:1-5:4 [only between two chapters, no more])

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5