Click here to Skip to main content
15,867,308 members
Articles / Programming Languages / C# 4.0

Symbol Inserter

Rate me:
Please Sign up or sign in to vote.
4.94/5 (23 votes)
8 Jul 2013CPOL5 min read 39K   1.4K   28   13
An application allowing you to enter your top ten symbols without your fingers leaving the keyboard.

Image 1

Introduction

When I first began developing, I was especially annoyed by the lack of several symbols (such as © and ☺) on the keyboard. This application presents the solution I eventually created for this problem.

When I wrote this, I first created a visual interface and then plugged code into the background to make it work – thus I am presenting this article with this format.

Goals for the Application

The basic goals for the application were as follows:

  • Try to replicate operating system look-and-feel.
  • Implement hands-on-keyboard functionality. This means registering a HotKey for the application on the keyboard, and using the number keys on the keyboard to select a symbol, close the application, and insert the symbol into your document, code, etc.
  • Allow the user to customize the symbols.
  • Automatically run the application when the user logs in so that one is spared the pain of starting it all the time.
  • Keep a notification icon in the tasktray to allow the user to close the application.

This is the summary of a single use of the application:

Image 2

Creating the Visual Interface

Command Links and emulating OP look-and-feel

Image 3

These controls can be found in the Microsoft API Code Pack. Simply download them and place them in your toolbox. The gray bar along the bottom of the application consists of two panels with dock set to bottom. The colors were obtained by using Paint's 'pick a color' tool on a screenshot of IE9's download dialog.

The Settings dialog

Image 4

In the settings dialog, the user can change what's in their symbol collection and the order they are in. They can also set the application to start when they login.

The code here is pretty straightforward. Perhaps the only part that can be commented on is moving the symbols up and down:

C#
private void btnUp_Click(object sender, EventArgs e)
{
    if (lstCollection.SelectedIndex != 0 && lstCollection.SelectedIndex != -1)
    {
        int point = lstCollection.SelectedIndex;
        char s = (char)lstCollection.SelectedItem;
        lstCollection.Items.RemoveAt(point);
        lstCollection.Items.Insert(point - 1, s);
        lstCollection.SelectedIndex = point - 1;
    }
}
 
private void btnDown_Click(object sender, EventArgs e)
{
    if (lstCollection.SelectedIndex != lstCollection.Items.Count - 1 && 
                  lstCollection.SelectedIndex != -1)
    {
        int point = lstCollection.SelectedIndex;
        char s = (char)lstCollection.SelectedItem;
        lstCollection.Items.RemoveAt(point);
        lstCollection.Items.Insert(point + 1, s);
        lstCollection.SelectedIndex = point + 1;
    }
}

The Tasktray Icon

Image 5

This is fairly basic. This code is in the form's constructor.

C#
//Setup the tasktray icon
icon.Icon = Properties.Resources.Symbol;
icon.Visible = true;
icon.ContextMenu = new ContextMenu();
icon.ContextMenu.MenuItems.Add("Show Dialog", ShwDlg);
icon.ContextMenu.MenuItems.Add("About", Abt);
icon.ContextMenu.MenuItems.Add("-");
icon.ContextMenu.MenuItems.Add("Exit", Exit);
icon.MouseUp += new MouseEventHandler(icon_MouseUp);

Note how a line break can be created by inserting '-' as a menu item. A word of warning about using NotifyIcon is that when you close the application, the icon may hover around in your task-tray until you mouseover. This issue can be resolved by this override:

C#
protected override void OnFormClosing(FormClosingEventArgs e)
{
    if (e.CloseReason == CloseReason.UserClosing && !AppClosingDown)
    {
        //Don't close, hide.
        e.Cancel = true;
        
        //Act as if the user clicked the Cancel button.
        btnCancel_Click(this, e);
    }
    else
    {
        icon.Visible = false;
        icon.Dispose();
    }
 
    base.OnFormClosing(e);
}

Also, note how the form rejects the close call and hides instead, if the user clicks the form's close button.

Loading the RadioButtons and handling the Main Form

Image 6

Loading the radio buttons is pretty straightforward. Here is the basic code run in the form's constructor. The radio buttons were preexistent from the Designer.

C#
//Process the radiobuttons.
int foundsofar = 0;
foreach (Control c in this.Controls)
{
    if (c.ToString().Contains("System.Windows.Forms.RadioButton"))
    {
         //Add a click handler
         c.Click += new EventHandler(c_Click);
 
         //Set the symbol
         c.Text = Properties.Settings.Default.Symbols[foundsofar].ToString();
 
         //If it was π make the font Arial Rounded MT Bold - looks better.
         if (c.Text == "π")
             c.Font = new Font("Arial Rounded MT Bold", c.Font.Size);
 
         //For the next control discovered.
         ++foundsofar;
    }
}

Then, the code for c_Click is this:

C#
void c_Click(object sender, EventArgs e)
{
    //We don't need to worry if the user
    //changes their mind because the timer immediately stops
    //if the form has already extended.
    tmAnim.Start();
}

And the timer's tick event is this:

