Add Icon to notification area in Windows tray

September 30, 2011

During a recent conversation with a client they mentioned they wanted a Windows Forms Application to pull information from their SharePoint site. We discussed the requirements and I said yes that fine we can do that. They then said they would like the application to run in the background and to have an Icon in the Notification area, see screenshot below, which pops up telling them some new content has been added to SharePoint.

NotificationArea

My immediate reaction was it must be relatively easy but it was not something I had tried before. When I got back to the office I created a test application and I was surprised by how easy this was. I have included below some same code which hides the application when it is started, adds an item to the notification area and displays a popup every 10 seconds. If this popup is click it will open the application or if the icon in the notification area is click it will also open the application.

Hopefully you find this useful and can use this as a starting point for further developments. As always this code should always be tested before being deployed to any live environments and is used at your own risk.

Code Snippet
  1. public partial class Form1 : Form
  2.     {
  3.         NotifyIcon notify;
  4.         Timer timer;
  5.         public Form1()
  6.         {
  7.             InitializeComponent();
  8.             timer = new Timer();
  9.             timer.Interval = 10000;
  10.                 timer.Start();
  11.             timer.Tick +=new EventHandler(timer_Tick);
  12.  
  13.             notify = new NotifyIcon();
  14.             notify.Icon =
  15.    new System.Drawing.Icon(System.Environment.GetFolderPath
  16.    (System.Environment.SpecialFolder.Personal)
  17.    + @"\logo.ico");
  18.             notify.Visible = true;
  19.             notify.Text = "Test notification area icon";
  20.  
  21.             this.WindowState = System.Windows.Forms.FormWindowState.Minimized;
  22.             this.Visible = false;
  23.             this.ShowInTaskbar = false;
  24.         }
  25.  
  26.         public void  timer_Tick(object sender, EventArgs e)
  27.         {
  28.             notify.Click += new EventHandler(notify_Click);
  29.             notify.ShowBalloonTip(3000, "Test notification area header", "Test notification area test", ToolTipIcon.Info);
  30.             notify.BalloonTipClicked += new EventHandler(notify_BalloonTipClicked);
  31.         }
  32.  
  33.         void notify_Click(object sender, EventArgs e)
  34.         {
  35.             this.WindowState = System.Windows.Forms.FormWindowState.Normal;
  36.             this.Visible = true;
  37.         }
  38.  
  39.         void notify_BalloonTipClicked(object sender, EventArgs e)
  40.         {
  41.             this.WindowState = System.Windows.Forms.FormWindowState.Normal;
  42.             this.Visible = true;
  43.         }
  44.     }

Advertisements

Creating installation package for Windows Forms Applications

August 5, 2011

Over the years I have created numerous Windows Forms Applications for a variety of purposes. Normally this is for my own use or for use by an IT team but on a recent project I was developing a Windows Forms Application which is to be used by regular Joe Blogs end users. This meant I needed to provide an installer to ensure all the prerequisites were installed i.e. .Net framework, etc. While it is not something I have had to do a lot of I have created a few installers in my time, however most of them have used the OOTB Setup Project template from Visual Studio.

On this project I am still using the OOTB Setup Project, however I have also had to capture some additional parameters during the installation such as the URL of the web service to connect to. I knew this would be possible but I had no idea what would be involved but after some research I was able to find an excellent MSDN article on Using a Custom Action to Display a Message at Installation. While the idea is slightly different the principle is the same and you can easily adjust this. In my case I capture the web service URL and update the settings file with the URL supplied. I found a good article which details the steps required to update the configuration file. My code is slightly different in that it is within the custom installer class so I have included this below as well.

Code in Customer Installer Class

Code Snippet
  1. public override void Install(System.Collections.IDictionary stateSaver)
  2.         {
  3.             base.Install(stateSaver);
  4.             try
  5.             {
  6.                 string webserviceURL = Context.Parameters["PARAMETER1"];
  7.                 
  8.                 Configuration config = ConfigurationManager.OpenExeConfiguration(Assembly.GetExecutingAssembly().Location);
  9.                 ApplicationSettingsGroup group = config.GetSectionGroup("applicationSettings") as ApplicationSettingsGroup;
  10.                 ClientSettingsSection settingsSection = group.Sections["Applications.Properties.Settings"] as ClientSettingsSection;
  11.                 SettingElement webServiceSetting = settingsSection.Settings.Get("SETTINGNAME");
  12.                 webServiceSetting.Value.ValueXml.InnerText = webserviceURL;
  13.                 config.Save(ConfigurationSaveMode.Full, true);
  14.             }
  15.             finally
  16.             {
  17.                 base.Dispose();
  18.             }
  19.         }


