Quick Links:
Requires .net framework 4.5 or greater
Summary:
I wanted to see how my stocks are doing periodically at a glance on my windows desktop computer throughout the day. I want a small popup that appears in the bottom right hand corner of the screen semi-frequently, which will show me the daily percentage increase/decrease of the stock.
Basic Requirements:
Behavior:
There should be a small popup in the bottom right hand corner of the screen every X seconds ( seconds are configurable ).
The small popup should list all of the stocks configured that the application is ‘watching’ and display the daily gain/loss in a percentage format.
The small popup should stay present for X seconds ( configurable ).
The list of stocks within the popup will be listed and the text will be white, the percentage gain/loss for the day will be green if positive, white if 0% and red if negative on the day.
The application can be configured to only popup within the US stock markets hours of operation ( taking into account that I live in the UK, also taking into account daylight savings time etc if need be ).
It is possible to ‘force’ the pop-up to show on an off-frequency ( upon a user request to show the popup ).
The popup will have a small X to close the popup forcefully in the top right hand corner.
The background of the pop-up will be black
UI Design template – what it could look like:
( edit/update, the above screenshot changed from the actual implementation, the main difference being the market the stock is listed on e.g. NYSE was omitted from the final implementation )
Environment/Setup:
The application should come with an installer.
The application should not use a database.
The application should reside in the task tray with a small icon.
The application should open a configuration menu when the task tray icon is double clicked.
The application should open a small pop-up menu within the task tray when the icon is right clicked the menu will have : [Open Config]; [Show Prices Now]; [Exit Application] as the menu items.
Application must start on startup of windows ( preferably via start-up folder shortcut )
Configuration:
The user will be able to add stock to watch within the config ( list ).
The stock to watch will be the official ticker abbreviation from either NASDAQ or NYSE ( only ).
The user can configure the frequency of the pop-up.
The user can configure the duration of the pop-up.
Implementation/Tutorial:
Requirements:
- Visual studio ( I use 2017 )
General solution creation:
To start with, create a standard windows forms application ( Visual C# -> Windows Desktop -> Windows Forms App (.NET Framework ) in visual studio. Set the Framework version to target anything higher ( or equal to ) .NET Framework 4.5 ( I used 4.5 ). Call the project StockMarketPopupTickerApplication .
The default form Form1.cs which is shown when the project is created will be our the ‘config page’ of our application, so when a user double clicks the task tray icon, the config page will show. Closing the config page will not close the application.
Setting Default Properties of Form1.cs
We need to enable this form to minimise to task tray first at load, within Form1.cs change the WindowState property to default to Minimized .
Adding a task tray functionality
We want the application to largely reside within the task tray ( bottom right hand corner of the screen ) so we need to find a suitable icon, and attach it to the application. I used https://www.iconfinder.com/ to find a suitable icon and I found this one:
At the following address: https://www.iconfinder.com/icons/87472/market_stock_icon
Download the file as a ICO file
Once you have downloaded your .ICO icon file, we need to attach it to the application. We will attach it to Form1.cs via the designer by adding a NotifyIcon within the designer toolbox:
Dragging and dropping the NotifyIcon into the designer for Form1.cs adds the component to the application.
Once the NotifyIcon has been added to the designer, select the notifyIcon1 and attach the recently downloaded Icon file, you do this by clicking on the notifyIcon1 at the bottom of the Form1.csForm1.cs Designer window, then within the properties panel on the right press the ‘…’ button on the Icon property, and browse to the file you recently downloaded:
We also need to include the icon file into our project for later use. Right click the StockMarketPopupTickerApplication project ( project, not the solution ) and select Add -> Existing item.
Within the browse dialog box we need to find and select our icon file ( the one that was just downloaded ), remember to change the File filters within the browse dialog box, by default it will not show .ico files.
You’ll see that the icon file you have downloaded has been added into the project files appears in the list:
This stage is important, at later on within the installer section, we will need this icon file attached to the project.
Change the icon text within the notifyIcon1 properties panel to something better than “notifyIcon1” change it to “Stock market watcher” or something like that. This Text is what is shown when you hover over the icon within the task bar, so name it something human readable.
We also need to setup the context menu – which is the menu that will appear when the task tray icon is clicked in the bottom right hand corner, we need it to show the options: [Open Config]; [Show Prices Now]; [Exit Application]. Do this by adding a ContextMenuStrip to the Form1.cs just like you did the NotifyIcon ( drag and drop onto the designer ):
Once added, select the contextMenuStrip1 and from within the properties window, find and edit the “Items” property by clicking the “…” button:
A new configuration window will popup, this allows you to add the Menu Items to your context menu. We need three, so add three MenuItem , give them decent names, and also give them a meaningful Text display:
We now need to attach events to each of these Menu items, this can be accomplished by code, or by using some of the designer. I chose to use the designer. If you click on the contextMenuStrip1 at the bottom of the Form1.cs designer, a preview of the ContextMenuStrip will show within the designer:
You will see the context menu appear on the main form
If you now single click on each of your menu items, the properties panel will show the properties of each of the MenuItems , here is where we can add OnClick events:
Select the events properties tab ( the lightning arrow within the properties window ) and double click in the white space next to the “Click” event. This will create an empty placeholder handler of the button being pressed. Something we will handle in the following section.
View within Form1.cs
View within properties designer showing event binding:
Repeat this process for each of the three MenuItem buttons
Which should result in empty code handlers which look like this: ( dependant upon what you called your Menu Items ):
Finally, we need to attach the context menu that we have just built to the notifyIcon1 . Click on the notifyIcon1 within the designer.
And bind the ContextMenuStrip to the notifyIcon1:
If you run your application now, you’ll see your application begin minimised within the system tray, with an icon, which when right clicked will display the three menu items you configured earlier:
Unfortunately, within the task bar, an application icon also appears, which is what we do not want:
To get rid of this change ShowInTaskBar to False for Form1.cs within the designer:
You can also see I have added an Application Icon ( not to be confused with notification icon ) – which I used the same icon as before ( mentioned previously ). I also changed MaximizeBox and MinimizeBox to False , this is so that the settings configuration window doesn’t have a minimise/maximise buttons, which are not needed, I wanted my config window to be a static size.
You can also change the config forms default title to something more appropriate, within Form1 properties ( from the designer) set the Text field:
Which should make the designer title look like this:
If you run your application now, you will see that it has no task bar icon. It has a task tray icon that when right clicked shows three menu items. The application doesn’t do too much after this. We need to be able to restore at least the config window, so we need to hook up the “Open Config” menu item, and also the double click on system tray icon, which should maximise the config window so that we could change settings ( which we are yet to add ).
Adding listener actions to task tray icon
Firstly we can restore the configuration window to a maximized state by attaching a listener to the notify icon double click event.
Attach the event by double clicking the white space within the events list of notifyIcon1:
Within the double click handler, alter the method so that it looks like this:
private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e)
{
if(this.WindowState == FormWindowState.Minimized)
{
this.WindowState = FormWindowState.Normal;
}
}
Add a listener to the window state changing ( we don’t want it to show in task bar unless the control config box is open ).
Add a SizeChanged event handler ( same process as before )
Form Size changed event:
Alter the handler code so that it looks like this:
private void Form1_SizeChanged(object sender, EventArgs e)
{
if (this.WindowState == FormWindowState.Minimized)
{
this.ShowInTaskbar = false;
}
else
{
this.ShowInTaskbar = true;
}
}
Finally we need to override the Form1.cs closing event:
We want the application to remin open ( in the task tray ) even when the application configuration dialog box is closed via pressing ‘X’. The only way that we want the application to fully close is from the task tray icon being right clicked, and choosing ‘Exit’. To handle this we use a class variable for the application to signify when the ‘Exit’ menu item selection has been selected, all other ways of closing the main settings form will not exit the application.
Add the class variable to Form1:
...
public partial class Form1 : Form
{
private bool closeFromNotifcationTray = false;
...
Now from within the FormClosing handler for Form1, alter it to this:
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if(closeFromNotifcationTray == true)
{
// Allow the close to happen,
}
else
{
// Not closed from Exit button in notification tray, so just minimize.
this.WindowState = FormWindowState.Minimized;
e.Cancel = true;
}
}
Now we should adjust the task tray ‘exit’ button handler to set the closeFromNotiifcaionTray to true:
private void exitStripMenuItem_Click(object sender, EventArgs e)
{
closeFromNotifcationTray = true;
Application.Exit();
}
If you build and run the application now, you will see that the following functionality is available:
- Upon start, the application is minimised to task tray
- When task tray icon is double clicked, form1 form shows ( nothing useful shows on the form at the moment )
- When the form1 form is in the foreground ( shown ), the application icon appears in the task bar ( task bar, not just the task tray ) with the selected application icon
- When form1 form is closed ( X in top right hand corner ), it minimised to system tray ( not exists application ).
- Three menu items show when icon in task tray is right clicked
- Upon clicking Exit, the application fully exits
We can now hook up one of the other task tray icon buttons ‘Open Config’, add the following code tot he openConfigStripMenu on Click event ( or whatever you called your ‘Open’ button ):
private void openConfigStripMenu_Click(object sender, EventArgs e)
{
if (this.WindowState == FormWindowState.Minimized)
{
this.WindowState = FormWindowState.Normal;
}
}
The Show Prices Menu item we will hook up later.
Config/Settings dialog
Ok, so now it’s time to add some config to the home dialog box which is form1.cs .
We will need to add three main controls/areas of functionality
- List controlling the stock tickers that we want to poll for/show
- The frequency and duration of the popup
- The option to select if stocks only popup during market opening hours
I won’t explain the full procedure for creating this dialog box and arrangement of controls, I’ll assume you are comfortable in doing so, the target structure of the UI settings config form is show below:
Here is a table of the controls that I added to the desginer
Control type |
Control variable name |
ListBox |
watchedStockList |
Button |
removeStockButton |
Button |
addStockButton |
TextBox |
addStockTextBox |
NumericUpDown |
popupFreqNUD |
NumericUpDown |
popupDurNUD |
CheckBox |
tradingHoursCheckbox |
4x Label |
4x labels to help label some of the controls |
After adding the controls, and fashioning them in a way which is to your liking, we need to resize the main Form control to be a bit more size appropriate, drag and resize the main form so it fits using the designer.
Then set the form size to be an exact size with no re-size ability within the Form properties:
Change FormBorderSize to FixedSingle
This will stop the form from being re-sizeable at run time.
Also change both NumericUpDown controls to have a higher than default maximum. By default these NUD controls will go to a maximum of 100. Given that we are dealing in seconds, it should be a much higher value. ( One every hour would be 3600 seconds, so a max of 3600 seems appropriate for the popupFreqNUD ).
At the moment most of these controls have no events hooked up to them, this is intentional, we will hook these controls up to event handlers in the next section.
Settings Storage ( object for view ):
I want the storage configurations ( such as stocks watched ) to be saved into a persistent storage object so that on each application load, the user doesn’t have to re-configure the application. In order to do this, I firstly need to create a store config class object that I can easily serialise to disk. We will serialize a config object to a file on desk within the executing directory called settingsConfig.xml
Create the following class within your project:
public class ConfigSettingsObject
{
public List<string> watchedStock { get; set; }
public int popupFrequencySeconds { get; set; }
public int popupDurationSeconds { get; set; }
public bool onlyPopupDuringTradingHours { get; set; }
}
This is the object we will store all the settings in and is to be populated on startup and upon each settings change.
Add the following object to the Form1 class:
private ConfigSettingsObject settings;
Within Form1 create some serializer methods which allow you to save/read these config settings:
Create serializer and deserializer methods to serialize to disk.
public void SerializeObject<T>(T serializableObject, string fileName)
{
if (serializableObject == null) { return; }
try
{
XmlDocument xmlDocument = new XmlDocument();
XmlSerializer serializer = new XmlSerializer(serializableObject.GetType());
using (MemoryStream stream = new MemoryStream())
{
serializer.Serialize(stream, serializableObject);
stream.Position = 0;
xmlDocument.Load(stream);
xmlDocument.Save(fileName);
stream.Close();
}
}
catch (Exception ex)
{
MessageBox.Show("Problem with Saving settings to disk: " + ex.Message);
}
}
public T DeSerializeObject<T>(string fileName)
{
if (string.IsNullOrEmpty(fileName)) { return default(T); }
T objectOut = default(T);
try
{
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load(fileName);
string xmlString = xmlDocument.OuterXml;
using (StringReader read = new StringReader(xmlString))
{
Type outType = typeof(T);
XmlSerializer serializer = new XmlSerializer(outType);
using (XmlReader reader = new XmlTextReader(read))
{
objectOut = (T)serializer.Deserialize(reader);
reader.Close();
}
read.Close();
}
}
catch (Exception ex)
{
MessageBox.Show("Problem with opening settings from disk: " + ex.Message + " try deleting the settingsConfig.xml file");
}
return objectOut;
}
Note: Upon adding this code, you’ll need to add using statement references for:
System.Xml , System.Xml.Serialization and System.IO
Within Form1 add a load settings info config method called LoadSettingsIntoConfigForm with the following method body:
private void LoadSettingsIntoConfigForm()
{
this.popupDurNUD.Value = settings.popupDurationSeconds;
this.popupFreqNUD.Value = settings.popupFrequencySeconds;
this.tradingHoursCheckbox.Checked = settings.onlyPopupDuringTradingHours;
this.addStockTextBox.Text = "";
this.watchedStockList.Items.AddRange(settings.watchedStock.ToArray());
}
Now create a method within Form1 to load the settings from from the .xml file we will store on disk
private void LoadSettingsFromFile()
{
if(File.Exists("settingsConfig.xml"))
{
settings = DeSerializeObject<ConfigSettingsObject>("settingsConfig.xml");
}
else
{
// No settings config exists, so we will populate the settings with some default values
settings = new ConfigSettingsObject();
settings.onlyPopupDuringTradingHours = false;
settings.popupDurationSeconds = 5;
settings.popupFrequencySeconds = 60;
settings.watchedStock = new List<string>() { "AAPL","TWTR" };
SaveSettingsToFile();
}
// Now we have the settings file, we can have it load the settings into the config form.
LoadSettingsIntoConfigForm();
}
You’ll see that within this method there is reference to a method called SaveSettingsToFile() , we need to create this method now:
private void SaveSettingsToFile()
{
SerializeObject(settings, "settingsConfig.xml");
}
We now have the basic load/save settings functionality made, we finally need to add the LoadSettingsFromFile() into the Form1_Load
Within the Form1.cs designer double click the Load event, which will add the Form1_Load event handler to the code behind:
Alter the Form1_Load event to call the LoadSettingsFromFile() method:
private void Form1_Load(object sender, EventArgs e)
{
LoadSettingsFromFile();
}
You should now be able to build and run your application, which will result in a settingsConfig.xml file being created within the application executing directory ( ..\StockMarketPopupTickerApplication\StockMarketPopupTickerApplication\bin\Debug in my case ).
If you have a look at the XML file created ( open in notepad++ for example ), you’ll see the basic structure of the object with the default values I hard coded into the application:
<?xml version="1.0"?>
<ConfigSettingsObject xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<watchedStock>
<string>AAPL</string>
<string>TWTR</string>
</watchedStock>
<popupFrequencySeconds>60</popupFrequencySeconds>
<popupDurationSeconds>5</popupDurationSeconds>
<onlyPopupDuringTradingHours>false</onlyPopupDuringTradingHours>
</ConfigSettingsObject>
You’ll see AAPL and TWTR ( Apple and Twitter ) in the XML. Now you can check that the application configuration page has these matching values within it. Run the application, and double click the task tray icon to show the settings dialog box to verify that the default values have been populated into the settings controls:
Now we need to attach change values to the boxes, – note, usually you can do this via the designer route ( by attaching an event to the properties of the NUD box within the Designer – properties window ), or you can manually code the events to be attached at runtime. In this example, we need to have them attached at runtime by code, and not within the form1.cs file, this is to stop an infinite loop.
Explanation: upon load of the form, an event is added to the NUD ( Numeric Up Down controls ) which fires upon change. Also upon load, we are going to populate the value of the NUD, so what will happen is the ‘changed event’ will fire upon loading the saved settings value, which is not strictly true, nothing has ‘changed; from the user, so we only want these events to fire after the NUD values have been loaded ( from saved values file ).
I have an AttachListeners method within Form1.cs , which is called after the LoadSettingsFromFile method is called. Create this method:
// We don't want the listeners firing whilst we are loading settings
// so we attach the listeners AFTER the load of the values from settings
private void AttachListeners()
{
this.popupFreqNUD.ValueChanged += new System.EventHandler(this.popupFreqNUD_ValueChanged);
this.tradingHoursCheckbox.CheckedChanged += new System.EventHandler(this.tradingHoursCheckbox_CheckedChanged);
this.popupDurNUD.ValueChanged += new System.EventHandler(this.popupDurNUD_ValueChanged);
}
The three event method handlers will currently not exist, we will create them soon.
The functionality I want is to save the settings configured by the user to file on every configuration control change we do this by calling the previously created SaveSettingsToFile method that we created earlier.
We now need to add the three missing listeners, two NUD listeners, and one checkbox changed listener:
private void popupFreqNUD_ValueChanged(object sender, EventArgs e)
{
settings.popupFrequencySeconds = Convert.ToInt32(popupFreqNUD.Value);
SaveSettingsToFile();
}
private void popupDurNUD_ValueChanged(object sender, EventArgs e)
{
settings.popupDurationSeconds = Convert.ToInt32(popupDurNUD.Value);
SaveSettingsToFile();
}
private void tradingHoursCheckbox_CheckedChanged(object sender, EventArgs e)
{
settings.onlyPopupDuringTradingHours = tradingHoursCheckbox.Checked;
SaveSettingsToFile();
}
Finally call the AttachListeners() method from within the Form1_Load() method:
private void Form1_Load(object sender, EventArgs e)
{
LoadSettingsFromFile();
AttachListeners();
}
You can now test your application by running it, and making sure that the changes you make to the two NUD and Checkboxes save to disk.
Adding/removing stock controls
Now it’s time to hook up the add/remove stock to watch control and have the list show the stocks we are watching. We need an add, remove, and re-draw method.
The re-draw method is called every time the back-end object ( in settings ) changes.
Add an ‘on-Click’ event to the remove and add buttons ( I used the designer and properties events box for this ), no need to have these in the AttachListeners() setup method:
private void removeStockButton_Click(object sender, EventArgs e)
{
if (this.watchedStockList.SelectedItem != null)
{
string stockToRemove = this.watchedStockList.SelectedItem.ToString();
if(settings.watchedStock.IndexOf(stockToRemove) >= -1)
{
settings.watchedStock.Remove(stockToRemove);
SaveSettingsToFile();
DrawListBasedOnStock();
}
}
}
private void addStockButton_Click(object sender, EventArgs e)
{
if(String.IsNullOrEmpty(this.addStockTextBox.Text) == false)
{
string stockToAdd = this.addStockTextBox.Text;
if (settings.watchedStock.IndexOf(stockToAdd) >= 0)
{
// Already added
}
else
{
settings.watchedStock.Add(stockToAdd);
this.addStockTextBox.Text = "";
SaveSettingsToFile();
DrawListBasedOnStock();
}
}
}
Then add the Referencing DrawListBasedOnStock():
private void DrawListBasedOnStock()
{
this.watchedStockList.Items.Clear();
this.watchedStockList.Items.AddRange(settings.watchedStock.ToArray());
}
Right now, if you run your application, the config page should be all hooked up, and any changes you make to the config, should be saved back to disk. To test this, run the application, change some values, close the application, then run it again. The values should be updated.
Example:
Building the code to get prices:
Third party service selection:
For the purposes of this project, after a couple of Google searches, it seemed that a service called AlphaVantage was a suitable candidate to be able to get the stock prices from (https://www.alphavantage.co/) however after I had a play with this API, it was not clear ( or possible ) to obtain the data I wanted – I do not believe that it provides an intra-day percentage from the market opening. All I could find was an intra-day range of 60 minutes, and not the whole day. Having dug around a little bit into the responses that the API returned, I saw that within the AlphaAdvantage response data that it actually used a provider called IEX, so I decided to take a look at the IEX exchange as a stock market price provider
https://iextrading.com/developer/
Reading through their API, it seems to allow me to query one stock and get the real time percentage change of a stock.
Update: As of June 1st 2019, the original iextrading API is no longer available, they have moved to iexcloud ( which is still free, you just need to sign up ):
https://iexcloud.io/
Some of the below may be out of date, but the changes have been included in the source repository within GIT.
Querying the IEX API programmatically:
Now that we have selected an ideal API to query from, we need to build some code which can query each stock and extract the percentage from the response.
Setup a static new class to help with the calls to the IEX API:
Note: NuGet package Newtonsoft.Json should be installed for this to work:
Install-Package Newtonsoft.Json
Create a new class IEXHelper.cs
IEX Helper Class for querying stock prices:
public static class IEXHelper
{
private const string IEXURL = "https://api.iextrading.com/1.0/";
/// <summary>
/// returns a percentage ( as Decimal ) for a stock ticker
/// </summary>
/// <param name="symbol"></param>
/// <returns></returns>
public static decimal GetPercentageForStock(string symbol)
{
decimal toReturn = -1;
// Build query to call against API:
string paramaters = $"stock/{symbol}/quote?displayPercent=true";
string objectAsString = APICall(paramaters);
var data = (JObject)JsonConvert.DeserializeObject(objectAsString);
toReturn = data["changePercent"].Value<Decimal>();
return toReturn;
}
/// <summary>
/// https://stackoverflow.com/questions/9620278/how-do-i-make-calls-to-a-rest-api-using-c
/// </summary>
/// <param name="paramaters"></param>
/// <returns></returns>
private static string APICall(string paramaters)
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(IEXURL);
// Add an Accept header for JSON format.
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// List data response.
HttpResponseMessage response = client.GetAsync(paramaters).Result; // Blocking call!
if (response.IsSuccessStatusCode)
{
// Parse the response body. Blocking!
return response.Content.ReadAsStringAsync().Result;
}
else
{
throw new Exception("Invalid status response: " + response.StatusCode);
}
}
}
You will need to make sure that your project already has the System.Net.Http assembly already added into the project ( .net 4.5 by default includes this assembly reference ):
If not, you can easily add reference to the assembly:
Within IEXHelper.cs you will need to add references to the following namespaces within this file:
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
Test project to test IEXHelper class:
I also quickly created a test project ( new Project -> Visual C# -> Test -> Unit Test Project ) to test these methods whilst developing code ( this is optional ):
[TestClass()]
public class IEXHelperTests
{
[TestMethod()]
public void GetPercentageForStockTestValidSymbol()
{
decimal percentage = IEXHelper.GetPercentageForStock("AAPL");
Assert.IsNotNull(percentage);
}
[TestMethod()]
[ExpectedException(typeof(Exception))]
public void GetPercentageForStockTestInvalidSymbol()
{
IEXHelper.GetPercentageForStock("EXXAMPLE");
}
}
Now we have a helper class which allows us to get the data from the IEX API on what we want to show in the popup. Next it is time to build the pop-up dialog box and show the data.
Building the popup box
I will use the standard Windows Forms class here and build the basic shape within the Visual Studio designer ( although it won’t have all that many controls on it ). Most of the form will be dynamic – listing X amount of stocks, so the size/controls/content will all be added dynamically upon loading the form.
Firstly, I create a new form ( right click project -> Add -> Windows Form ) called PopupForm
The size on the designer will be big by default, but this does not matter as it will be dynamically re-sized to fit the contents. We do however need to set the background of the Form to black and remove the border:
We also need the popup box to not show within the task bar ( by default an application icon will appear within the active windows task bar, which is not what Iwant ):
That is all that is needed to be changed from the Designer side of the form, the rest will be handled within code at run time.
Dynamic insertion of data into popup box
Now that we have a base popup box built ( albeit a very simple form, with no border, and a Black background ) we are not going to actually use this Form directly. We are going to create a new Class called DynamicPopupForm which will derive from the PopupForm and override some key methods ( such as the startup method ) so that we can do some dynamic display.
Create a new class DynamicPopupForm.cs ( right click project, Add New – Class ) which should look like this:
public class DynamicPopupForm : PopupForm
{
int secondsToDisplayFor = 5;
System.Timers.Timer timer;
public DynamicPopupForm(List<StockListObjectData> data, int secondsToDisplayFor)
{
this.secondsToDisplayFor = secondsToDisplayFor;
this.Shown += DynamicPopupForm_Shown;
}
private void DynamicPopupForm_Shown(object sender, EventArgs e)
{
timer = new System.Timers.Timer();
timer.Interval = secondsToDisplayFor * 1000;
timer.Elapsed += Timer_Elapsed;
timer.Enabled = true;
}
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
timer.Enabled = false;
this.Invoke((System.Windows.Forms.MethodInvoker)delegate
{
this.Close();
});
}
}
You will see that constructor of the DynamicPopupForm form has an List<> of StockListObjectData passed into it. This is a ‘model’ class that I have created in a separate file StockListObjectData.cs , create this new file, and make it look like this :
public class StockListObjectData
{
public string StockName { get; set; }
public decimal PercentageChange { get; set; }
}
This Class is essentially our model which we are passing through to the view ( the DynamicPopupForm )
What the constructor is doing is showing the form ( it will be blank ), then after X amount of seconds, automatically closing the form.
Now re-visit the DynamicPopupForm.cs and add a using namespace reference to System.Timers :
using System.Timers;
We can test this newly created DynamicPopupForm by hooking up the showPricesNowMenuItem_Click within Form1.cs to call a new DynamicPopupForm
Within Form1.cs , hook up the show prices menu item event to a new method called ShowStockPrices :
private void showPricesNowMenuItem_Click(object sender, EventArgs e)
{
ShowStockPrices();
}
private void ShowStockPrices()
{
List<StockListObjectData> data = new List<StockListObjectData>()
{
new StockListObjectData(){ PercentageChange = -1, StockName = "AAPL"},
new StockListObjectData(){ PercentageChange = -0.05M, StockName = "MSFT"},
new StockListObjectData(){ PercentageChange = 2.05M, StockName = "FMC"},
};
DynamicPopupForm dpop = new DynamicPopupForm(data, 5);
dpop.Show();
}
You can see that the ShowStockPrices() currently builds a test set of StockListObjectData which is then passed through to the DynamicPopupForm constructor.
Now if you run your application and right click the icon in the taskbar, you can click the “Show Prices Now” menu item, you’ll see that the DynamicPopupForm ( all black ) shows for 5 seconds, then disappears – nothing special yet… The data is not used at the moment, the next section will use this test data.
Dynamic Layout
Now that the general control/show of the DynamicPopupForm is built, we need to add data to it add code to dynamically resize the form based on inputs.
I used the following method to build a layout to the form ( which is placed within DynamicPopupForm.cs ):
private Size BuildStockListUI(List<StockListObjectData> data)
{
// 2 pixels in, just to have a slight padding
int xlocation = 2;
int ylocation = 2;
int contentHeight = 0;
int contentWidth = 0;
// Dynamically add controls to the form
foreach(var stockData in data)
{
var stockTicker = new System.Windows.Forms.Label();
stockTicker.AutoSize = true;
stockTicker.Size = new System.Drawing.Size(100, 15);
stockTicker.Name = "label" + stockData.StockName;
stockTicker.Text = stockData.StockName;
stockTicker.Location = new System.Drawing.Point(xlocation, ylocation);
stockTicker.ForeColor = System.Drawing.Color.White;
this.Controls.Add(stockTicker);
var percentageLabel = new System.Windows.Forms.Label();
percentageLabel.AutoSize = true;
percentageLabel.Size = new System.Drawing.Size(100, 15);
percentageLabel.Name = "labelPercent" + stockData.StockName;
percentageLabel.Text = stockData.PercentageChange.ToString("0.00") + "%";
percentageLabel.Location = new System.Drawing.Point(xlocation+100, ylocation);
// Change percentage based on plus mins or equal
if (stockData.PercentageChange > 0)
{
percentageLabel.ForeColor = System.Drawing.Color.Green;
percentageLabel.Text = "+" + percentageLabel.Text;
}
else if (stockData.PercentageChange < 0)
{
percentageLabel.ForeColor = System.Drawing.Color.Red;
}
else
{
percentageLabel.ForeColor = System.Drawing.Color.Gray;
}
this.Controls.Add(percentageLabel);
ylocation += 15;
contentWidth = 200;
contentHeight += 15;
}
return new System.Drawing.Size(contentWidth, contentHeight);
}
Note: You’ll need to add using reference to System.Drawing :
using System.Drawing;
Most of this code is just dynamic layout code which adds labels to the form in an orderly fashion. It returns a Size object so that the application knows how big the parent control should be. Notice that we set green/red fore colour of the Label depending upon if the stock is positive/negative for the day.
To call this, adjust the constructor of the DynamicPopupForm to the below:
public DynamicPopupForm(List<StockListObjectData> data, int secondsToDisplayFor)
{
Size sizeOfContent = BuildStockListUI(data);
// + 4 is for the 2 pixels padding
// + 10 is for room for X button.
this.ClientSize = new System.Drawing.Size(sizeOfContent.Width + 14, sizeOfContent.Height + 4);
this.secondsToDisplayFor = secondsToDisplayFor;
this.Shown += DynamicPopupForm_Shown;
}
By returning the Size of the controls which are built by the BuildStockListUI method, it allows the parent form ( DynamicPopupForm ) to be resized to fit the content, which is adjusted by setting the form ClientSize property. I have added an extra 10 pixels to the width, and 4 pixels for padding on all sides.
If you now run the application, and press the ‘Show Stocks Now’ button, you’ll see a popup with our test data, which will seemly appear in the ‘middle’ of your screen.
The popup is shown correctly with the test data, and it is of appropriate size, but the positioning is wrong, I want it to appear in a discrete location on the screen ( bottom right hand corner ).
The following code allows us to do this:
Rectangle workingArea = Screen.GetWorkingArea(this);
this.Location = new Point(workingArea.Right - Size.Width,
workingArea.Bottom - Size.Height);
Put this within the constructor within DynamicPopupForm.cs : ( full constructor code: )
public DynamicPopupForm(List<StockListObjectData> data, int secondsToDisplayFor)
{
Size sizeOfContent = BuildStockListUI(data);
// + 4 is for the 2 pixels padding
// + 10 is for room for X button.
this.ClientSize = new System.Drawing.Size(sizeOfContent.Width + 14, sizeOfContent.Height + 4);
this.TopMost = true;
// Move it to bottom right hand corner of screen.
Rectangle workingArea = Screen.GetWorkingArea(this);
this.Location = new Point(workingArea.Right - Size.Width,
workingArea.Bottom - Size.Height);
this.secondsToDisplayFor = secondsToDisplayFor;
this.Shown += DynamicPopupForm_Shown;
}
Note: You’ll also need to add a namespace reference to System.Windows.Forms :
using System.Windows.Forms;
Note2: I also added this.TopMost to make sure that the popup is always on top. If I clicked somewhere in a web browser that was full screen, the popup would disappear, which is not what I wanted. So Setting TopMost to true makes sure it is always on top of other windows.
Lastly we need to add a small cross in the top right hand corner of the popup in order to be able to forcefully close the popup should we need to. Add the following code to the DynamicPopupForm class
private void AddCloseButton(int currentWidth)
{
var closeLabel = new System.Windows.Forms.Label();
closeLabel.Text = "X";
closeLabel.ForeColor = System.Drawing.Color.Gray;
closeLabel.AutoSize = true;
closeLabel.Size = new System.Drawing.Size(10, 10);
closeLabel.Location = new System.Drawing.Point(currentWidth, 0);
closeLabel.Click += CloseLabel_Click;
this.Controls.Add(closeLabel);
}
private void CloseLabel_Click(object sender, EventArgs e)
{
timer.Stop();
this.Close();
}
We hook up the close label to a new event which firstly stops the timer from firing ( timer.Stop() ), then closes the DynamicPopupForm . Note: it is important to stop the timer first so that the application does not exception.
We call this AddCloseButton from within the constructor after the dynamic building of the stock ( BuildStockListUI ) has taken place:
Alter the DynamicPopupForm constructor to call the AddCloseButton :
public DynamicPopupForm(List<StockListObjectData> data, int secondsToDisplayFor)
{
Size sizeOfContent = BuildStockListUI(data);
// + 4 is for the 2 pixels padding
// + 10 is for room for X button.
AddCloseButton(sizeOfContent.Width);
this.ClientSize = new System.Drawing.Size(sizeOfContent.Width + 14, sizeOfContent.Height + 4);
this.TopMost = true;
// Move it to bottom right hand corner of screen.
Rectangle workingArea = Screen.GetWorkingArea(this);
this.Location = new Point(workingArea.Right - Size.Width,
workingArea.Bottom - Size.Height);
this.secondsToDisplayFor = secondsToDisplayFor;
this.Shown += DynamicPopupForm_Shown;
}
If you run the application now, and call the “Show Popup Now” it should display the test data that we input earlier in the ShowStockPrices() method. The popup should appear in the bottom right hand corner of your screen, and should look like:
Working with Real Data
Next we can build the application to use real data from IEX rather than the fake data we put into ShowStockPrices() earlier. Alter the ShowStockPrices() method within Form1.cs and remove the fake data, and replace with the following:
private void ShowStockPrices()
{
var data = GetStockPrices(settings);
DynamicPopupForm dpop = new DynamicPopupForm(data, 5);
dpop.Show();
}
This calls a new method GetStockPrices() which accepts a ConfigSettingsObject object, the new method GetStockPrices() looks like this:
private List<StockListObjectData> GetStockPrices(ConfigSettingsObject settings)
{
List<StockListObjectData> toReturn = new List<StockListObjectData>();
foreach(var stock in settings.watchedStock)
{
StockListObjectData newData = new StockListObjectData();
newData.StockName = stock;
newData.PercentageChange = IEXHelper.GetPercentageForStock(stock);
toReturn.Add(newData);
}
return toReturn;
}
This method will call the IEXHelper method GetPercentageForStock , passing in the stock ticker name ( e.g. AAPL ). It does this for every stock that is configured/watched by the application.
Running the application now and pressing the “Show Popup Now” will get and show real data ( make sure you have some stocks added to your ‘watched list’ ), notice when pressing “Show Popup Now” there will be a small delay before the popup shows whilst the application contacts the IEX API, this is normal.
Note: I added a few more stocks to see how it would behave with many stocks:
Adding Periodic Popups
Now that manual popups are enabled via “Show Popup Now”, the application is required to popup automatically every X seconds, we can do this by utilizing a Timer.
Within Form1.cs add a class variable of type System.Timer.Timers
...
public partial class Form1 : Form
{
private System.Timers.Timer popupTimer;
...
Then initiate that variable within the Form Load event, alter the Form1_Load event handler to the below, and also add the PopupTimer_Elapsed method:
private void Form1_Load(object sender, EventArgs e)
{
LoadSettingsFromFile();
popupTimer = new System.Timers.Timer();
popupTimer.Interval = settings.popupFrequencySeconds * 1000;
popupTimer.Elapsed += PopupTimer_Elapsed;
popupTimer.Start();
AttachListeners();
}
private void PopupTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
ShowStockPrices();
}
Lastly, we need the timer interval frequency to adjust whenever the config settings are altered, adjust the popupFreqNUD_ValueChanged method to change the timer interval:
private void popupFreqNUD_ValueChanged(object sender, EventArgs e)
{
settings.popupFrequencySeconds = Convert.ToInt32(popupFreqNUD.Value);
SaveSettingsToFile();
popupTimer.Interval = settings.popupFrequencySeconds * 1000;
}
In theory this works, but I discovered that the popup doesnt show on the execution of the timer, or if it did, it was displaying strangely e.g. :
I believe this happens because the Timer which fires the event PopupTimer_Elapsed fires the event ( and therefore executing code within it ) on a non-UI Thread, instead it looks to be firing on a worker thread. Within Winforms in order to get UI forms to redraw/display correctly, the UI element ( in this case the DynamicPopupForm ) has to have affinity with the main UI thread ( of which there is only one ). In order to have the DynamicPopupForm render correctly, it needs to be attached to the main UI thread, to do this we use Invoke along with MethodInvoker.
https://msdn.microsoft.com/en-us/library/zyzhdc6b(v=vs.110).aspx
https://msdn.microsoft.com/en-us/library/system.windows.forms.methodinvoker(v=vs.110).aspx
Fix:
From within the ShowStockPrices method, alter the body to Invoke the creation and showing of the DynamicPopupForm – this will attach the call to the DynamicPopupForm.Show() to the main UI thread which will allow the popup to render correctly:
private void ShowStockPrices()
{
var data = GetStockPrices(settings);
Invoke(new MethodInvoker(() =>
{
DynamicPopupForm dpop = new DynamicPopupForm(data, 5);
dpop.Show();
}));
}
Now if we run our application, the DynamicPopupForm should display and render correctly, and disappear after 5 seconds ( dependant upon configuration you chose )
Showing only during market operational hours
There is a requirement for the popup to only show during operational market hours. The tick box/settings data values are already within the saved ConfigSettingsObject , the application needs to use the boolean value onlyPopupDuringTradingHours to check if the popup should be displayed at all.
There are a few different ways we can get if the American stock market ( NYSE NASDAQ ) if open or not ( possibly via different API calls to IEX ), I have chosen to look at the data returned by the query to IEX. I found the following returned value within the IEX data which is returned when one stock is queried:
"latestSource": "Close"
Having a look at the documentation ( https://iextrading.com/developer/docs/#quote ) it looks like the following values are possible:
“IEX real time price”
“15 minute delayed price”
“Close”
“Previous close”
Therefore, if the value returned by IEX is “Close” or “Previous close” the market is closed and so the application should not show the entries.
This has it’s advantages and disadvantages. It allows the popup to only show stocks when the exchange is open on a stock-by-stock basis, which is good – should IEX start supporting other markets. But it also means that the application will poll/query IEX even if the stock exchange is closed – for every stock, which is mostly an unnecessary overhead, an overhead I was willing to accept for this project.
Firstly we need to alter the StockListDataObject.cs class to include a new variable which records if the stock market is open or not, adjust it so that it looks like this:
public class StockListObjectData
{
public string StockName { get; set; }
public decimal PercentageChange { get; set; }
public bool StockStockMarketOpen { get; set; }
}
Next, we change the IEXHelper.cs file and alter the return object for GetPercentageForStock from a decimal to a StockListObjectData
The full method for GetPercentageForStock within IEXHelper.cs should now look like this:
public static StockListObjectData GetPercentageForStock(string symbol)
{
StockListObjectData toReturn = new StockListObjectData();
toReturn.PercentageChange = -1;
toReturn.StockName = symbol;
toReturn.StockStockMarketOpen = false;
// Build query to call against API:
string paramaters = $"stock/{symbol}/quote?displayPercent=true";
string objectAsString = APICall(paramaters);
var data = (JObject)JsonConvert.DeserializeObject(objectAsString);
toReturn.PercentageChange = data["changePercent"].Value<Decimal>();
string closedValueString = data["latestSource"].Value<String>();
if (closedValueString.ToLower().Contains("Close"))
{
toReturn.StockStockMarketOpen = false;
}
else
{
toReturn.StockStockMarketOpen = true;
}
return toReturn;
}
Notice that this method no longer just gets the percentage for the stock, it actually does more than this, so I decided to rename the method to GetStockData within IEXHelper.cs :
public static StockListObjectData GetStockData(string symbol)
{
....
Note: If you are using a test project IEXHelperTests like me, you’ll have to rename the method call in the tests too:
[TestMethod()]
public void GetPercentageForStockTestValidSymbol()
{
decimal percentage = IEXHelper.GetStockData("AAPL").PercentageChange;
Assert.IsNotNull(percentage);
}
Altering the method return will break the application/show compilation error within VS, the next step is to fix this to that it accepts the new object type, and also account for the StockStockMarketClosed new property.
Alter the GetStockPrices method within Form1.cs to check for stock market being open for a stock if the settings deem it to do this check:
private List<StockListObjectData> GetStockPrices(ConfigSettingsObject settings)
{
List<StockListObjectData> toReturn = new List<StockListObjectData>();
foreach(var stock in settings.watchedStock)
{
StockListObjectData newData = IEXHelper.GetStockData(stock);
if(settings.onlyPopupDuringTradingHours == true)
{
if(newData.StockStockMarketOpen == true)
{
toReturn.Add(newData);
}
}
else
{
toReturn.Add(newData);
}
}
return toReturn;
}
Lastly, alter the ShowStockPrices so that it does not attempt to show the dialog if no stocks are returned:
private void ShowStockPrices()
{
var data = GetStockPrices(settings);
if (data.Count() >= 1)
{
Invoke(new MethodInvoker(() =>
{
DynamicPopupForm dpop = new DynamicPopupForm(data, 5);
dpop.Show();
}));
}
}
You should now have a fully working Stock Market Popup Ticker application will periodically show the popup dialog in the bottom right hand corner. The major development of the application is now complete, and you have a working product. The next section discusses how to set the application up with an installer.
Installer
As part of the requirements I wanted the application to have an installer ( for easy distribution/sharing ).
I chose to use Wix for my project installer, mostly because I have used it before at work and I know that it is very captable. There are plenty of articles on stack overflow debating which installer project type is best, I feel that the consensus is that Wix is one of the more complete installer products, even though it may be tricky to use for a first timer.
Firstly, you need to download and install the Wix toolset build tools: http://wixtoolset.org/releases/
I’m using the most current stable version which is v3.11.1. When you click download, it will take you to a github page, download the one that is an executable as shown below:
Once the executable is downloaded, run it, and the WiX installer GUI will show, just press install:
It is also necessary to install the Wix Toolset Visual Studio Extension 2017 : https://marketplace.visualstudio.com/items?itemName=RobMensching.WixToolsetVisualStudio2017Extension
Download and install that extension like any other VS extension.
( Remember to restart VS after these installations )
Add a new Setup Project for WiX v3 to your solution, call it StockMarketPopupTickerSetup
You’ll be presented with the Product.wxs file. This is the setup configuration file ( no GUI available for WiX ) and it instructs WiX what to include in the setup file and all of the configuration details within the setup file.
Now that the setup project is created, we need to give it reference to the StockMarketPopupTickerApplication project. Add a reference to the StockMarketPopupTickerApplication project from the StockMarketPopupTickerSetup project:
Alteration/configuration of the WiX wxs is a complicated topic – there is quite a lot of learning to be done in regards to WiX in order to begin/start using it. This is a sentiment that is echoed in other places on the internet too, although WiX is very powerful, it has a semi-steep learning curve compared to other setup/installer products.
If you would like to know more about the WiX setup, I recommend that you read the ( quite lengthy ) ‘Wix Tutorial’ tutorial from the FireGiant website: https://www.firegiant.com/wix/tutorial/getting-started/
Before we alter the WiX configuration, change the icon file that is attached to the project StockMarketPopupTicker to be copied into the output directory, set it to copy if newer , the WiX installer will need access to this icon file in order to build the installer file and create shortcuts etc with the correct application icon.
Moving on to setting up the WiX configuration file:
I’m going to omit explaining every line/configuration section within the WiX installer from this post in detail ( I may do this in another post at a later date). This is the resulting Product.wxs file for this product:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi' xmlns:util="http://schemas.microsoft.com/wix/UtilExtension" >
<Product
Id="FB578C47-0B05-4D95-8925-AF5D61B4BBCD"
Name="Stock Market Popup Ticker"
Language="1033" Version="1.0.0"
Manufacturer="Nick Saxelby"
UpgradeCode="6D539E30-821C-4865-9498-E459A35311E0">
<Package Id="*" InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
<!-- Credit for this part: https://stackoverflow.com/questions/29919701/how-to-remove-license-dialog-from-the-wix-installer-built-in-ui-wixui-minimal -->
<UI Id="WixUI_MinimalX">
<TextStyle Id="WixUI_Font_Normal" FaceName="Tahoma" Size="8" />
<TextStyle Id="WixUI_Font_Bigger" FaceName="Tahoma" Size="12" />
<TextStyle Id="WixUI_Font_Title" FaceName="Tahoma" Size="9" Bold="yes" />
<Property Id="DefaultUIFont" Value="WixUI_Font_Normal" />
<Property Id="WixUI_Mode" Value="Minimal" />
<DialogRef Id="ErrorDlg" />
<DialogRef Id="FatalError" />
<DialogRef Id="FilesInUse" />
<DialogRef Id="MsiRMFilesInUse" />
<DialogRef Id="PrepareDlg" />
<DialogRef Id="ProgressDlg" />
<DialogRef Id="ResumeDlg" />
<DialogRef Id="UserExit" />
<DialogRef Id="WelcomeDlg" />
<Publish Dialog="ExitDialog" Control="Finish" Event="EndDialog" Value="Return" Order="999">1</Publish>
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg">1</Publish>
<Publish Dialog="MaintenanceWelcomeDlg" Control="Next" Event="NewDialog" Value="MaintenanceTypeDlg">1</Publish>
<Publish Dialog="MaintenanceTypeDlg" Control="RepairButton" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
<Publish Dialog="MaintenanceTypeDlg" Control="RemoveButton" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
<Publish Dialog="MaintenanceTypeDlg" Control="Back" Event="NewDialog" Value="MaintenanceWelcomeDlg">1</Publish>
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" Order="2">1</Publish>
<Property Id="ARPNOMODIFY" Value="1" />
</UI>
<UIRef Id="WixUI_Common" />
<Icon Id="icon.ico" SourceFile="$(var.StockMarketPopupTickerApplication.TargetDir)\if_stock-market_87472.ico"/>
<Property Id="ARPPRODUCTICON" Value="icon.ico" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate EmbedCab="yes"/>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="APPLICATIONROOTDIRECTORY" Name="Stock Market Popup Ticker" />
</Directory>
<Directory Id="ProgramMenuFolder">
<Directory Id="ApplicationProgramsFolder" Name="Stock Market Popup Ticker"/>
</Directory>
<Directory Id="StartupFolder" >
</Directory>
</Directory>
<DirectoryRef Id="APPLICATIONROOTDIRECTORY">
<Component Id="MainExecutableFiles" Guid="A39FB1A6-72D0-4E90-97A4-93972F30E689">
<File Id="ExecutableFile" Name="StockMarketPopupTickerApplication.exe" Source="$(var.StockMarketPopupTickerApplication.TargetPath)" KeyPath="yes" Vital="yes" />
<File Id="SettingsFile" Name="settingsConfig.xml" Source="$(var.StockMarketPopupTickerApplication.TargetDir)\settingsConfig.xml" Vital="yes">
<util:PermissionEx User="Users" GenericAll="yes"/>
</File>
<File Id="NewtonsoftJSON" Name="Newtonsoft.Json.dll" Source="$(var.StockMarketPopupTickerApplication.TargetDir)\Newtonsoft.Json.dll" Vital="yes" />
<File Id="NewtonsoftXML" Name="Newtonsoft.Json.xml" Source="$(var.StockMarketPopupTickerApplication.TargetDir)\Newtonsoft.Json.xml" Vital="yes" />
<RemoveFolder Id="APPLICATIONROOTDIRECTORY" On="uninstall"/>
</Component>
</DirectoryRef>
<DirectoryRef Id="ApplicationProgramsFolder">
<Component Id="ApplicationShortcut" Guid="09DE8EAE-3458-4BA7-A628-40977552E11B">
<Shortcut Id="ApplicationStartMenuShortcut"
Name="Stock Market Popup Ticker"
Description="Stock Market Popup Ticker"
Target="[#ExecutableFile]"
WorkingDirectory="APPLICATIONROOTDIRECTORY"
Icon="icon.ico"/>
<RemoveFolder Id="ApplicationProgramsFolder" On="uninstall"/>
<RegistryValue Root="HKCU" Key="Software\[Manufacturer]\[ProductName]" Name="installed" Type="integer" Value="1" KeyPath="yes"/>
</Component>
</DirectoryRef>
<DirectoryRef Id="StartupFolder">
<Component Id="StartUpShortcut" Guid="1E8F6723-79EC-4D98-B553-533BFD50D763">
<Shortcut Id="ApplicationStartUpShortcut"
Name="Stock Market Popup Ticker"
Description="Stock Market Popup Ticker"
Target="[#ExecutableFile]"
WorkingDirectory="APPLICATIONROOTDIRECTORY"
Icon="icon.ico"/>
<RemoveFolder Id="StartupFolder" On="uninstall"/>
<RegistryValue Root="HKCU" Key="Software\[Manufacturer]\[ProductName]" Name="ShortCutStartUp" Type="integer" Value="1" KeyPath="yes"/>
</Component>
</DirectoryRef>
<Feature Id="Complete" Title="Stock Market Popup Ticker Setup" Level="1">
<ComponentRef Id="MainExecutableFiles" />
<ComponentRef Id="ApplicationShortcut" />
<ComponentRef Id="StartUpShortcut" />
</Feature>
</Product>
</Wix>
Note: Everywhere within this configuration file where it uses ‘Guid=’ followed by a GUID; has been generated by me using any online GUID generation tool ( example : https://www.guidgenerator.com/online-guid-generator.aspx ). If you are using this post to make your own product, it is important not to copy these GUID values exactly. These GUID’s are Globally unique identifiers, and so if you try to install application A which has the same GUID as application B, there will be problems. Please re-generate these GUID values if you are using this post to make your own product.
Before building this setup file, we need to add a reference to an extension that is needed: PermissionEx , which is part of the WixUtilExtension.dll extension. To add a reference to this extension, within the WiX setup project add a reference and find the file at the location:
C:\Program Files (x86)\WiX Toolset v3.11\bin\WixUtilExtension.dll
Your insert reference dialogue will look like this ( if you are using VS 2017 ):
When you press ‘ok’, you may see the following error dialog pop up:
“Unable to cast transparent proxy to type ‘WixExtensionValidator’.”
I did a little bit of digging, and it seems that this is not as fatal an error as it may look.
If you press ‘Ok’, then unload/reload the WiX setup application ( or just restart visual studio ). When you re-visit the References section within the WiX setup project, you’ll see it has added it:
Note: if you don’t get this error, then lucky you, you should see the below:
You need to add one more reference to a Wix extension ( same process as above ), we need the WiXUIExtension to allow our .msi to have a friendly GUI on install
Add the following dll to references:
C:\Program Files (x86)\WiX Toolset v3.11\bin\WixUIExtension.dll
Note: again, same instructions as before if you see an error message pop up.
So now you should have three references within the Setup project.
You should now be able to build the setup project, browse to the output directory, and you should see a StockMarketPopupTickerSetup.msi file which is the installer file. Give the installer a go and you’ll be guided though an installer :
What does this WiX setup file do:
- Installs required executable files within the Program Files folder
- Creates a shortcut to the application within the start menu
- Creates a shortcut to the application within the windows StartUp folder – which allows the application to run on startup
- Creates permissions to allow users to alter the settingsConfig.xml file
Future improvements/Features:
- Within installer, provide a GUI which allows the user to click ‘Next’ ‘Next’ ‘Next’ etc
- Add more stock markets, at the moment, the API that I use only finds symbols within NYSE and NASDAQ, IEX responded to me saying that in the future it may be possible to select different exchanges
.NETC#
Stock market desktop ticker .NET application
July 25, 2018
Programming
No Comments
Nick
Quick Links:
Requires .net framework 4.5 or greater
Summary:
I wanted to see how my stocks are doing periodically at a glance on my windows desktop computer throughout the day. I want a small popup that appears in the bottom right hand corner of the screen semi-frequently, which will show me the daily percentage increase/decrease of the stock.
Basic Requirements:
Behavior:
There should be a small popup in the bottom right hand corner of the screen every X seconds ( seconds are configurable ).
The small popup should list all of the stocks configured that the application is ‘watching’ and display the daily gain/loss in a percentage format.
The small popup should stay present for X seconds ( configurable ).
The list of stocks within the popup will be listed and the text will be white, the percentage gain/loss for the day will be green if positive, white if 0% and red if negative on the day.
The application can be configured to only popup within the US stock markets hours of operation ( taking into account that I live in the UK, also taking into account daylight savings time etc if need be ).
It is possible to ‘force’ the pop-up to show on an off-frequency ( upon a user request to show the popup ).
The popup will have a small X to close the popup forcefully in the top right hand corner.
The background of the pop-up will be black
UI Design template – what it could look like:
( edit/update, the above screenshot changed from the actual implementation, the main difference being the market the stock is listed on e.g. NYSE was omitted from the final implementation )
Environment/Setup:
The application should come with an installer.
The application should not use a database.
The application should reside in the task tray with a small icon.
The application should open a configuration menu when the task tray icon is double clicked.
The application should open a small pop-up menu within the task tray when the icon is right clicked the menu will have : [Open Config]; [Show Prices Now]; [Exit Application] as the menu items.
Application must start on startup of windows ( preferably via start-up folder shortcut )
Configuration:
The user will be able to add stock to watch within the config ( list ).
The stock to watch will be the official ticker abbreviation from either NASDAQ or NYSE ( only ).
The user can configure the frequency of the pop-up.
The user can configure the duration of the pop-up.
Implementation/Tutorial:
Requirements:
General solution creation:
To start with, create a standard windows forms application ( Visual C# -> Windows Desktop -> Windows Forms App (.NET Framework ) in visual studio. Set the Framework version to target anything higher ( or equal to ) .NET Framework 4.5 ( I used 4.5 ). Call the project StockMarketPopupTickerApplication .
The default form Form1.cs which is shown when the project is created will be our the ‘config page’ of our application, so when a user double clicks the task tray icon, the config page will show. Closing the config page will not close the application.
Setting Default Properties of Form1.cs
We need to enable this form to minimise to task tray first at load, within Form1.cs change the WindowState property to default to Minimized .
Adding a task tray functionality
We want the application to largely reside within the task tray ( bottom right hand corner of the screen ) so we need to find a suitable icon, and attach it to the application. I used https://www.iconfinder.com/ to find a suitable icon and I found this one:
At the following address: https://www.iconfinder.com/icons/87472/market_stock_icon
Download the file as a ICO file
Once you have downloaded your .ICO icon file, we need to attach it to the application. We will attach it to Form1.cs via the designer by adding a NotifyIcon within the designer toolbox:
Dragging and dropping the NotifyIcon into the designer for Form1.cs adds the component to the application.
Once the NotifyIcon has been added to the designer, select the notifyIcon1 and attach the recently downloaded Icon file, you do this by clicking on the notifyIcon1 at the bottom of the Form1.csForm1.cs Designer window, then within the properties panel on the right press the ‘…’ button on the Icon property, and browse to the file you recently downloaded:
We also need to include the icon file into our project for later use. Right click the StockMarketPopupTickerApplication project ( project, not the solution ) and select Add -> Existing item.
Within the browse dialog box we need to find and select our icon file ( the one that was just downloaded ), remember to change the File filters within the browse dialog box, by default it will not show .ico files.
You’ll see that the icon file you have downloaded has been added into the project files appears in the list:
This stage is important, at later on within the installer section, we will need this icon file attached to the project.
Change the icon text within the notifyIcon1 properties panel to something better than “notifyIcon1” change it to “Stock market watcher” or something like that. This Text is what is shown when you hover over the icon within the task bar, so name it something human readable.
We also need to setup the context menu – which is the menu that will appear when the task tray icon is clicked in the bottom right hand corner, we need it to show the options: [Open Config]; [Show Prices Now]; [Exit Application]. Do this by adding a ContextMenuStrip to the Form1.cs just like you did the NotifyIcon ( drag and drop onto the designer ):
Once added, select the contextMenuStrip1 and from within the properties window, find and edit the “Items” property by clicking the “…” button:
A new configuration window will popup, this allows you to add the Menu Items to your context menu. We need three, so add three MenuItem , give them decent names, and also give them a meaningful Text display:
We now need to attach events to each of these Menu items, this can be accomplished by code, or by using some of the designer. I chose to use the designer. If you click on the contextMenuStrip1 at the bottom of the Form1.cs designer, a preview of the ContextMenuStrip will show within the designer:
You will see the context menu appear on the main form
If you now single click on each of your menu items, the properties panel will show the properties of each of the MenuItems , here is where we can add OnClick events:
Select the events properties tab ( the lightning arrow within the properties window ) and double click in the white space next to the “Click” event. This will create an empty placeholder handler of the button being pressed. Something we will handle in the following section.
View within Form1.cs
View within properties designer showing event binding:
Repeat this process for each of the three MenuItem buttons
Which should result in empty code handlers which look like this: ( dependant upon what you called your Menu Items ):
Finally, we need to attach the context menu that we have just built to the notifyIcon1 . Click on the notifyIcon1 within the designer.
And bind the ContextMenuStrip to the notifyIcon1:
If you run your application now, you’ll see your application begin minimised within the system tray, with an icon, which when right clicked will display the three menu items you configured earlier:
Unfortunately, within the task bar, an application icon also appears, which is what we do not want:
To get rid of this change ShowInTaskBar to False for Form1.cs within the designer:
You can also see I have added an Application Icon ( not to be confused with notification icon ) – which I used the same icon as before ( mentioned previously ). I also changed MaximizeBox and MinimizeBox to False , this is so that the settings configuration window doesn’t have a minimise/maximise buttons, which are not needed, I wanted my config window to be a static size.
You can also change the config forms default title to something more appropriate, within Form1 properties ( from the designer) set the Text field:
Which should make the designer title look like this:
If you run your application now, you will see that it has no task bar icon. It has a task tray icon that when right clicked shows three menu items. The application doesn’t do too much after this. We need to be able to restore at least the config window, so we need to hook up the “Open Config” menu item, and also the double click on system tray icon, which should maximise the config window so that we could change settings ( which we are yet to add ).
Adding listener actions to task tray icon
Firstly we can restore the configuration window to a maximized state by attaching a listener to the notify icon double click event.
Attach the event by double clicking the white space within the events list of notifyIcon1:
Within the double click handler, alter the method so that it looks like this:
Add a listener to the window state changing ( we don’t want it to show in task bar unless the control config box is open ).
Add a SizeChanged event handler ( same process as before )
Form Size changed event:
Alter the handler code so that it looks like this:
Finally we need to override the Form1.cs closing event:
We want the application to remin open ( in the task tray ) even when the application configuration dialog box is closed via pressing ‘X’. The only way that we want the application to fully close is from the task tray icon being right clicked, and choosing ‘Exit’. To handle this we use a class variable for the application to signify when the ‘Exit’ menu item selection has been selected, all other ways of closing the main settings form will not exit the application.
Add the class variable to Form1:
Now from within the FormClosing handler for Form1, alter it to this:
Now we should adjust the task tray ‘exit’ button handler to set the closeFromNotiifcaionTray to true:
If you build and run the application now, you will see that the following functionality is available:
We can now hook up one of the other task tray icon buttons ‘Open Config’, add the following code tot he openConfigStripMenu on Click event ( or whatever you called your ‘Open’ button ):
The Show Prices Menu item we will hook up later.
Config/Settings dialog
Ok, so now it’s time to add some config to the home dialog box which is form1.cs .
We will need to add three main controls/areas of functionality
I won’t explain the full procedure for creating this dialog box and arrangement of controls, I’ll assume you are comfortable in doing so, the target structure of the UI settings config form is show below:
Here is a table of the controls that I added to the desginer
After adding the controls, and fashioning them in a way which is to your liking, we need to resize the main Form control to be a bit more size appropriate, drag and resize the main form so it fits using the designer.
Then set the form size to be an exact size with no re-size ability within the Form properties:
Change FormBorderSize to FixedSingle
This will stop the form from being re-sizeable at run time.
Also change both NumericUpDown controls to have a higher than default maximum. By default these NUD controls will go to a maximum of 100. Given that we are dealing in seconds, it should be a much higher value. ( One every hour would be 3600 seconds, so a max of 3600 seems appropriate for the popupFreqNUD ).
At the moment most of these controls have no events hooked up to them, this is intentional, we will hook these controls up to event handlers in the next section.
Settings Storage ( object for view ):
I want the storage configurations ( such as stocks watched ) to be saved into a persistent storage object so that on each application load, the user doesn’t have to re-configure the application. In order to do this, I firstly need to create a store config class object that I can easily serialise to disk. We will serialize a config object to a file on desk within the executing directory called settingsConfig.xml
Create the following class within your project:
This is the object we will store all the settings in and is to be populated on startup and upon each settings change.
Add the following object to the Form1 class:
Within Form1 create some serializer methods which allow you to save/read these config settings:
Create serializer and deserializer methods to serialize to disk.
Note: Upon adding this code, you’ll need to add using statement references for:
System.Xml , System.Xml.Serialization and System.IO
Within Form1 add a load settings info config method called LoadSettingsIntoConfigForm with the following method body:
Now create a method within Form1 to load the settings from from the .xml file we will store on disk
You’ll see that within this method there is reference to a method called SaveSettingsToFile() , we need to create this method now:
We now have the basic load/save settings functionality made, we finally need to add the LoadSettingsFromFile() into the Form1_Load
Within the Form1.cs designer double click the Load event, which will add the Form1_Load event handler to the code behind:
Alter the Form1_Load event to call the LoadSettingsFromFile() method:
You should now be able to build and run your application, which will result in a settingsConfig.xml file being created within the application executing directory ( ..\StockMarketPopupTickerApplication\StockMarketPopupTickerApplication\bin\Debug in my case ).
If you have a look at the XML file created ( open in notepad++ for example ), you’ll see the basic structure of the object with the default values I hard coded into the application:
You’ll see AAPL and TWTR ( Apple and Twitter ) in the XML. Now you can check that the application configuration page has these matching values within it. Run the application, and double click the task tray icon to show the settings dialog box to verify that the default values have been populated into the settings controls:
Now we need to attach change values to the boxes, – note, usually you can do this via the designer route ( by attaching an event to the properties of the NUD box within the Designer – properties window ), or you can manually code the events to be attached at runtime. In this example, we need to have them attached at runtime by code, and not within the form1.cs file, this is to stop an infinite loop.
Explanation: upon load of the form, an event is added to the NUD ( Numeric Up Down controls ) which fires upon change. Also upon load, we are going to populate the value of the NUD, so what will happen is the ‘changed event’ will fire upon loading the saved settings value, which is not strictly true, nothing has ‘changed; from the user, so we only want these events to fire after the NUD values have been loaded ( from saved values file ).
I have an AttachListeners method within Form1.cs , which is called after the LoadSettingsFromFile method is called. Create this method:
The three event method handlers will currently not exist, we will create them soon.
The functionality I want is to save the settings configured by the user to file on every configuration control change we do this by calling the previously created SaveSettingsToFile method that we created earlier.
We now need to add the three missing listeners, two NUD listeners, and one checkbox changed listener:
Finally call the AttachListeners() method from within the Form1_Load() method:
You can now test your application by running it, and making sure that the changes you make to the two NUD and Checkboxes save to disk.
Adding/removing stock controls
Now it’s time to hook up the add/remove stock to watch control and have the list show the stocks we are watching. We need an add, remove, and re-draw method.
The re-draw method is called every time the back-end object ( in settings ) changes.
Add an ‘on-Click’ event to the remove and add buttons ( I used the designer and properties events box for this ), no need to have these in the AttachListeners() setup method:
Then add the Referencing DrawListBasedOnStock():
Right now, if you run your application, the config page should be all hooked up, and any changes you make to the config, should be saved back to disk. To test this, run the application, change some values, close the application, then run it again. The values should be updated.
Example:
Building the code to get prices:
Third party service selection:
For the purposes of this project, after a couple of Google searches, it seemed that a service called AlphaVantage was a suitable candidate to be able to get the stock prices from (https://www.alphavantage.co/) however after I had a play with this API, it was not clear ( or possible ) to obtain the data I wanted – I do not believe that it provides an intra-day percentage from the market opening. All I could find was an intra-day range of 60 minutes, and not the whole day. Having dug around a little bit into the responses that the API returned, I saw that within the AlphaAdvantage response data that it actually used a provider called IEX, so I decided to take a look at the IEX exchange as a stock market price provider
https://iextrading.com/developer/
Reading through their API, it seems to allow me to query one stock and get the real time percentage change of a stock.
Update: As of June 1st 2019, the original iextrading API is no longer available, they have moved to iexcloud ( which is still free, you just need to sign up ):
https://iexcloud.io/
Some of the below may be out of date, but the changes have been included in the source repository within GIT.
Querying the IEX API programmatically:
Now that we have selected an ideal API to query from, we need to build some code which can query each stock and extract the percentage from the response.
Setup a static new class to help with the calls to the IEX API:
Note: NuGet package Newtonsoft.Json should be installed for this to work:
Create a new class IEXHelper.cs
IEX Helper Class for querying stock prices:
You will need to make sure that your project already has the System.Net.Http assembly already added into the project ( .net 4.5 by default includes this assembly reference ):
If not, you can easily add reference to the assembly:
Within IEXHelper.cs you will need to add references to the following namespaces within this file:
Test project to test IEXHelper class:
I also quickly created a test project ( new Project -> Visual C# -> Test -> Unit Test Project ) to test these methods whilst developing code ( this is optional ):
Now we have a helper class which allows us to get the data from the IEX API on what we want to show in the popup. Next it is time to build the pop-up dialog box and show the data.
Building the popup box
I will use the standard Windows Forms class here and build the basic shape within the Visual Studio designer ( although it won’t have all that many controls on it ). Most of the form will be dynamic – listing X amount of stocks, so the size/controls/content will all be added dynamically upon loading the form.
Firstly, I create a new form ( right click project -> Add -> Windows Form ) called PopupForm
The size on the designer will be big by default, but this does not matter as it will be dynamically re-sized to fit the contents. We do however need to set the background of the Form to black and remove the border:
We also need the popup box to not show within the task bar ( by default an application icon will appear within the active windows task bar, which is not what Iwant ):
That is all that is needed to be changed from the Designer side of the form, the rest will be handled within code at run time.
Dynamic insertion of data into popup box
Now that we have a base popup box built ( albeit a very simple form, with no border, and a Black background ) we are not going to actually use this Form directly. We are going to create a new Class called DynamicPopupForm which will derive from the PopupForm and override some key methods ( such as the startup method ) so that we can do some dynamic display.
Create a new class DynamicPopupForm.cs ( right click project, Add New – Class ) which should look like this:
You will see that constructor of the DynamicPopupForm form has an List<> of StockListObjectData passed into it. This is a ‘model’ class that I have created in a separate file StockListObjectData.cs , create this new file, and make it look like this :
This Class is essentially our model which we are passing through to the view ( the DynamicPopupForm )
What the constructor is doing is showing the form ( it will be blank ), then after X amount of seconds, automatically closing the form.
Now re-visit the DynamicPopupForm.cs and add a using namespace reference to System.Timers :
We can test this newly created DynamicPopupForm by hooking up the showPricesNowMenuItem_Click within Form1.cs to call a new DynamicPopupForm
Within Form1.cs , hook up the show prices menu item event to a new method called ShowStockPrices :
You can see that the ShowStockPrices() currently builds a test set of StockListObjectData which is then passed through to the DynamicPopupForm constructor.
Now if you run your application and right click the icon in the taskbar, you can click the “Show Prices Now” menu item, you’ll see that the DynamicPopupForm ( all black ) shows for 5 seconds, then disappears – nothing special yet… The data is not used at the moment, the next section will use this test data.
Dynamic Layout
Now that the general control/show of the DynamicPopupForm is built, we need to add data to it add code to dynamically resize the form based on inputs.
I used the following method to build a layout to the form ( which is placed within DynamicPopupForm.cs ):
Note: You’ll need to add using reference to System.Drawing :
Most of this code is just dynamic layout code which adds labels to the form in an orderly fashion. It returns a Size object so that the application knows how big the parent control should be. Notice that we set green/red fore colour of the Label depending upon if the stock is positive/negative for the day.
To call this, adjust the constructor of the DynamicPopupForm to the below:
By returning the Size of the controls which are built by the BuildStockListUI method, it allows the parent form ( DynamicPopupForm ) to be resized to fit the content, which is adjusted by setting the form ClientSize property. I have added an extra 10 pixels to the width, and 4 pixels for padding on all sides.
If you now run the application, and press the ‘Show Stocks Now’ button, you’ll see a popup with our test data, which will seemly appear in the ‘middle’ of your screen.
The popup is shown correctly with the test data, and it is of appropriate size, but the positioning is wrong, I want it to appear in a discrete location on the screen ( bottom right hand corner ).
The following code allows us to do this:
Put this within the constructor within DynamicPopupForm.cs : ( full constructor code: )
Note: You’ll also need to add a namespace reference to System.Windows.Forms :
Note2: I also added this.TopMost to make sure that the popup is always on top. If I clicked somewhere in a web browser that was full screen, the popup would disappear, which is not what I wanted. So Setting TopMost to true makes sure it is always on top of other windows.
Lastly we need to add a small cross in the top right hand corner of the popup in order to be able to forcefully close the popup should we need to. Add the following code to the DynamicPopupForm class
We hook up the close label to a new event which firstly stops the timer from firing ( timer.Stop() ), then closes the DynamicPopupForm . Note: it is important to stop the timer first so that the application does not exception.
We call this AddCloseButton from within the constructor after the dynamic building of the stock ( BuildStockListUI ) has taken place:
Alter the DynamicPopupForm constructor to call the AddCloseButton :
If you run the application now, and call the “Show Popup Now” it should display the test data that we input earlier in the ShowStockPrices() method. The popup should appear in the bottom right hand corner of your screen, and should look like:
Working with Real Data
Next we can build the application to use real data from IEX rather than the fake data we put into ShowStockPrices() earlier. Alter the ShowStockPrices() method within Form1.cs and remove the fake data, and replace with the following:
This calls a new method GetStockPrices() which accepts a ConfigSettingsObject object, the new method GetStockPrices() looks like this:
This method will call the IEXHelper method GetPercentageForStock , passing in the stock ticker name ( e.g. AAPL ). It does this for every stock that is configured/watched by the application.
Running the application now and pressing the “Show Popup Now” will get and show real data ( make sure you have some stocks added to your ‘watched list’ ), notice when pressing “Show Popup Now” there will be a small delay before the popup shows whilst the application contacts the IEX API, this is normal.
Note: I added a few more stocks to see how it would behave with many stocks:
Adding Periodic Popups
Now that manual popups are enabled via “Show Popup Now”, the application is required to popup automatically every X seconds, we can do this by utilizing a Timer.
Within Form1.cs add a class variable of type System.Timer.Timers
Then initiate that variable within the Form Load event, alter the Form1_Load event handler to the below, and also add the PopupTimer_Elapsed method:
Lastly, we need the timer interval frequency to adjust whenever the config settings are altered, adjust the popupFreqNUD_ValueChanged method to change the timer interval:
In theory this works, but I discovered that the popup doesnt show on the execution of the timer, or if it did, it was displaying strangely e.g. :
I believe this happens because the Timer which fires the event PopupTimer_Elapsed fires the event ( and therefore executing code within it ) on a non-UI Thread, instead it looks to be firing on a worker thread. Within Winforms in order to get UI forms to redraw/display correctly, the UI element ( in this case the DynamicPopupForm ) has to have affinity with the main UI thread ( of which there is only one ). In order to have the DynamicPopupForm render correctly, it needs to be attached to the main UI thread, to do this we use Invoke along with MethodInvoker.
https://msdn.microsoft.com/en-us/library/zyzhdc6b(v=vs.110).aspx
https://msdn.microsoft.com/en-us/library/system.windows.forms.methodinvoker(v=vs.110).aspx
Fix:
From within the ShowStockPrices method, alter the body to Invoke the creation and showing of the DynamicPopupForm – this will attach the call to the DynamicPopupForm.Show() to the main UI thread which will allow the popup to render correctly:
Now if we run our application, the DynamicPopupForm should display and render correctly, and disappear after 5 seconds ( dependant upon configuration you chose )
Showing only during market operational hours
There is a requirement for the popup to only show during operational market hours. The tick box/settings data values are already within the saved ConfigSettingsObject , the application needs to use the boolean value onlyPopupDuringTradingHours to check if the popup should be displayed at all.
There are a few different ways we can get if the American stock market ( NYSE NASDAQ ) if open or not ( possibly via different API calls to IEX ), I have chosen to look at the data returned by the query to IEX. I found the following returned value within the IEX data which is returned when one stock is queried:
Having a look at the documentation ( https://iextrading.com/developer/docs/#quote ) it looks like the following values are possible:
“IEX real time price”
“15 minute delayed price”
“Close”
“Previous close”
Therefore, if the value returned by IEX is “Close” or “Previous close” the market is closed and so the application should not show the entries.
This has it’s advantages and disadvantages. It allows the popup to only show stocks when the exchange is open on a stock-by-stock basis, which is good – should IEX start supporting other markets. But it also means that the application will poll/query IEX even if the stock exchange is closed – for every stock, which is mostly an unnecessary overhead, an overhead I was willing to accept for this project.
Firstly we need to alter the StockListDataObject.cs class to include a new variable which records if the stock market is open or not, adjust it so that it looks like this:
Next, we change the IEXHelper.cs file and alter the return object for GetPercentageForStock from a decimal to a StockListObjectData
The full method for GetPercentageForStock within IEXHelper.cs should now look like this:
Notice that this method no longer just gets the percentage for the stock, it actually does more than this, so I decided to rename the method to GetStockData within IEXHelper.cs :
Note: If you are using a test project IEXHelperTests like me, you’ll have to rename the method call in the tests too:
Altering the method return will break the application/show compilation error within VS, the next step is to fix this to that it accepts the new object type, and also account for the StockStockMarketClosed new property.
Alter the GetStockPrices method within Form1.cs to check for stock market being open for a stock if the settings deem it to do this check:
Lastly, alter the ShowStockPrices so that it does not attempt to show the dialog if no stocks are returned:
You should now have a fully working Stock Market Popup Ticker application will periodically show the popup dialog in the bottom right hand corner. The major development of the application is now complete, and you have a working product. The next section discusses how to set the application up with an installer.
Installer
As part of the requirements I wanted the application to have an installer ( for easy distribution/sharing ).
I chose to use Wix for my project installer, mostly because I have used it before at work and I know that it is very captable. There are plenty of articles on stack overflow debating which installer project type is best, I feel that the consensus is that Wix is one of the more complete installer products, even though it may be tricky to use for a first timer.
Firstly, you need to download and install the Wix toolset build tools: http://wixtoolset.org/releases/
I’m using the most current stable version which is v3.11.1. When you click download, it will take you to a github page, download the one that is an executable as shown below:
Once the executable is downloaded, run it, and the WiX installer GUI will show, just press install:
It is also necessary to install the Wix Toolset Visual Studio Extension 2017 : https://marketplace.visualstudio.com/items?itemName=RobMensching.WixToolsetVisualStudio2017Extension
Download and install that extension like any other VS extension.
( Remember to restart VS after these installations )
Add a new Setup Project for WiX v3 to your solution, call it StockMarketPopupTickerSetup
You’ll be presented with the Product.wxs file. This is the setup configuration file ( no GUI available for WiX ) and it instructs WiX what to include in the setup file and all of the configuration details within the setup file.
Now that the setup project is created, we need to give it reference to the StockMarketPopupTickerApplication project. Add a reference to the StockMarketPopupTickerApplication project from the StockMarketPopupTickerSetup project:
Alteration/configuration of the WiX wxs is a complicated topic – there is quite a lot of learning to be done in regards to WiX in order to begin/start using it. This is a sentiment that is echoed in other places on the internet too, although WiX is very powerful, it has a semi-steep learning curve compared to other setup/installer products.
If you would like to know more about the WiX setup, I recommend that you read the ( quite lengthy ) ‘Wix Tutorial’ tutorial from the FireGiant website: https://www.firegiant.com/wix/tutorial/getting-started/
Before we alter the WiX configuration, change the icon file that is attached to the project StockMarketPopupTicker to be copied into the output directory, set it to copy if newer , the WiX installer will need access to this icon file in order to build the installer file and create shortcuts etc with the correct application icon.
Moving on to setting up the WiX configuration file:
I’m going to omit explaining every line/configuration section within the WiX installer from this post in detail ( I may do this in another post at a later date). This is the resulting Product.wxs file for this product:
Note: Everywhere within this configuration file where it uses ‘Guid=’ followed by a GUID; has been generated by me using any online GUID generation tool ( example : https://www.guidgenerator.com/online-guid-generator.aspx ). If you are using this post to make your own product, it is important not to copy these GUID values exactly. These GUID’s are Globally unique identifiers, and so if you try to install application A which has the same GUID as application B, there will be problems. Please re-generate these GUID values if you are using this post to make your own product.
Before building this setup file, we need to add a reference to an extension that is needed: PermissionEx , which is part of the WixUtilExtension.dll extension. To add a reference to this extension, within the WiX setup project add a reference and find the file at the location:
Your insert reference dialogue will look like this ( if you are using VS 2017 ):
When you press ‘ok’, you may see the following error dialog pop up:
“Unable to cast transparent proxy to type ‘WixExtensionValidator’.”
I did a little bit of digging, and it seems that this is not as fatal an error as it may look.
If you press ‘Ok’, then unload/reload the WiX setup application ( or just restart visual studio ). When you re-visit the References section within the WiX setup project, you’ll see it has added it:
Note: if you don’t get this error, then lucky you, you should see the below:
You need to add one more reference to a Wix extension ( same process as above ), we need the WiXUIExtension to allow our .msi to have a friendly GUI on install
Add the following dll to references:
Note: again, same instructions as before if you see an error message pop up.
So now you should have three references within the Setup project.
You should now be able to build the setup project, browse to the output directory, and you should see a StockMarketPopupTickerSetup.msi file which is the installer file. Give the installer a go and you’ll be guided though an installer :
What does this WiX setup file do:
Future improvements/Features:
.NETC#