C#
private void tmAnim_Tick(object sender, EventArgs e)
{
    if (this.Height != 318)
    {
        this.Top -= 5;
        this.Height += 10;
    }
    else
    {
        tmAnim.Stop();
    }
}

Thus, when the user clicks any of the radio buttons, the form slides out and looks like the picture above.

Creating Action

That's the visual interface covered! Now I will summarize how the application actually whirrs.

Starting when the User logs in

This requires some Registry editing. This article was my inspiration.

First, you will need these values:

C#
private const string RegistryPath = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run";
private const string KeyName = "JohnsonSymbolInserter";

You can change KeyName to whatever you wish, of course. Here is the basic code summary:

C#
//Get value
RegistryKey rk = Registry.CurrentUser.OpenSubKey(RegistryPath);
bool isRunningOnLogin = (rk.GetValue(KeyName) != null);
 
//Set as yes
RegistryKey rk = Registry.CurrentUser.OpenSubKey(RegistryPath, true);
rk.SetValue(KeyName, Application.ExecutablePath);
 
//Set as no
RegistryKey rk = Registry.CurrentUser.OpenSubKey(registryPath, true);
rk.DeleteValue(KeyName);
 
//Flip value
RegistryKey rk = Registry.CurrentUser.OpenSubKey(registryPath, true);
if (rk.GetValue(KeyName) == null)
   rk.SetValue(KeyName, Application.ExecutablePath);
else
   rk.DeleteValue(KeyName);

Registering a HotKey

This article (again!) was my source for implementing HotKeys. Basically, we need these external calls:

C#
[DllImport("user32.dll")]
private static extern int RegisterHotKey(IntPtr hwnd, int id, uint mod, Keys k);
 
[DllImport("user32.dll")]
private static extern int UnregisterHotKey(IntPtr hwnd, int id);

And we will also need these constants (you can also register HotKeys for Ctrl, Alt, etc., see here for more info on RegisterHotKey).

C#
private const int ID = 0xFDAE;
private const int WindowsKey = 0x0008;

You then override OnHandleCreated, OnHandleDestroyed, and WndProc as below:

C#
protected override void OnHandleCreated(EventArgs e)
{
    base.OnHandleCreated(e);
 
    //The hot key...
    try
    {
        RegisterHotKey(this.Handle, ID, WindowsKey, Keys.S);
    }
    catch
    {
        MessageBox.Show("The application failed to register the Windows+S HotKey. " + 
          "The application will now exit.", "Register Failed", 
          MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
        Application.Exit();
    }
}
 
protected override void OnHandleDestroyed(EventArgs e)
{
    base.OnHandleDestroyed(e);
 
    //The hot key...
    try
    {
        UnregisterHotKey(this.Handle, ID);
    }
    catch
    {
        //Hmm...
    }
}
 
protected override void WndProc(ref Message m)
{
    if (m.Msg == 0x0312)
    {
        this.ShowForm();
    }
 
    base.WndProc(ref m);
}

Sending Symbols - Method One

To insert a symbol is the main objective of this application... how is it done?

The easiest method to implement is to copy your selected symbol to the clipboard, like this.

C#
//Hide the form and reset the height
this.HideForm();
 
//Get the selected symbol
string selected = "";
foreach (Control c in this.Controls)
{
    if (c.ToString().Contains("System.Windows.Forms.RadioButton"))
    {
         //Is it selected?
         if (((RadioButton)c).Checked)
         {
             selected = c.Text;
 
             //De-select it now
             ((RadioButton)c).Checked = false;
         }
    }
}
 
Clipboard.SetText(selected);

Sending Symbols - Method Two

However, having to paste the symbol yourself all the time becomes tiresome. I know this because I was initially using a much-less-polished version of this application; pulling it up one time, I decided I would re-write it and publish it here on CodeProject.

So, I've also used SendKeys in this application. SendKeys can be used to send strings between applications as keyboard input. For example,

C#
SendKeys.Send("Hello World!");

In this example, the application with current focus would receive simulated keyboard input as if someone had just typed instantaneously "Hello World!". The advantage of simulated keyboard input is that the keyboard input can be anything, even a symbol. This method is slightly more work to implement - it requires a few more external calls - but the result is far easier to use.

Because SendKeys sends keystrokes to the currently active window, if SendKeys is called while our application is focused, the symbol will simply be sent to our application.

I used these external calls to solve this issue.

C#
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
 
[DllImport("user32.dll")]
private static extern int SetForegroundWindow(IntPtr hwnd);

When the form is shown, the application grabs the previously active application, as you can see in ShowForm():

C#
private void ShowForm()
{
    //Get the currently active window
    previousApp = GetForegroundWindow();
 
    this.Opacity = 1;
    this.Show();
 
    //Set as focused window
    SetForegroundWindow(this.Handle);

    //Ensure the cancel button is focussed.
    if (!btnCancel.Focused)
        btnCancel.Focus();
}

When the user clicks the "Insert Now" button, this is what happens...

C#
//Hide the form and reset the height
this.HideForm();
 
//Get the selected symbol
string selected = "";
foreach (Control c in this.Controls)
{
    if (c.ToString().Contains("System.Windows.Forms.RadioButton"))
    {
         //Is it selected?
         if (((RadioButton)c).Checked)
         {
             selected = c.Text;
 
             //De-select it now
             ((RadioButton)c).Checked = false;
         }
    }
}
 
//Set previous app as focused window and send the keys to it
try
{
    SetForegroundWindow(previousApp);
    SendKeys.Send(selected);
}
catch
{
    MessageBox.Show("The symbol could not be sent to your document.");
    this.ShowForm();
}

...and you should be able to see how the process works. Another look at the flowchart diagram should help explain:

Image 7

Method Three - An Improvement on Method Two

I promised that you would be able to insert symbols without your fingers leaving the keyboard. Implementing this would save having to stop typing, use your mouse/fingerpad, and then resume typing.

Because there are ten symbols available, we can use the number keys to select our desired symbol, with 0 representing 10.

When this application displays itself, the control which automatically receives focus is the Cancel button (take a look at the above code for ShowForm()). All we need to do is add a KeyUp event handler to this control:

C#
private void btnCancel_KeyUp(object sender, KeyEventArgs e)
{
    int j = 0;
    if (e.KeyData.ToString().Length == 2)
    {
        //Number keypresses are 'D1' to 'D0' respectively.
        if (int.TryParse(e.KeyData.ToString().Substring(1, 1), out j))
        {
            //If it was zero, send number 10.
            if (j == 0)
                j = 10;
 
            //Hide the form and reset the height
            this.HideForm();
 
            //Send the appropiate symbol
            try
            {
                SetForegroundWindow(previousApp);
                SendKeys.Send(Properties.Settings.Default.Symbols[j - 1].ToString());
            }
            catch
            {
                MessageBox.Show("The symbol could not be sent to your document.");
                this.ShowForm();
            }
        }
    }
}

Voila! This final method produces the most convenient user experience of all the three I presented.

Conclusion

I hope you enjoyed this article and that at least a few people enjoy using it. Happy Typing! If you have any comments or suggestions, feel free to post below.

History

  • 8/07/2013: More article polishing.
  • 9/03/2012: Made a few polishes to the article text.
  • 4/08/2012: Realized that the "Edit Symbol" dialog was displaying its icon in the taskbar. Removed that.
  • 31/07/2012: Fixed a bug where the cancel button was not being focused. Also fixed a bug where the radio buttons were not being cleared.
  • 30/07/2012: Initial public release.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Founder
Australia Australia
I'm a university student with a passion for stunning UI design and flawless code.

I fell in love with computers when I was 10 years old, and today I am fluent in C#, Python, Java, Javascript, HTML5, and CSS3. I know a little MATLAB, PHP, and SQL.

In my spare time, I build all sorts of cool things, like this Rubik's Cube Robot.

Away from the keyboard, I enjoy quality time with friends and family, as well as reading, painting, playing the piano, teaching chess & karate, and volunteering within my community.

I have learnt that success comes through hard work and dedication, and that giving back to the same people and communities that have helped me is both important and very rewarding.

Follow me on GitHub

Comments and Discussions

 
GeneralMy vote of 5 Pin
Bassam Abdul-Baki12-Aug-13 3:30
professionalBassam Abdul-Baki12-Aug-13 3:30 
GeneralRe: My vote of 5 Pin
Mitchell J.12-Aug-13 22:21
professionalMitchell J.12-Aug-13 22:21 
Generalwelldone Pin
Ali Al Omairi(Abu AlHassan)7-Jul-13 22:33
professionalAli Al Omairi(Abu AlHassan)7-Jul-13 22:33 
GeneralMy vote of 5 Pin
Michael Haephrati8-Mar-13 23:41
professionalMichael Haephrati8-Mar-13 23:41 
GeneralRe: My vote of 5 Pin
Mitchell J.9-Mar-13 0:22
professionalMitchell J.9-Mar-13 0:22 
GeneralMy vote of 5 Pin
pradiprenushe27-Aug-12 3:00
professionalpradiprenushe27-Aug-12 3:00 
QuestionHave you seen ... Pin
Richard MacCutchan18-Aug-12 23:27
mveRichard MacCutchan18-Aug-12 23:27 
AnswerRe: Have you seen ... Pin
Mitchell J.23-Aug-12 0:44
professionalMitchell J.23-Aug-12 0:44 
GeneralRe: Have you seen ... Pin
Richard MacCutchan23-Aug-12 1:41
mveRichard MacCutchan23-Aug-12 1:41 
GeneralMy vote of 4 Pin
Member 844697331-Jul-12 1:53
Member 844697331-Jul-12 1:53 
GeneralRe: My vote of 4 Pin
Mitchell J.31-Jul-12 19:56
professionalMitchell J.31-Jul-12 19:56 
QuestionNice Pin
Dave Kerr30-Jul-12 21:30
mentorDave Kerr30-Jul-12 21:30 
AnswerRe: Nice Pin
Mitchell J.30-Jul-12 21:45
professionalMitchell J.30-Jul-12 21:45 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.