DataGridView CellEnter event fires multiple times

August 4, 2011

When working on a Windows Forms Application I had a DataGridView and I wanted to update some textboxes at the bottom of the form when the user selected a particular row/cell. I looked at the different DataGridView events available and decided the best option to cover users clicking on a cell and them navigating around the DataGridView using the up and down arrows was the CellEnter event.

Everything seemed to be working fine but I was occasionally getting some strange result or errors so I decided to spend some time debugging to try and identify the issue. What I found was even if I was not clicking on the DataGridView certain other actions caused the CellEnter event to fire also when I first clicked on an a row/cell after loading the form the event fired twice. This was extremely problematic in my case as I was doing some additional processing on a background worker thread so these miscellaneous events being fired resulted in unexpected results and the occasion exception as the background worker thread was already in the middle of processing another request.

Reading up on the CellEnter event it mentions that the event may occur twice if the DataGridView didn’t have input focus. Thinking about it this made sense and related to the issue I was having where the event was indeed firing twice. What must be happening is when a user clicks on a cell the focus must first be set on the DataGridView and when this happens it must act like it is selecting the first cell hence every time I was debugging the first time around the column and row indexed were both 0. I was able to get around this by adding the code below to the top of the CellEnter event. This way if the DataGridView wasn’t Focused I could exist the event without processing.

Code Snippet
  1. if (!DgvData.Focused)
  2.             {
  3.                 return;
  4.             }

 

This seemed to work but it meant when I loaded the DataGridView first time around the additional information at the bottom of my form was not filled in as technically the form still didn’t have input focus. I was able to get around this by manually setting the focus on the DataGridView after I assigned my data source. This would then trigger the CellEnter event where DgvData.Focused would now be true so it would continue and do the additional processing I wanted.

Code Snippet
  1. //binding causes the cell enter event to fire.
  2.                     DgvData.DataSource = calls;                    
  3.                     //show calls tab. This causes the cell enter event to fire as well.
  4.                     ShowTab(TpCallPlans);
  5.                     
  6.                     DgvData.Focus();

 

The other issue I was having was the CellEnter event was firing when I was not touching the DataGridView. In my form I had multiple tabs and the DataGridView was on the first one. I have several buttons on the form that show or hide the relevant tabs but what I noticed was that removing a tab from the TabControl also triggered the CellEnter event. The code below shows the function I was using to only show the appropriate tab and when stepping through the code line 11, actually removing the tab from the TabControl, caused the CellEnter event to fire. Obviously this was not what I was expecting and was leading to incorrect data on certain sections of the form. What I found was the same fixed I used above also corrected this issue as while the event still fired the DataGridView didn’t have input focus so it simply existed the method.

Code Snippet
  1. /// <summary>
  2.         /// Method to hide all tabs except the one supplied
  3.         /// </summary>
  4.         /// <param name="tpToShow">A TabPage object of the tab we want to show</param>
  5.         private void ShowTab(TabPage tpToShow)
  6.         {
  7.             foreach (TabPage tp in TbOptions.TabPages)
  8.             {
  9.                 if (!tp.Name.Equals(tpToShow.Name, StringComparison.CurrentCultureIgnoreCase))
  10.                 {
  11.                     TbOptions.TabPages.Remove(tp);
  12.                 }
  13.  
  14.                 if (TbOptions.TabPages.Count == 0)
  15.                 {
  16.                     TbOptions.TabPages.Add(tpToShow);
  17.                 }
  18.  
  19.             }
  20.         }

 

Hopefully this can help other people encountering the same issue as it took me a while to work out why this was happening and how to resolve it.


Creating button shortcuts in Windows Forms Applications

July 22, 2011

I recently created a Windows Forms Application and some of the feedback from users was they would like to have some shortcuts for certain buttons. At first I just assumed this was easy enough but when I started looking into it I realised there didn’t appear to be any out of the box functionality that would do this.

As I’m sure most people do I turned to Google and found that there wasn’t a way to handle this with out custom code. At first I looked at using the KeyDown event on the form but this didn’t quite work as expected so I managed to find an another article which suggested overriding the ProcessCmdKey method. I tried this and like magic it works, see code below.

