MDM166AA: Now showing local network connection and internet connection status.
Also having a cheap network signal animation.
2 using System.Collections.Generic;
3 using System.ComponentModel;
8 using System.Threading.Tasks;
9 using System.Windows.Forms;
11 using CodeProject.Dialog;
12 using System.Drawing.Imaging;
13 using System.ServiceModel;
14 using System.Threading;
15 using System.Diagnostics;
16 using System.Deployment.Application;
17 using System.Reflection;
19 using NAudio.CoreAudioApi;
20 using NAudio.CoreAudioApi.Interfaces;
21 using System.Runtime.InteropServices;
25 using SharpDisplayClient;
28 namespace SharpDisplayManager
31 public delegate uint ColorProcessingDelegate(int aX, int aY, uint aPixel);
32 public delegate int CoordinateTranslationDelegate(System.Drawing.Bitmap aBmp, int aInt);
33 //Delegates are used for our thread safe method
34 public delegate void AddClientDelegate(string aSessionId, ICallback aCallback);
35 public delegate void RemoveClientDelegate(string aSessionId);
36 public delegate void SetFieldDelegate(string SessionId, DataField aField);
37 public delegate void SetFieldsDelegate(string SessionId, System.Collections.Generic.IList<DataField> aFields);
38 public delegate void SetLayoutDelegate(string SessionId, TableLayout aLayout);
39 public delegate void SetClientNameDelegate(string aSessionId, string aName);
40 public delegate void PlainUpdateDelegate();
44 /// Our Display manager main form
46 public partial class MainForm : Form, IMMNotificationClient
48 DateTime LastTickTime;
50 System.Drawing.Bitmap iBmp;
51 bool iCreateBitmap; //Workaround render to bitmap issues when minimized
52 ServiceHost iServiceHost;
53 // Our collection of clients sorted by session id.
54 public Dictionary<string, ClientData> iClients;
55 // The name of the client which informations are currently displayed.
56 public string iCurrentClientSessionId;
57 ClientData iCurrentClientData;
60 //Function pointer for pixel color filtering
61 ColorProcessingDelegate iColorFx;
62 //Function pointer for pixel X coordinate intercept
63 CoordinateTranslationDelegate iScreenX;
64 //Function pointer for pixel Y coordinate intercept
65 CoordinateTranslationDelegate iScreenY;
67 private MMDeviceEnumerator iMultiMediaDeviceEnumerator;
68 private MMDevice iMultiMediaDevice;
70 private NetworkManager iNetworkManager;
74 /// Manage run when Windows startup option
76 private StartupManager iStartupManager;
81 private NotifyIconAdv iNotifyIcon;
85 iCurrentClientSessionId = "";
86 iCurrentClientData = null;
87 LastTickTime = DateTime.Now;
88 //Instantiate our display and register for events notifications
89 iDisplay = new Display();
90 iDisplay.OnOpened += OnDisplayOpened;
91 iDisplay.OnClosed += OnDisplayClosed;
93 iClients = new Dictionary<string, ClientData>();
94 iStartupManager = new StartupManager();
95 iNotifyIcon = new NotifyIconAdv();
97 //Have our designer initialize its controls
98 InitializeComponent();
100 //Populate device types
101 PopulateDeviceTypes();
103 //Initial status update
106 //We have a bug when drawing minimized and reusing our bitmap
107 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
108 iCreateBitmap = false;
110 //Minimize our window if desired
111 if (Properties.Settings.Default.StartMinimized)
113 WindowState = FormWindowState.Minimized;
121 /// <param name="sender"></param>
122 /// <param name="e"></param>
123 private void MainForm_Load(object sender, EventArgs e)
125 //Check if we are running a Click Once deployed application
126 if (ApplicationDeployment.IsNetworkDeployed)
128 //This is a proper Click Once installation, fetch and show our version number
129 this.Text += " - v" + ApplicationDeployment.CurrentDeployment.CurrentVersion;
133 //Not a proper Click Once installation, assuming development build then
134 this.Text += " - development";
138 iMultiMediaDeviceEnumerator = new MMDeviceEnumerator();
139 iMultiMediaDeviceEnumerator.RegisterEndpointNotificationCallback(this);
140 UpdateAudioDeviceAndMasterVolumeThreadSafe();
143 iNetworkManager = new NetworkManager();
144 iNetworkManager.OnConnectivityChanged += OnConnectivityChanged;
145 UpdateNetworkStatus();
147 //Setup notification icon
150 // To make sure start up with minimize to tray works
151 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
157 //When not debugging we want the screen to be empty until a client takes over
160 //When developing we want at least one client for testing
161 StartNewClient("abcdefghijklmnopqrst-0123456789","ABCDEFGHIJKLMNOPQRST-0123456789");
164 //Open display connection on start-up if needed
165 if (Properties.Settings.Default.DisplayConnectOnStartup)
167 OpenDisplayConnection();
170 //Start our server so that we can get client requests
175 /// Called when our display is opened.
177 /// <param name="aDisplay"></param>
178 private void OnDisplayOpened(Display aDisplay)
180 //Set our screen size now that our display is connected
181 //Our panelDisplay is the container of our tableLayoutPanel
182 //tableLayoutPanel will resize itself to fit the client size of our panelDisplay
183 //panelDisplay needs an extra 2 pixels for borders on each sides
184 //tableLayoutPanel will eventually be the exact size of our display
185 Size size = new Size(iDisplay.WidthInPixels() + 2, iDisplay.HeightInPixels() + 2);
186 panelDisplay.Size = size;
188 //Our display was just opened, update our UI
190 //Initiate asynchronous request
191 iDisplay.RequestFirmwareRevision();
194 UpdateMasterVolumeThreadSafe();
196 UpdateNetworkStatus();
199 //Testing icon in debug, no arm done if icon not supported
200 //iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconRecording, 0, 1);
201 //iDisplay.SetAllIconsStatus(2);
207 /// Called when our display is closed.
209 /// <param name="aDisplay"></param>
210 private void OnDisplayClosed(Display aDisplay)
212 //Our display was just closed, update our UI consequently
216 public void OnConnectivityChanged(NetworkManager aNetwork, NLM_CONNECTIVITY newConnectivity)
218 //Update network status
219 UpdateNetworkStatus();
223 /// Update our Network Status
225 private void UpdateNetworkStatus()
227 if (iDisplay.IsOpen())
229 iDisplay.SetIconOnOff(Display.TMiniDisplayIconType.EMiniDisplayIconInternet, iNetworkManager.NetworkListManager.IsConnectedToInternet);
230 iDisplay.SetIconOnOff(Display.TMiniDisplayIconType.EMiniDisplayIconNetworkSignal, iNetworkManager.NetworkListManager.IsConnected);
235 int iLastNetworkIconIndex = 0;
236 int iUpdateCountSinceLastNetworkAnimation = 0;
241 private void UpdateNetworkSignal(DateTime aLastTickTime, DateTime aNewTickTime)
243 iUpdateCountSinceLastNetworkAnimation++;
244 iUpdateCountSinceLastNetworkAnimation = iUpdateCountSinceLastNetworkAnimation % 4;
246 if (iDisplay.IsOpen() && iNetworkManager.NetworkListManager.IsConnected && iUpdateCountSinceLastNetworkAnimation==0)
248 int iconCount=iDisplay.IconCount(Display.TMiniDisplayIconType.EMiniDisplayIconNetworkSignal);
249 iLastNetworkIconIndex++;
250 iLastNetworkIconIndex = iLastNetworkIconIndex % (iconCount+1);
251 for (int i=0;i<iconCount;i++)
253 if (i < iLastNetworkIconIndex)
255 iDisplay.SetIconOn(Display.TMiniDisplayIconType.EMiniDisplayIconNetworkSignal,i);
259 iDisplay.SetIconOff(Display.TMiniDisplayIconType.EMiniDisplayIconNetworkSignal,i);
268 /// Receive volume change notification and reflect changes on our slider.
270 /// <param name="data"></param>
271 public void OnVolumeNotificationThreadSafe(AudioVolumeNotificationData data)
273 UpdateMasterVolumeThreadSafe();
277 /// Update master volume when user moves our slider.
279 /// <param name="sender"></param>
280 /// <param name="e"></param>
281 private void trackBarMasterVolume_Scroll(object sender, EventArgs e)
283 //Just like Windows Volume Mixer we unmute if the volume is adjusted
284 iMultiMediaDevice.AudioEndpointVolume.Mute = false;
285 //Set volume level according to our volume slider new position
286 iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar = trackBarMasterVolume.Value / 100.0f;
291 /// Mute check box changed.
293 /// <param name="sender"></param>
294 /// <param name="e"></param>
295 private void checkBoxMute_CheckedChanged(object sender, EventArgs e)
297 iMultiMediaDevice.AudioEndpointVolume.Mute = checkBoxMute.Checked;
301 /// Device State Changed
303 public void OnDeviceStateChanged([MarshalAs(UnmanagedType.LPWStr)] string deviceId, [MarshalAs(UnmanagedType.I4)] DeviceState newState){}
308 public void OnDeviceAdded([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId) { }
313 public void OnDeviceRemoved([MarshalAs(UnmanagedType.LPWStr)] string deviceId) { }
316 /// Default Device Changed
318 public void OnDefaultDeviceChanged(DataFlow flow, Role role, [MarshalAs(UnmanagedType.LPWStr)] string defaultDeviceId)
320 if (role == Role.Multimedia && flow == DataFlow.Render)
322 UpdateAudioDeviceAndMasterVolumeThreadSafe();
327 /// Property Value Changed
329 /// <param name="pwstrDeviceId"></param>
330 /// <param name="key"></param>
331 public void OnPropertyValueChanged([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId, PropertyKey key){}
337 /// Update master volume indicators based our current system states.
338 /// This typically includes volume levels and mute status.
340 private void UpdateMasterVolumeThreadSafe()
342 if (this.InvokeRequired)
344 //Not in the proper thread, invoke ourselves
345 PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateMasterVolumeThreadSafe);
346 this.Invoke(d, new object[] { });
350 //Update volume slider
351 float volumeLevelScalar = iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar;
352 trackBarMasterVolume.Value = Convert.ToInt32(volumeLevelScalar * 100);
353 //Update mute checkbox
354 checkBoxMute.Checked = iMultiMediaDevice.AudioEndpointVolume.Mute;
356 //If our display connection is open we need to update its icons
357 if (iDisplay.IsOpen())
359 //First take care our our volume level icons
360 int volumeIconCount = iDisplay.IconCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolume);
361 if (volumeIconCount > 0)
363 //Compute current volume level from system level and the number of segments in our display volume bar.
364 //That tells us how many segments in our volume bar needs to be turned on.
365 float currentVolume = volumeLevelScalar * volumeIconCount;
366 int segmentOnCount = Convert.ToInt32(currentVolume);
367 //Check if our segment count was rounded up, this will later be used for half brightness segment
368 bool roundedUp = segmentOnCount > currentVolume;
370 for (int i = 0; i < volumeIconCount; i++)
372 if (i < segmentOnCount)
374 //If we are dealing with our last segment and our segment count was rounded up then we will use half brightness.
375 if (i == segmentOnCount - 1 && roundedUp)
378 iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconVolume, i, (iDisplay.IconStatusCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolume) - 1)/2);
383 iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconVolume, i, iDisplay.IconStatusCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolume) - 1);
388 iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconVolume, i, 0);
393 //Take care our our mute icon
394 iDisplay.SetIconOnOff(Display.TMiniDisplayIconType.EMiniDisplayIconMute, iMultiMediaDevice.AudioEndpointVolume.Mute);
402 private void UpdateAudioDeviceAndMasterVolumeThreadSafe()
404 if (this.InvokeRequired)
406 //Not in the proper thread, invoke ourselves
407 PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateAudioDeviceAndMasterVolumeThreadSafe);
408 this.Invoke(d, new object[] { });
412 //We are in the correct thread just go ahead.
415 //Get our master volume
416 iMultiMediaDevice = iMultiMediaDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
418 labelDefaultAudioDevice.Text = iMultiMediaDevice.FriendlyName;
420 //Show our volume in our track bar
421 UpdateMasterVolumeThreadSafe();
423 //Register to get volume modifications
424 iMultiMediaDevice.AudioEndpointVolume.OnVolumeNotification += OnVolumeNotificationThreadSafe;
426 trackBarMasterVolume.Enabled = true;
430 Debug.WriteLine("Exception thrown in UpdateAudioDeviceAndMasterVolume");
431 Debug.WriteLine(ex.ToString());
432 //Something went wrong S/PDIF device ca throw exception I guess
433 trackBarMasterVolume.Enabled = false;
440 private void PopulateDeviceTypes()
442 int count = Display.TypeCount();
444 for (int i = 0; i < count; i++)
446 comboBoxDisplayType.Items.Add(Display.TypeName((Display.TMiniDisplayType)i));
453 private void SetupTrayIcon()
455 iNotifyIcon.Icon = GetIcon("vfd.ico");
456 iNotifyIcon.Text = "Sharp Display Manager";
457 iNotifyIcon.Visible = true;
459 //Double click toggles visibility - typically brings up the application
460 iNotifyIcon.DoubleClick += delegate(object obj, EventArgs args)
465 //Adding a context menu, useful to be able to exit the application
466 ContextMenu contextMenu = new ContextMenu();
467 //Context menu item to toggle visibility
468 MenuItem hideShowItem = new MenuItem("Hide/Show");
469 hideShowItem.Click += delegate(object obj, EventArgs args)
473 contextMenu.MenuItems.Add(hideShowItem);
475 //Context menu item separator
476 contextMenu.MenuItems.Add(new MenuItem("-"));
478 //Context menu exit item
479 MenuItem exitItem = new MenuItem("Exit");
480 exitItem.Click += delegate(object obj, EventArgs args)
484 contextMenu.MenuItems.Add(exitItem);
486 iNotifyIcon.ContextMenu = contextMenu;
490 /// Access icons from embedded resources.
492 /// <param name="name"></param>
493 /// <returns></returns>
494 public static Icon GetIcon(string name)
496 name = "SharpDisplayManager.Resources." + name;
499 Assembly.GetExecutingAssembly().GetManifestResourceNames();
500 for (int i = 0; i < names.Length; i++)
502 if (names[i].Replace('\\', '.') == name)
504 using (Stream stream = Assembly.GetExecutingAssembly().
505 GetManifestResourceStream(names[i]))
507 return new Icon(stream);
517 /// Set our current client.
518 /// This will take care of applying our client layout and set data fields.
520 /// <param name="aSessionId"></param>
521 void SetCurrentClient(string aSessionId)
523 if (aSessionId == iCurrentClientSessionId)
525 //Given client is already the current one.
526 //Don't bother changing anything then.
530 //Set current client ID.
531 iCurrentClientSessionId = aSessionId;
532 //Fetch and set current client data.
533 iCurrentClientData = iClients[aSessionId];
534 //Apply layout and set data fields.
535 UpdateTableLayoutPanel(iCurrentClientData);
538 private void buttonFont_Click(object sender, EventArgs e)
540 //fontDialog.ShowColor = true;
541 //fontDialog.ShowApply = true;
542 fontDialog.ShowEffects = true;
543 fontDialog.Font = cds.Font;
545 fontDialog.FixedPitchOnly = checkBoxFixedPitchFontOnly.Checked;
547 //fontDialog.ShowHelp = true;
549 //fontDlg.MaxSize = 40;
550 //fontDlg.MinSize = 22;
552 //fontDialog.Parent = this;
553 //fontDialog.StartPosition = FormStartPosition.CenterParent;
555 //DlgBox.ShowDialog(fontDialog);
557 //if (fontDialog.ShowDialog(this) != DialogResult.Cancel)
558 if (DlgBox.ShowDialog(fontDialog) != DialogResult.Cancel)
560 //Set the fonts to all our labels in our layout
561 foreach (Control ctrl in tableLayoutPanel.Controls)
563 if (ctrl is MarqueeLabel)
565 ((MarqueeLabel)ctrl).Font = fontDialog.Font;
570 cds.Font = fontDialog.Font;
571 Properties.Settings.Default.Save();
580 void CheckFontHeight()
582 //Show font height and width
583 labelFontHeight.Text = "Font height: " + cds.Font.Height;
584 float charWidth = IsFixedWidth(cds.Font);
585 if (charWidth == 0.0f)
587 labelFontWidth.Visible = false;
591 labelFontWidth.Visible = true;
592 labelFontWidth.Text = "Font width: " + charWidth;
595 MarqueeLabel label = null;
596 //Get the first label control we can find
597 foreach (Control ctrl in tableLayoutPanel.Controls)
599 if (ctrl is MarqueeLabel)
601 label = (MarqueeLabel)ctrl;
606 //Now check font height and show a warning if needed.
607 if (label != null && label.Font.Height > label.Height)
609 labelWarning.Text = "WARNING: Selected font is too height by " + (label.Font.Height - label.Height) + " pixels!";
610 labelWarning.Visible = true;
614 labelWarning.Visible = false;
619 private void buttonCapture_Click(object sender, EventArgs e)
621 System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height);
622 tableLayoutPanel.DrawToBitmap(bmp, tableLayoutPanel.ClientRectangle);
623 //Bitmap bmpToSave = new Bitmap(bmp);
624 bmp.Save("D:\\capture.png");
626 ((MarqueeLabel)tableLayoutPanel.Controls[0]).Text = "Captured";
629 string outputFileName = "d:\\capture.png";
630 using (MemoryStream memory = new MemoryStream())
632 using (FileStream fs = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
634 bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
635 byte[] bytes = memory.ToArray();
636 fs.Write(bytes, 0, bytes.Length);
643 private void CheckForRequestResults()
645 if (iDisplay.IsRequestPending())
647 switch (iDisplay.AttemptRequestCompletion())
649 case Display.TMiniDisplayRequest.EMiniDisplayRequestFirmwareRevision:
650 toolStripStatusLabelConnect.Text += " v" + iDisplay.FirmwareRevision();
651 //Issue next request then
652 iDisplay.RequestPowerSupplyStatus();
655 case Display.TMiniDisplayRequest.EMiniDisplayRequestPowerSupplyStatus:
656 if (iDisplay.PowerSupplyStatus())
658 toolStripStatusLabelPower.Text = "ON";
662 toolStripStatusLabelPower.Text = "OFF";
664 //Issue next request then
665 iDisplay.RequestDeviceId();
668 case Display.TMiniDisplayRequest.EMiniDisplayRequestDeviceId:
669 toolStripStatusLabelConnect.Text += " - " + iDisplay.DeviceId();
670 //No more request to issue
676 public static uint ColorWhiteIsOn(int aX, int aY, uint aPixel)
678 if ((aPixel & 0x00FFFFFF) == 0x00FFFFFF)
685 public static uint ColorUntouched(int aX, int aY, uint aPixel)
690 public static uint ColorInversed(int aX, int aY, uint aPixel)
695 public static uint ColorChessboard(int aX, int aY, uint aPixel)
697 if ((aX % 2 == 0) && (aY % 2 == 0))
701 else if ((aX % 2 != 0) && (aY % 2 != 0))
709 public static int ScreenReversedX(System.Drawing.Bitmap aBmp, int aX)
711 return aBmp.Width - aX - 1;
714 public int ScreenReversedY(System.Drawing.Bitmap aBmp, int aY)
716 return iBmp.Height - aY - 1;
719 public int ScreenX(System.Drawing.Bitmap aBmp, int aX)
724 public int ScreenY(System.Drawing.Bitmap aBmp, int aY)
730 /// Select proper pixel delegates according to our current settings.
732 private void SetupPixelDelegates()
734 //Select our pixel processing routine
735 if (cds.InverseColors)
737 //iColorFx = ColorChessboard;
738 iColorFx = ColorInversed;
742 iColorFx = ColorWhiteIsOn;
745 //Select proper coordinate translation functions
746 //We used delegate/function pointer to support reverse screen without doing an extra test on each pixels
747 if (cds.ReverseScreen)
749 iScreenX = ScreenReversedX;
750 iScreenY = ScreenReversedY;
760 //This is our timer tick responsible to perform our render
761 private void timer_Tick(object sender, EventArgs e)
763 //Update our animations
764 DateTime NewTickTime = DateTime.Now;
766 UpdateNetworkSignal(LastTickTime, NewTickTime);
768 //Update animation for all our marquees
769 foreach (Control ctrl in tableLayoutPanel.Controls)
771 if (ctrl is MarqueeLabel)
773 ((MarqueeLabel)ctrl).UpdateAnimation(LastTickTime, NewTickTime);
779 if (iDisplay.IsOpen())
781 CheckForRequestResults();
786 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
788 tableLayoutPanel.DrawToBitmap(iBmp, tableLayoutPanel.ClientRectangle);
789 //iBmp.Save("D:\\capture.png");
791 //Send it to our display
792 for (int i = 0; i < iBmp.Width; i++)
794 for (int j = 0; j < iBmp.Height; j++)
798 //Get our processed pixel coordinates
799 int x = iScreenX(iBmp, i);
800 int y = iScreenY(iBmp, j);
802 uint color = (uint)iBmp.GetPixel(i, j).ToArgb();
803 //Apply color effects
804 color = iColorFx(x,y,color);
806 iDisplay.SetPixel(x, y, color);
811 iDisplay.SwapBuffers();
815 //Compute instant FPS
816 toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " / " + (1000/timer.Interval).ToString() + " FPS";
818 LastTickTime = NewTickTime;
823 /// Attempt to establish connection with our display hardware.
825 private void OpenDisplayConnection()
827 CloseDisplayConnection();
829 if (!iDisplay.Open((Display.TMiniDisplayType)cds.DisplayType))
832 toolStripStatusLabelConnect.Text = "Connection error";
836 private void CloseDisplayConnection()
838 //Status will be updated upon receiving the closed event
842 private void buttonOpen_Click(object sender, EventArgs e)
844 OpenDisplayConnection();
847 private void buttonClose_Click(object sender, EventArgs e)
849 CloseDisplayConnection();
852 private void buttonClear_Click(object sender, EventArgs e)
855 iDisplay.SwapBuffers();
858 private void buttonFill_Click(object sender, EventArgs e)
861 iDisplay.SwapBuffers();
864 private void trackBarBrightness_Scroll(object sender, EventArgs e)
866 cds.Brightness = trackBarBrightness.Value;
867 Properties.Settings.Default.Save();
868 iDisplay.SetBrightness(trackBarBrightness.Value);
874 /// CDS stands for Current Display Settings
876 private DisplaySettings cds
880 DisplaysSettings settings = Properties.Settings.Default.DisplaysSettings;
881 if (settings == null)
883 settings = new DisplaysSettings();
885 Properties.Settings.Default.DisplaysSettings = settings;
888 //Make sure all our settings have been created
889 while (settings.Displays.Count <= Properties.Settings.Default.CurrentDisplayIndex)
891 settings.Displays.Add(new DisplaySettings());
894 DisplaySettings displaySettings = settings.Displays[Properties.Settings.Default.CurrentDisplayIndex];
895 return displaySettings;
900 /// Check if the given font has a fixed character pitch.
902 /// <param name="ft"></param>
903 /// <returns>0.0f if this is not a monospace font, otherwise returns the character width.</returns>
904 public float IsFixedWidth(Font ft)
906 Graphics g = CreateGraphics();
907 char[] charSizes = new char[] { 'i', 'a', 'Z', '%', '#', 'a', 'B', 'l', 'm', ',', '.' };
908 float charWidth = g.MeasureString("I", ft, Int32.MaxValue, StringFormat.GenericTypographic).Width;
910 bool fixedWidth = true;
912 foreach (char c in charSizes)
913 if (g.MeasureString(c.ToString(), ft, Int32.MaxValue, StringFormat.GenericTypographic).Width != charWidth)
925 /// Synchronize UI with settings
927 private void UpdateStatus()
930 checkBoxShowBorders.Checked = cds.ShowBorders;
931 tableLayoutPanel.CellBorderStyle = (cds.ShowBorders ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
933 //Set the proper font to each of our labels
934 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
936 ctrl.Font = cds.Font;
940 //Check if "run on Windows startup" is enabled
941 checkBoxAutoStart.Checked = iStartupManager.Startup;
943 checkBoxConnectOnStartup.Checked = Properties.Settings.Default.DisplayConnectOnStartup;
944 checkBoxMinimizeToTray.Checked = Properties.Settings.Default.MinimizeToTray;
945 checkBoxStartMinimized.Checked = Properties.Settings.Default.StartMinimized;
946 checkBoxReverseScreen.Checked = cds.ReverseScreen;
947 checkBoxInverseColors.Checked = cds.InverseColors;
948 checkBoxShowVolumeLabel.Checked = cds.ShowVolumeLabel;
949 checkBoxScaleToFit.Checked = cds.ScaleToFit;
950 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
951 labelMinFontSize.Enabled = cds.ScaleToFit;
952 maskedTextBoxMinFontSize.Text = cds.MinFontSize.ToString();
953 maskedTextBoxScrollingSpeed.Text = cds.ScrollingSpeedInPixelsPerSecond.ToString();
954 comboBoxDisplayType.SelectedIndex = cds.DisplayType;
955 timer.Interval = cds.TimerInterval;
956 maskedTextBoxTimerInterval.Text = cds.TimerInterval.ToString();
957 textBoxScrollLoopSeparator.Text = cds.Separator;
959 SetupPixelDelegates();
961 if (iDisplay.IsOpen())
963 //We have a display connection
964 //Reflect that in our UI
966 tableLayoutPanel.Enabled = true;
967 panelDisplay.Enabled = true;
969 //Only setup brightness if display is open
970 trackBarBrightness.Minimum = iDisplay.MinBrightness();
971 trackBarBrightness.Maximum = iDisplay.MaxBrightness();
972 if (cds.Brightness < iDisplay.MinBrightness() || cds.Brightness > iDisplay.MaxBrightness())
974 //Brightness out of range, this can occur when using auto-detect
975 //Use max brightness instead
976 trackBarBrightness.Value = iDisplay.MaxBrightness();
977 iDisplay.SetBrightness(iDisplay.MaxBrightness());
981 trackBarBrightness.Value = cds.Brightness;
982 iDisplay.SetBrightness(cds.Brightness);
985 //Try compute the steps to something that makes sense
986 trackBarBrightness.LargeChange = Math.Max(1, (iDisplay.MaxBrightness() - iDisplay.MinBrightness()) / 5);
987 trackBarBrightness.SmallChange = 1;
990 buttonFill.Enabled = true;
991 buttonClear.Enabled = true;
992 buttonOpen.Enabled = false;
993 buttonClose.Enabled = true;
994 trackBarBrightness.Enabled = true;
995 toolStripStatusLabelConnect.Text = "Connected - " + iDisplay.Vendor() + " - " + iDisplay.Product();
996 //+ " - " + iDisplay.SerialNumber();
998 if (iDisplay.SupportPowerOnOff())
1000 buttonPowerOn.Enabled = true;
1001 buttonPowerOff.Enabled = true;
1005 buttonPowerOn.Enabled = false;
1006 buttonPowerOff.Enabled = false;
1009 if (iDisplay.SupportClock())
1011 buttonShowClock.Enabled = true;
1012 buttonHideClock.Enabled = true;
1016 buttonShowClock.Enabled = false;
1017 buttonHideClock.Enabled = false;
1021 //Check if Volume Label is supported. To date only MDM166AA supports that crap :)
1022 checkBoxShowVolumeLabel.Enabled = iDisplay.IconCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolumeLabel)>0;
1024 if (cds.ShowVolumeLabel)
1026 iDisplay.SetIconOn(Display.TMiniDisplayIconType.EMiniDisplayIconVolumeLabel);
1030 iDisplay.SetIconOff(Display.TMiniDisplayIconType.EMiniDisplayIconVolumeLabel);
1035 //Display is connection not available
1036 //Reflect that in our UI
1037 checkBoxShowVolumeLabel.Enabled = false;
1038 tableLayoutPanel.Enabled = false;
1039 panelDisplay.Enabled = false;
1040 buttonFill.Enabled = false;
1041 buttonClear.Enabled = false;
1042 buttonOpen.Enabled = true;
1043 buttonClose.Enabled = false;
1044 trackBarBrightness.Enabled = false;
1045 buttonPowerOn.Enabled = false;
1046 buttonPowerOff.Enabled = false;
1047 buttonShowClock.Enabled = false;
1048 buttonHideClock.Enabled = false;
1049 toolStripStatusLabelConnect.Text = "Disconnected";
1050 toolStripStatusLabelPower.Text = "N/A";
1059 /// <param name="sender"></param>
1060 /// <param name="e"></param>
1061 private void checkBoxShowVolumeLabel_CheckedChanged(object sender, EventArgs e)
1063 cds.ShowVolumeLabel = checkBoxShowVolumeLabel.Checked;
1064 Properties.Settings.Default.Save();
1068 private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e)
1070 //Save our show borders setting
1071 tableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
1072 cds.ShowBorders = checkBoxShowBorders.Checked;
1073 Properties.Settings.Default.Save();
1077 private void checkBoxConnectOnStartup_CheckedChanged(object sender, EventArgs e)
1079 //Save our connect on startup setting
1080 Properties.Settings.Default.DisplayConnectOnStartup = checkBoxConnectOnStartup.Checked;
1081 Properties.Settings.Default.Save();
1084 private void checkBoxMinimizeToTray_CheckedChanged(object sender, EventArgs e)
1086 //Save our "Minimize to tray" setting
1087 Properties.Settings.Default.MinimizeToTray = checkBoxMinimizeToTray.Checked;
1088 Properties.Settings.Default.Save();
1092 private void checkBoxStartMinimized_CheckedChanged(object sender, EventArgs e)
1094 //Save our "Start minimized" setting
1095 Properties.Settings.Default.StartMinimized = checkBoxStartMinimized.Checked;
1096 Properties.Settings.Default.Save();
1099 private void checkBoxAutoStart_CheckedChanged(object sender, EventArgs e)
1101 iStartupManager.Startup = checkBoxAutoStart.Checked;
1105 private void checkBoxReverseScreen_CheckedChanged(object sender, EventArgs e)
1107 //Save our reverse screen setting
1108 cds.ReverseScreen = checkBoxReverseScreen.Checked;
1109 Properties.Settings.Default.Save();
1110 SetupPixelDelegates();
1113 private void checkBoxInverseColors_CheckedChanged(object sender, EventArgs e)
1115 //Save our inverse colors setting
1116 cds.InverseColors = checkBoxInverseColors.Checked;
1117 Properties.Settings.Default.Save();
1118 SetupPixelDelegates();
1121 private void checkBoxScaleToFit_CheckedChanged(object sender, EventArgs e)
1123 //Save our scale to fit setting
1124 cds.ScaleToFit = checkBoxScaleToFit.Checked;
1125 Properties.Settings.Default.Save();
1127 labelMinFontSize.Enabled = cds.ScaleToFit;
1128 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
1131 private void MainForm_Resize(object sender, EventArgs e)
1133 if (WindowState == FormWindowState.Minimized)
1136 //iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
1137 iCreateBitmap = true;
1141 private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
1143 iNetworkManager.Dispose();
1144 CloseDisplayConnection();
1146 e.Cancel = iClosing;
1149 public void StartServer()
1151 iServiceHost = new ServiceHost
1154 new Uri[] { new Uri("net.tcp://localhost:8001/") }
1157 iServiceHost.AddServiceEndpoint(typeof(IService), new NetTcpBinding(SecurityMode.None, true), "DisplayService");
1158 iServiceHost.Open();
1161 public void StopServer()
1163 if (iClients.Count > 0 && !iClosing)
1167 BroadcastCloseEvent();
1171 if (MessageBox.Show("Force exit?", "Waiting for clients...", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
1173 iClosing = false; //We make sure we force close if asked twice
1178 //We removed that as it often lags for some reason
1179 //iServiceHost.Close();
1183 public void BroadcastCloseEvent()
1185 Trace.TraceInformation("BroadcastCloseEvent - start");
1187 var inactiveClients = new List<string>();
1188 foreach (var client in iClients)
1190 //if (client.Key != eventData.ClientName)
1194 Trace.TraceInformation("BroadcastCloseEvent - " + client.Key);
1195 client.Value.Callback.OnCloseOrder(/*eventData*/);
1197 catch (Exception ex)
1199 inactiveClients.Add(client.Key);
1204 if (inactiveClients.Count > 0)
1206 foreach (var client in inactiveClients)
1208 iClients.Remove(client);
1209 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(client, false)[0]);
1213 if (iClients.Count==0)
1220 /// Just remove all our fields.
1222 private void ClearLayout()
1224 tableLayoutPanel.Controls.Clear();
1225 tableLayoutPanel.RowStyles.Clear();
1226 tableLayoutPanel.ColumnStyles.Clear();
1230 /// Just launch a demo client.
1232 private void StartNewClient(string aTopText = "", string aBottomText = "")
1234 Thread clientThread = new Thread(SharpDisplayClient.Program.MainWithParams);
1235 SharpDisplayClient.StartParams myParams = new SharpDisplayClient.StartParams(new Point(this.Right, this.Top),aTopText,aBottomText);
1236 clientThread.Start(myParams);
1240 private void buttonStartClient_Click(object sender, EventArgs e)
1245 private void buttonSuspend_Click(object sender, EventArgs e)
1247 LastTickTime = DateTime.Now; //Reset timer to prevent jump
1248 timer.Enabled = !timer.Enabled;
1251 buttonSuspend.Text = "Run";
1255 buttonSuspend.Text = "Pause";
1259 private void buttonCloseClients_Click(object sender, EventArgs e)
1261 BroadcastCloseEvent();
1264 private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e)
1273 /// <param name="aSessionId"></param>
1274 /// <param name="aCallback"></param>
1275 public void AddClientThreadSafe(string aSessionId, ICallback aCallback)
1277 if (this.InvokeRequired)
1279 //Not in the proper thread, invoke ourselves
1280 AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe);
1281 this.Invoke(d, new object[] { aSessionId, aCallback });
1285 //We are in the proper thread
1286 //Add this session to our collection of clients
1287 ClientData newClient = new ClientData(aSessionId, aCallback);
1288 Program.iMainForm.iClients.Add(aSessionId, newClient);
1289 //Add this session to our UI
1290 UpdateClientTreeViewNode(newClient);
1297 /// <param name="aSessionId"></param>
1298 public void RemoveClientThreadSafe(string aSessionId)
1300 if (this.InvokeRequired)
1302 //Not in the proper thread, invoke ourselves
1303 RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe);
1304 this.Invoke(d, new object[] { aSessionId });
1308 //We are in the proper thread
1309 //Remove this session from both client collection and UI tree view
1310 if (Program.iMainForm.iClients.Keys.Contains(aSessionId))
1312 Program.iMainForm.iClients.Remove(aSessionId);
1313 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(aSessionId, false)[0]);
1316 if (iClients.Count == 0)
1318 //Clear our screen when last client disconnects
1323 //We were closing our form
1324 //All clients are now closed
1325 //Just resume our close operation
1336 /// <param name="aSessionId"></param>
1337 /// <param name="aLayout"></param>
1338 public void SetClientLayoutThreadSafe(string aSessionId, TableLayout aLayout)
1340 if (this.InvokeRequired)
1342 //Not in the proper thread, invoke ourselves
1343 SetLayoutDelegate d = new SetLayoutDelegate(SetClientLayoutThreadSafe);
1344 this.Invoke(d, new object[] { aSessionId, aLayout });
1348 ClientData client = iClients[aSessionId];
1351 client.Layout = aLayout;
1352 UpdateTableLayoutPanel(client);
1354 UpdateClientTreeViewNode(client);
1362 /// <param name="aSessionId"></param>
1363 /// <param name="aField"></param>
1364 public void SetClientFieldThreadSafe(string aSessionId, DataField aField)
1366 if (this.InvokeRequired)
1368 //Not in the proper thread, invoke ourselves
1369 SetFieldDelegate d = new SetFieldDelegate(SetClientFieldThreadSafe);
1370 this.Invoke(d, new object[] { aSessionId, aField });
1374 //We are in the proper thread
1375 //Call the non-thread-safe variant
1376 SetClientField(aSessionId, aField);
1383 /// <param name="aSessionId"></param>
1384 /// <param name="aField"></param>
1385 private void SetClientField(string aSessionId, DataField aField)
1387 SetCurrentClient(aSessionId);
1388 ClientData client = iClients[aSessionId];
1391 bool somethingChanged = false;
1393 //Make sure all our fields are in place
1394 while (client.Fields.Count < (aField.Index + 1))
1396 //Add a text field with proper index
1397 client.Fields.Add(new DataField(client.Fields.Count));
1398 somethingChanged = true;
1401 if (client.Fields[aField.Index].IsSameLayout(aField))
1403 //Same layout just update our field
1404 client.Fields[aField.Index] = aField;
1406 if (aField.IsText && tableLayoutPanel.Controls[aField.Index] is MarqueeLabel)
1408 //Text field control already in place, just change the text
1409 MarqueeLabel label = (MarqueeLabel)tableLayoutPanel.Controls[aField.Index];
1410 somethingChanged = (label.Text != aField.Text || label.TextAlign != aField.Alignment);
1411 label.Text = aField.Text;
1412 label.TextAlign = aField.Alignment;
1414 else if (aField.IsBitmap && tableLayoutPanel.Controls[aField.Index] is PictureBox)
1416 somethingChanged = true; //TODO: Bitmap comp or should we leave that to clients?
1417 //Bitmap field control already in place just change the bitmap
1418 PictureBox pictureBox = (PictureBox)tableLayoutPanel.Controls[aField.Index];
1419 pictureBox.Image = aField.Bitmap;
1423 somethingChanged = true;
1424 //The requested control in our layout it not of the correct type
1425 //Wrong control type, re-create them all
1426 UpdateTableLayoutPanel(iCurrentClientData);
1431 somethingChanged = true;
1432 //Different layout, need to rebuild it
1433 client.Fields[aField.Index] = aField;
1434 UpdateTableLayoutPanel(iCurrentClientData);
1438 if (somethingChanged)
1440 UpdateClientTreeViewNode(client);
1448 /// <param name="aTexts"></param>
1449 public void SetClientFieldsThreadSafe(string aSessionId, System.Collections.Generic.IList<DataField> aFields)
1451 if (this.InvokeRequired)
1453 //Not in the proper thread, invoke ourselves
1454 SetFieldsDelegate d = new SetFieldsDelegate(SetClientFieldsThreadSafe);
1455 this.Invoke(d, new object[] { aSessionId, aFields });
1459 //Put each our text fields in a label control
1460 foreach (DataField field in aFields)
1462 SetClientField(aSessionId, field);
1470 /// <param name="aSessionId"></param>
1471 /// <param name="aName"></param>
1472 public void SetClientNameThreadSafe(string aSessionId, string aName)
1474 if (this.InvokeRequired)
1476 //Not in the proper thread, invoke ourselves
1477 SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe);
1478 this.Invoke(d, new object[] { aSessionId, aName });
1482 //We are in the proper thread
1484 ClientData client = iClients[aSessionId];
1488 client.Name = aName;
1489 //Update our tree-view
1490 UpdateClientTreeViewNode(client);
1498 /// <param name="aClient"></param>
1499 private void UpdateClientTreeViewNode(ClientData aClient)
1501 if (aClient == null)
1506 TreeNode node = null;
1507 //Check that our client node already exists
1508 //Get our client root node using its key which is our session ID
1509 TreeNode[] nodes = treeViewClients.Nodes.Find(aClient.SessionId, false);
1510 if (nodes.Count()>0)
1512 //We already have a node for that client
1514 //Clear children as we are going to recreate them below
1519 //Client node does not exists create a new one
1520 treeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId);
1521 node = treeViewClients.Nodes.Find(aClient.SessionId, false)[0];
1527 if (aClient.Name != "")
1529 //We have a name, us it as text for our root node
1530 node.Text = aClient.Name;
1531 //Add a child with SessionId
1532 node.Nodes.Add(new TreeNode(aClient.SessionId));
1536 //No name, use session ID instead
1537 node.Text = aClient.SessionId;
1540 if (aClient.Fields.Count > 0)
1542 //Create root node for our texts
1543 TreeNode textsRoot = new TreeNode("Fields");
1544 node.Nodes.Add(textsRoot);
1545 //For each text add a new entry
1546 foreach (DataField field in aClient.Fields)
1548 if (!field.IsBitmap)
1550 DataField textField = (DataField)field;
1551 textsRoot.Nodes.Add(new TreeNode("[Text]" + textField.Text));
1555 textsRoot.Nodes.Add(new TreeNode("[Bitmap]"));
1565 /// Update our table layout row styles to make sure each rows have similar height
1567 private void UpdateTableLayoutRowStyles()
1569 foreach (RowStyle rowStyle in tableLayoutPanel.RowStyles)
1571 rowStyle.SizeType = SizeType.Percent;
1572 rowStyle.Height = 100 / tableLayoutPanel.RowCount;
1578 /// Empty and recreate our table layout with the given number of columns and rows.
1579 /// Sizes of rows and columns are uniform.
1581 /// <param name="aColumn"></param>
1582 /// <param name="aRow"></param>
1583 private void UpdateTableLayoutPanel(int aColumn, int aRow)
1585 tableLayoutPanel.Controls.Clear();
1586 tableLayoutPanel.RowStyles.Clear();
1587 tableLayoutPanel.ColumnStyles.Clear();
1588 tableLayoutPanel.RowCount = 0;
1589 tableLayoutPanel.ColumnCount = 0;
1591 while (tableLayoutPanel.RowCount < aRow)
1593 tableLayoutPanel.RowCount++;
1596 while (tableLayoutPanel.ColumnCount < aColumn)
1598 tableLayoutPanel.ColumnCount++;
1601 for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
1603 //Create our column styles
1604 this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100 / tableLayoutPanel.ColumnCount));
1606 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
1610 //Create our row styles
1611 this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100 / tableLayoutPanel.RowCount));
1614 MarqueeLabel control = new SharpDisplayManager.MarqueeLabel();
1615 control.AutoEllipsis = true;
1616 control.AutoSize = true;
1617 control.BackColor = System.Drawing.Color.Transparent;
1618 control.Dock = System.Windows.Forms.DockStyle.Fill;
1619 control.Location = new System.Drawing.Point(1, 1);
1620 control.Margin = new System.Windows.Forms.Padding(0);
1621 control.Name = "marqueeLabelCol" + aColumn + "Row" + aRow;
1622 control.OwnTimer = false;
1623 control.PixelsPerSecond = 64;
1624 control.Separator = cds.Separator;
1625 control.MinFontSize = cds.MinFontSize;
1626 control.ScaleToFit = cds.ScaleToFit;
1627 //control.Size = new System.Drawing.Size(254, 30);
1628 //control.TabIndex = 2;
1629 control.Font = cds.Font;
1630 control.Text = "ABCDEFGHIJKLMNOPQRST-0123456789";
1631 control.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
1632 control.UseCompatibleTextRendering = true;
1634 tableLayoutPanel.Controls.Add(control, i, j);
1643 /// Update our display table layout.
1645 /// <param name="aLayout"></param>
1646 private void UpdateTableLayoutPanel(ClientData aClient)
1648 if (aClient == null)
1655 TableLayout layout = aClient.Layout;
1658 tableLayoutPanel.Controls.Clear();
1659 tableLayoutPanel.RowStyles.Clear();
1660 tableLayoutPanel.ColumnStyles.Clear();
1661 tableLayoutPanel.RowCount = 0;
1662 tableLayoutPanel.ColumnCount = 0;
1664 while (tableLayoutPanel.RowCount < layout.Rows.Count)
1666 tableLayoutPanel.RowCount++;
1669 while (tableLayoutPanel.ColumnCount < layout.Columns.Count)
1671 tableLayoutPanel.ColumnCount++;
1674 for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
1676 //Create our column styles
1677 this.tableLayoutPanel.ColumnStyles.Add(layout.Columns[i]);
1679 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
1683 //Create our row styles
1684 this.tableLayoutPanel.RowStyles.Add(layout.Rows[j]);
1687 //Check if we already have a control
1688 Control existingControl = tableLayoutPanel.GetControlFromPosition(i,j);
1689 if (existingControl!=null)
1691 //We already have a control in that cell as a results of row/col spanning
1692 //Move on to next cell then
1698 //Check if a client field already exists for that cell
1699 if (aClient.Fields.Count <= tableLayoutPanel.Controls.Count)
1701 //No client field specified, create a text field by default
1702 aClient.Fields.Add(new DataField(aClient.Fields.Count));
1705 //Create a control corresponding to the field specified for that cell
1706 DataField field = aClient.Fields[tableLayoutPanel.Controls.Count];
1707 Control control = CreateControlForDataField(field);
1709 //Add newly created control to our table layout at the specified row and column
1710 tableLayoutPanel.Controls.Add(control, i, j);
1711 //Make sure we specify row and column span for that new control
1712 tableLayoutPanel.SetRowSpan(control,field.RowSpan);
1713 tableLayoutPanel.SetColumnSpan(control, field.ColumnSpan);
1718 while (aClient.Fields.Count > fieldCount)
1720 //We have too much fields for this layout
1721 //Just discard them until we get there
1722 aClient.Fields.RemoveAt(aClient.Fields.Count-1);
1729 /// Check our type of data field and create corresponding control
1731 /// <param name="aField"></param>
1732 private Control CreateControlForDataField(DataField aField)
1734 Control control=null;
1735 if (!aField.IsBitmap)
1737 MarqueeLabel label = new SharpDisplayManager.MarqueeLabel();
1738 label.AutoEllipsis = true;
1739 label.AutoSize = true;
1740 label.BackColor = System.Drawing.Color.Transparent;
1741 label.Dock = System.Windows.Forms.DockStyle.Fill;
1742 label.Location = new System.Drawing.Point(1, 1);
1743 label.Margin = new System.Windows.Forms.Padding(0);
1744 label.Name = "marqueeLabel" + aField.Index;
1745 label.OwnTimer = false;
1746 label.PixelsPerSecond = cds.ScrollingSpeedInPixelsPerSecond;
1747 label.Separator = cds.Separator;
1748 label.MinFontSize = cds.MinFontSize;
1749 label.ScaleToFit = cds.ScaleToFit;
1750 //control.Size = new System.Drawing.Size(254, 30);
1751 //control.TabIndex = 2;
1752 label.Font = cds.Font;
1754 label.TextAlign = aField.Alignment;
1755 label.UseCompatibleTextRendering = true;
1756 label.Text = aField.Text;
1762 //Create picture box
1763 PictureBox picture = new PictureBox();
1764 picture.AutoSize = true;
1765 picture.BackColor = System.Drawing.Color.Transparent;
1766 picture.Dock = System.Windows.Forms.DockStyle.Fill;
1767 picture.Location = new System.Drawing.Point(1, 1);
1768 picture.Margin = new System.Windows.Forms.Padding(0);
1769 picture.Name = "pictureBox" + aField;
1771 picture.Image = aField.Bitmap;
1780 /// Called when the user selected a new display type.
1782 /// <param name="sender"></param>
1783 /// <param name="e"></param>
1784 private void comboBoxDisplayType_SelectedIndexChanged(object sender, EventArgs e)
1786 //Store the selected display type in our settings
1787 Properties.Settings.Default.CurrentDisplayIndex = comboBoxDisplayType.SelectedIndex;
1788 cds.DisplayType = comboBoxDisplayType.SelectedIndex;
1789 Properties.Settings.Default.Save();
1791 //Try re-opening the display connection if we were already connected.
1792 //Otherwise just update our status to reflect display type change.
1793 if (iDisplay.IsOpen())
1795 OpenDisplayConnection();
1803 private void maskedTextBoxTimerInterval_TextChanged(object sender, EventArgs e)
1805 if (maskedTextBoxTimerInterval.Text != "")
1807 int interval = Convert.ToInt32(maskedTextBoxTimerInterval.Text);
1811 timer.Interval = interval;
1812 cds.TimerInterval = timer.Interval;
1813 Properties.Settings.Default.Save();
1818 private void maskedTextBoxMinFontSize_TextChanged(object sender, EventArgs e)
1820 if (maskedTextBoxMinFontSize.Text != "")
1822 int minFontSize = Convert.ToInt32(maskedTextBoxMinFontSize.Text);
1824 if (minFontSize > 0)
1826 cds.MinFontSize = minFontSize;
1827 Properties.Settings.Default.Save();
1828 //We need to recreate our layout for that change to take effect
1829 UpdateTableLayoutPanel(iCurrentClientData);
1835 private void maskedTextBoxScrollingSpeed_TextChanged(object sender, EventArgs e)
1837 if (maskedTextBoxScrollingSpeed.Text != "")
1839 int scrollingSpeed = Convert.ToInt32(maskedTextBoxScrollingSpeed.Text);
1841 if (scrollingSpeed > 0)
1843 cds.ScrollingSpeedInPixelsPerSecond = scrollingSpeed;
1844 Properties.Settings.Default.Save();
1845 //We need to recreate our layout for that change to take effect
1846 UpdateTableLayoutPanel(iCurrentClientData);
1851 private void textBoxScrollLoopSeparator_TextChanged(object sender, EventArgs e)
1853 cds.Separator = textBoxScrollLoopSeparator.Text;
1854 Properties.Settings.Default.Save();
1856 //Update our text fields
1857 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
1859 ctrl.Separator = cds.Separator;
1864 private void buttonPowerOn_Click(object sender, EventArgs e)
1869 private void buttonPowerOff_Click(object sender, EventArgs e)
1871 iDisplay.PowerOff();
1874 private void buttonShowClock_Click(object sender, EventArgs e)
1876 iDisplay.ShowClock();
1879 private void buttonHideClock_Click(object sender, EventArgs e)
1881 iDisplay.HideClock();
1884 private void buttonUpdate_Click(object sender, EventArgs e)
1886 InstallUpdateSyncWithInfo();
1890 private void InstallUpdateSyncWithInfo()
1892 UpdateCheckInfo info = null;
1894 if (ApplicationDeployment.IsNetworkDeployed)
1896 ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
1900 info = ad.CheckForDetailedUpdate();
1903 catch (DeploymentDownloadException dde)
1905 MessageBox.Show("The new version of the application cannot be downloaded at this time. \n\nPlease check your network connection, or try again later. Error: " + dde.Message);
1908 catch (InvalidDeploymentException ide)
1910 MessageBox.Show("Cannot check for a new version of the application. The ClickOnce deployment is corrupt. Please redeploy the application and try again. Error: " + ide.Message);
1913 catch (InvalidOperationException ioe)
1915 MessageBox.Show("This application cannot be updated. It is likely not a ClickOnce application. Error: " + ioe.Message);
1919 if (info.UpdateAvailable)
1921 Boolean doUpdate = true;
1923 if (!info.IsUpdateRequired)
1925 DialogResult dr = MessageBox.Show("An update is available. Would you like to update the application now?", "Update Available", MessageBoxButtons.OKCancel);
1926 if (!(DialogResult.OK == dr))
1933 // Display a message that the app MUST reboot. Display the minimum required version.
1934 MessageBox.Show("This application has detected a mandatory update from your current " +
1935 "version to version " + info.MinimumRequiredVersion.ToString() +
1936 ". The application will now install the update and restart.",
1937 "Update Available", MessageBoxButtons.OK,
1938 MessageBoxIcon.Information);
1946 MessageBox.Show("The application has been upgraded, and will now restart.");
1947 Application.Restart();
1949 catch (DeploymentDownloadException dde)
1951 MessageBox.Show("Cannot install the latest version of the application. \n\nPlease check your network connection, or try again later. Error: " + dde);
1958 MessageBox.Show("You are already running the latest version.", "Application up-to-date");
1967 private void SysTrayHideShow()
1973 WindowState = FormWindowState.Normal;
1978 /// Use to handle minimize events.
1980 /// <param name="sender"></param>
1981 /// <param name="e"></param>
1982 private void MainForm_SizeChanged(object sender, EventArgs e)
1984 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
1997 /// <param name="sender"></param>
1998 /// <param name="e"></param>
1999 private void tableLayoutPanel_SizeChanged(object sender, EventArgs e)
2001 //Our table layout size has changed which means our display size has changed.
2002 //We need to re-create our bitmap.
2003 iCreateBitmap = true;
2008 /// A UI thread copy of a client relevant data.
2009 /// Keeping this copy in the UI thread helps us deal with threading issues.
2011 public class ClientData
2013 public ClientData(string aSessionId, ICallback aCallback)
2015 SessionId = aSessionId;
2017 Fields = new List<DataField>();
2018 Layout = new TableLayout(1, 2); //Default to one column and two rows
2019 Callback = aCallback;
2022 public string SessionId { get; set; }
2023 public string Name { get; set; }
2024 public List<DataField> Fields { get; set; }
2025 public TableLayout Layout { get; set; }
2026 public ICallback Callback { get; set; }