To be honest I would expect this functionality to work without the need to write custom code but there isn’t much to it so its not too bad.

 

Code Snippet
  1. protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
  2.         {
  3.             switch(keyData)
  4.             {                
  5.                 case (Keys.Control | Keys.N):
  6.                     BtnTest1_Click(null, null);
  7.                     break;
  8.                 case (Keys.Control | Keys.S):
  9.                     BtnTest2_Click(null, null);
  10.                     break;
  11.                 case (Keys.Control | Keys.F):
  12.                     BtnTest3_Click(null, null);
  13.                     break;
  14.                 case (Keys.Control | Keys.G):
  15.                     BtnTest4_Click(null, null);
  16.                     break;
  17.                 case (Keys.Control | Keys.P):
  18.                     BtnTest5_Click(null, null);
  19.                     break;
  20.             }
  21.  
  22.             return base.ProcessCmdKey(ref msg, keyData);
  23.         }


Allow Users to add rows in Data Grid View

July 7, 2011

While working on a project that required me to create a Windows Forms Application I was binding a list of order objects to a Data Grid View but I also wanted users to be able to add items as well. Initially I thought this would be easy I simply set the AllowUsersToAddRows property on the Data Grid View to true, however when I ran the application this didn’t do anything.

I spent some time investigating this and found that you have to create BindingSource object and set the AlloNew property to true and then it adds an extra row where users can add data, see full code below.

Code Snippet
  1. BindingSource source = new BindingSource();
  2.                     source.AllowNew = true;
  3.                     source.DataSource = order.OrderLines.Line.ToList();
  4.  
  5.                     DgvOrder_OrderLines.DataSource = source;


Launching Windows Forms Applications from within an existing Windows Forms Application

July 7, 2011

My most recent project has been to develop a Windows Forms Application that connections to Microsoft Dynamics NAV system. I have been working closely with a NAV specialist who has been exposing some web services I can then use to develop the application.

The application has to be able to perform a variety of tasks and operations so I have separated these out into different tabs to make the interface more intuitive and less cluttered. My problem came when I needed to get a users to select an item from a drop down list when they clicked on a submit button. Normally I could add a panel and then hide this until the user pressed the button but given the amount of information on some forms I decided this would not be a good idea.

My next thought was it would be nice if I could re-use the MessageBox functionality and add a drop down list on this but after some research online I soon learned that this was not possible. I did find some posts about creating a class based on the MessageBox functionality and extending this but that seemed like a waste of time and effort.

I then spent some time researching other options and was surprised to learn how easy it is to call another Windows Forms Application in an already running Windows Forms Application and get some data from it, see example code below.

Launch the new Form
  1. string orderPadNo = String.Empty;
  2.             OrderPad orderPadForm = new OrderPad(customerNumber);
  3.             try
  4.             {
  5.                 // Show select order pads Form as a modal dialog and determine if DialogResult = OK.
  6.                 if (orderPadForm.ShowDialog(this) == DialogResult.OK)
  7.                 {
  8.                     // Read the contents of select order pads combobox
  9.                     ComboBox cbxOrderPads = (ComboBox)orderPadForm.Controls["CbxOrderPad_OrderPads"];
  10.                     if (cbxOrderPads != null)
  11.                     {
  12.                         orderPadNo = cbxOrderPads.SelectedValue as string;
  13.                     }
  14.                 }
  15.             }
  16.             finally
  17.             {
  18.                 if (orderPadForm != null)
  19.                 {
  20.                     orderPadForm.Dispose();
  21.                 }
  22.             }

In my new Windows Forms Application I then have the required functionality/display and two buttons. One button confirms the users selection and the other is for cancelling out of the interface. I have included the code below to return out of the new Windows Forms Application.

Code Snippet
  1. private void BtSelectOrderPad_Cancel_Click(object sender, EventArgs e)
  2.         {
  3.             this.DialogResult = System.Windows.Forms.DialogResult.Cancel;
  4.         }
  5.  
  6.         private void BtSelectOrderPad_Ok_Click(object sender, EventArgs e)
  7.         {
  8.             if (CbxOrderPad_OrderPads.SelectedValue == null || String.IsNullOrEmpty(CbxOrderPad_OrderPads.SelectedValue as string))
  9.             {
  10.                 MessageBox.Show("Please select an order pad");
  11.                 return;
  12.             }
  13.  
  14.             this.DialogResult = System.Windows.Forms.DialogResult.OK;
  15.         }


%d bloggers like this: