Audio: Taking icon statuc count into account.
Volume indicator on MDM166AA now using half brightness bar when rounding up.
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;
23 using SharpDisplayClient;
26 namespace SharpDisplayManager
29 public delegate uint ColorProcessingDelegate(int aX, int aY, uint aPixel);
30 public delegate int CoordinateTranslationDelegate(System.Drawing.Bitmap aBmp, int aInt);
31 //Delegates are used for our thread safe method
32 public delegate void AddClientDelegate(string aSessionId, ICallback aCallback);
33 public delegate void RemoveClientDelegate(string aSessionId);
34 public delegate void SetFieldDelegate(string SessionId, DataField aField);
35 public delegate void SetFieldsDelegate(string SessionId, System.Collections.Generic.IList<DataField> aFields);
36 public delegate void SetLayoutDelegate(string SessionId, TableLayout aLayout);
37 public delegate void SetClientNameDelegate(string aSessionId, string aName);
38 public delegate void PlainUpdateDelegate();
42 /// Our Display manager main form
44 public partial class MainForm : Form, IMMNotificationClient
46 DateTime LastTickTime;
48 System.Drawing.Bitmap iBmp;
49 bool iCreateBitmap; //Workaround render to bitmap issues when minimized
50 ServiceHost iServiceHost;
51 // Our collection of clients sorted by session id.
52 public Dictionary<string, ClientData> iClients;
53 // The name of the client which informations are currently displayed.
54 public string iCurrentClientSessionId;
55 ClientData iCurrentClientData;
58 //Function pointer for pixel color filtering
59 ColorProcessingDelegate iColorFx;
60 //Function pointer for pixel X coordinate intercept
61 CoordinateTranslationDelegate iScreenX;
62 //Function pointer for pixel Y coordinate intercept
63 CoordinateTranslationDelegate iScreenY;
65 private MMDeviceEnumerator iMultiMediaDeviceEnumerator;
66 private MMDevice iMultiMediaDevice;
70 /// Manage run when Windows startup option
72 private StartupManager iStartupManager;
77 private NotifyIconAdv iNotifyIcon;
81 iCurrentClientSessionId = "";
82 iCurrentClientData = null;
83 LastTickTime = DateTime.Now;
84 //Instantiate our display and register for events notifications
85 iDisplay = new Display();
86 iDisplay.OnOpened += OnDisplayOpened;
87 iDisplay.OnClosed += OnDisplayClosed;
89 iClients = new Dictionary<string, ClientData>();
90 iStartupManager = new StartupManager();
91 iNotifyIcon = new NotifyIconAdv();
93 //Have our designer initialize its controls
94 InitializeComponent();
96 //Populate device types
97 PopulateDeviceTypes();
99 //Initial status update
102 //We have a bug when drawing minimized and reusing our bitmap
103 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
104 iCreateBitmap = false;
106 //Minimize our window if desired
107 if (Properties.Settings.Default.StartMinimized)
109 WindowState = FormWindowState.Minimized;
117 /// <param name="sender"></param>
118 /// <param name="e"></param>
119 private void MainForm_Load(object sender, EventArgs e)
121 //Check if we are running a Click Once deployed application
122 if (ApplicationDeployment.IsNetworkDeployed)
124 //This is a proper Click Once installation, fetch and show our version number
125 this.Text += " - v" + ApplicationDeployment.CurrentDeployment.CurrentVersion;
129 //Not a proper Click Once installation, assuming development build then
130 this.Text += " - development";
134 iMultiMediaDeviceEnumerator = new MMDeviceEnumerator();
135 iMultiMediaDeviceEnumerator.RegisterEndpointNotificationCallback(this);
137 UpdateAudioDeviceAndMasterVolumeThreadSafe();
139 //Setup notification icon
142 // To make sure start up with minimize to tray works
143 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
149 //When not debugging we want the screen to be empty until a client takes over
152 //When developing we want at least one client for testing
153 StartNewClient("abcdefghijklmnopqrst-0123456789","ABCDEFGHIJKLMNOPQRST-0123456789");
156 //Open display connection on start-up if needed
157 if (Properties.Settings.Default.DisplayConnectOnStartup)
159 OpenDisplayConnection();
162 //Start our server so that we can get client requests
167 /// Called when our display is opened.
169 /// <param name="aDisplay"></param>
170 private void OnDisplayOpened(Display aDisplay)
172 //Set our screen size now that our display is connected
173 //Our panelDisplay is the container of our tableLayoutPanel
174 //tableLayoutPanel will resize itself to fit the client size of our panelDisplay
175 //panelDisplay needs an extra 2 pixels for borders on each sides
176 //tableLayoutPanel will eventually be the exact size of our display
177 Size size = new Size(iDisplay.WidthInPixels() + 2, iDisplay.HeightInPixels() + 2);
178 panelDisplay.Size = size;
180 //Our display was just opened, update our UI
182 //Initiate asynchronous request
183 iDisplay.RequestFirmwareRevision();
186 UpdateMasterVolumeThreadSafe();
189 //Testing icon in debug, no arm done if icon not supported
190 //iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconRecording, 0, 1);
191 //iDisplay.SetAllIconsStatus(2);
197 /// Called when our display is closed.
199 /// <param name="aDisplay"></param>
200 private void OnDisplayClosed(Display aDisplay)
202 //Our display was just closed, update our UI consequently
207 /// Receive volume change notification and reflect changes on our slider.
209 /// <param name="data"></param>
210 public void OnVolumeNotificationThreadSafe(AudioVolumeNotificationData data)
212 UpdateMasterVolumeThreadSafe();
216 /// Update master volume when user moves our slider.
218 /// <param name="sender"></param>
219 /// <param name="e"></param>
220 private void trackBarMasterVolume_Scroll(object sender, EventArgs e)
222 iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar = trackBarMasterVolume.Value / 100.0f;
227 /// Mute check box changed.
229 /// <param name="sender"></param>
230 /// <param name="e"></param>
231 private void checkBoxMute_CheckedChanged(object sender, EventArgs e)
233 iMultiMediaDevice.AudioEndpointVolume.Mute = checkBoxMute.Checked;
238 /// Device State Changed
240 public void OnDeviceStateChanged([MarshalAs(UnmanagedType.LPWStr)] string deviceId, [MarshalAs(UnmanagedType.I4)] DeviceState newState){}
245 public void OnDeviceAdded([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId) { }
250 public void OnDeviceRemoved([MarshalAs(UnmanagedType.LPWStr)] string deviceId) { }
253 /// Default Device Changed
255 public void OnDefaultDeviceChanged(DataFlow flow, Role role, [MarshalAs(UnmanagedType.LPWStr)] string defaultDeviceId)
257 if (role == Role.Multimedia && flow == DataFlow.Render)
259 UpdateAudioDeviceAndMasterVolumeThreadSafe();
264 /// Property Value Changed
266 /// <param name="pwstrDeviceId"></param>
267 /// <param name="key"></param>
268 public void OnPropertyValueChanged([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId, PropertyKey key){}
276 private void UpdateMasterVolumeThreadSafe()
278 if (this.InvokeRequired)
280 //Not in the proper thread, invoke ourselves
281 PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateMasterVolumeThreadSafe);
282 this.Invoke(d, new object[] { });
286 //Update volume slider
287 float volumeLevelScalar = iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar;
288 trackBarMasterVolume.Value = Convert.ToInt32(volumeLevelScalar * 100);
289 //Update mute checkbox
290 checkBoxMute.Checked = iMultiMediaDevice.AudioEndpointVolume.Mute;
292 //TODO: Check our display device too
293 if (iDisplay.IsOpen())
295 int volumeIconCount = iDisplay.IconCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolume);
296 if (volumeIconCount > 0)
298 int currentVolume = Convert.ToInt32(volumeLevelScalar * volumeIconCount);
300 bool roundedUp = currentVolume > (volumeLevelScalar * volumeIconCount);
302 for (int i = 0; i < volumeIconCount; i++)
304 if (i < currentVolume)
306 if (i == currentVolume - 1 && roundedUp)
309 iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconVolume, i, (iDisplay.IconStatusCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolume) - 1)/2);
314 iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconVolume, i, iDisplay.IconStatusCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolume) - 1);
319 iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconVolume, i, 0);
324 int muteIconCount = iDisplay.IconCount(Display.TMiniDisplayIconType.EMiniDisplayIconMute);
325 if (muteIconCount > 0)
327 for (int i = 0; i < muteIconCount; i++)
329 if (iMultiMediaDevice.AudioEndpointVolume.Mute)
331 iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconMute, i, iDisplay.IconStatusCount(Display.TMiniDisplayIconType.EMiniDisplayIconMute)-1);
335 iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconMute, i, 0);
347 private void UpdateAudioDeviceAndMasterVolumeThreadSafe()
349 if (this.InvokeRequired)
351 //Not in the proper thread, invoke ourselves
352 PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateAudioDeviceAndMasterVolumeThreadSafe);
353 this.Invoke(d, new object[] { });
357 //We are in the correct thread just go ahead.
360 //Get our master volume
361 iMultiMediaDevice = iMultiMediaDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
362 //Show our volume in our track bar
363 UpdateMasterVolumeThreadSafe();
365 //Register to get volume modifications
366 iMultiMediaDevice.AudioEndpointVolume.OnVolumeNotification += OnVolumeNotificationThreadSafe;
368 trackBarMasterVolume.Enabled = true;
372 Debug.WriteLine("Exception thrown in UpdateAudioDeviceAndMasterVolume");
373 Debug.WriteLine(ex.ToString());
374 //Something went wrong S/PDIF device ca throw exception I guess
375 trackBarMasterVolume.Enabled = false;
382 private void PopulateDeviceTypes()
384 int count = Display.TypeCount();
386 for (int i = 0; i < count; i++)
388 comboBoxDisplayType.Items.Add(Display.TypeName((Display.TMiniDisplayType)i));
395 private void SetupTrayIcon()
397 iNotifyIcon.Icon = GetIcon("vfd.ico");
398 iNotifyIcon.Text = "Sharp Display Manager";
399 iNotifyIcon.Visible = true;
401 //Double click toggles visibility - typically brings up the application
402 iNotifyIcon.DoubleClick += delegate(object obj, EventArgs args)
407 //Adding a context menu, useful to be able to exit the application
408 ContextMenu contextMenu = new ContextMenu();
409 //Context menu item to toggle visibility
410 MenuItem hideShowItem = new MenuItem("Hide/Show");
411 hideShowItem.Click += delegate(object obj, EventArgs args)
415 contextMenu.MenuItems.Add(hideShowItem);
417 //Context menu item separator
418 contextMenu.MenuItems.Add(new MenuItem("-"));
420 //Context menu exit item
421 MenuItem exitItem = new MenuItem("Exit");
422 exitItem.Click += delegate(object obj, EventArgs args)
426 contextMenu.MenuItems.Add(exitItem);
428 iNotifyIcon.ContextMenu = contextMenu;
432 /// Access icons from embedded resources.
434 /// <param name="name"></param>
435 /// <returns></returns>
436 public static Icon GetIcon(string name)
438 name = "SharpDisplayManager.Resources." + name;
441 Assembly.GetExecutingAssembly().GetManifestResourceNames();
442 for (int i = 0; i < names.Length; i++)
444 if (names[i].Replace('\\', '.') == name)
446 using (Stream stream = Assembly.GetExecutingAssembly().
447 GetManifestResourceStream(names[i]))
449 return new Icon(stream);
459 /// Set our current client.
460 /// This will take care of applying our client layout and set data fields.
462 /// <param name="aSessionId"></param>
463 void SetCurrentClient(string aSessionId)
465 if (aSessionId == iCurrentClientSessionId)
467 //Given client is already the current one.
468 //Don't bother changing anything then.
472 //Set current client ID.
473 iCurrentClientSessionId = aSessionId;
474 //Fetch and set current client data.
475 iCurrentClientData = iClients[aSessionId];
476 //Apply layout and set data fields.
477 UpdateTableLayoutPanel(iCurrentClientData);
480 private void buttonFont_Click(object sender, EventArgs e)
482 //fontDialog.ShowColor = true;
483 //fontDialog.ShowApply = true;
484 fontDialog.ShowEffects = true;
485 fontDialog.Font = cds.Font;
487 fontDialog.FixedPitchOnly = checkBoxFixedPitchFontOnly.Checked;
489 //fontDialog.ShowHelp = true;
491 //fontDlg.MaxSize = 40;
492 //fontDlg.MinSize = 22;
494 //fontDialog.Parent = this;
495 //fontDialog.StartPosition = FormStartPosition.CenterParent;
497 //DlgBox.ShowDialog(fontDialog);
499 //if (fontDialog.ShowDialog(this) != DialogResult.Cancel)
500 if (DlgBox.ShowDialog(fontDialog) != DialogResult.Cancel)
502 //Set the fonts to all our labels in our layout
503 foreach (Control ctrl in tableLayoutPanel.Controls)
505 if (ctrl is MarqueeLabel)
507 ((MarqueeLabel)ctrl).Font = fontDialog.Font;
512 cds.Font = fontDialog.Font;
513 Properties.Settings.Default.Save();
522 void CheckFontHeight()
524 //Show font height and width
525 labelFontHeight.Text = "Font height: " + cds.Font.Height;
526 float charWidth = IsFixedWidth(cds.Font);
527 if (charWidth == 0.0f)
529 labelFontWidth.Visible = false;
533 labelFontWidth.Visible = true;
534 labelFontWidth.Text = "Font width: " + charWidth;
537 MarqueeLabel label = null;
538 //Get the first label control we can find
539 foreach (Control ctrl in tableLayoutPanel.Controls)
541 if (ctrl is MarqueeLabel)
543 label = (MarqueeLabel)ctrl;
548 //Now check font height and show a warning if needed.
549 if (label != null && label.Font.Height > label.Height)
551 labelWarning.Text = "WARNING: Selected font is too height by " + (label.Font.Height - label.Height) + " pixels!";
552 labelWarning.Visible = true;
556 labelWarning.Visible = false;
561 private void buttonCapture_Click(object sender, EventArgs e)
563 System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height);
564 tableLayoutPanel.DrawToBitmap(bmp, tableLayoutPanel.ClientRectangle);
565 //Bitmap bmpToSave = new Bitmap(bmp);
566 bmp.Save("D:\\capture.png");
568 ((MarqueeLabel)tableLayoutPanel.Controls[0]).Text = "Captured";
571 string outputFileName = "d:\\capture.png";
572 using (MemoryStream memory = new MemoryStream())
574 using (FileStream fs = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
576 bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
577 byte[] bytes = memory.ToArray();
578 fs.Write(bytes, 0, bytes.Length);
585 private void CheckForRequestResults()
587 if (iDisplay.IsRequestPending())
589 switch (iDisplay.AttemptRequestCompletion())
591 case Display.TMiniDisplayRequest.EMiniDisplayRequestFirmwareRevision:
592 toolStripStatusLabelConnect.Text += " v" + iDisplay.FirmwareRevision();
593 //Issue next request then
594 iDisplay.RequestPowerSupplyStatus();
597 case Display.TMiniDisplayRequest.EMiniDisplayRequestPowerSupplyStatus:
598 if (iDisplay.PowerSupplyStatus())
600 toolStripStatusLabelPower.Text = "ON";
604 toolStripStatusLabelPower.Text = "OFF";
606 //Issue next request then
607 iDisplay.RequestDeviceId();
610 case Display.TMiniDisplayRequest.EMiniDisplayRequestDeviceId:
611 toolStripStatusLabelConnect.Text += " - " + iDisplay.DeviceId();
612 //No more request to issue
618 public static uint ColorWhiteIsOn(int aX, int aY, uint aPixel)
620 if ((aPixel & 0x00FFFFFF) == 0x00FFFFFF)
627 public static uint ColorUntouched(int aX, int aY, uint aPixel)
632 public static uint ColorInversed(int aX, int aY, uint aPixel)
637 public static uint ColorChessboard(int aX, int aY, uint aPixel)
639 if ((aX % 2 == 0) && (aY % 2 == 0))
643 else if ((aX % 2 != 0) && (aY % 2 != 0))
651 public static int ScreenReversedX(System.Drawing.Bitmap aBmp, int aX)
653 return aBmp.Width - aX - 1;
656 public int ScreenReversedY(System.Drawing.Bitmap aBmp, int aY)
658 return iBmp.Height - aY - 1;
661 public int ScreenX(System.Drawing.Bitmap aBmp, int aX)
666 public int ScreenY(System.Drawing.Bitmap aBmp, int aY)
672 /// Select proper pixel delegates according to our current settings.
674 private void SetupPixelDelegates()
676 //Select our pixel processing routine
677 if (cds.InverseColors)
679 //iColorFx = ColorChessboard;
680 iColorFx = ColorInversed;
684 iColorFx = ColorWhiteIsOn;
687 //Select proper coordinate translation functions
688 //We used delegate/function pointer to support reverse screen without doing an extra test on each pixels
689 if (cds.ReverseScreen)
691 iScreenX = ScreenReversedX;
692 iScreenY = ScreenReversedY;
702 //This is our timer tick responsible to perform our render
703 private void timer_Tick(object sender, EventArgs e)
705 //Update our animations
706 DateTime NewTickTime = DateTime.Now;
708 //Update animation for all our marquees
709 foreach (Control ctrl in tableLayoutPanel.Controls)
711 if (ctrl is MarqueeLabel)
713 ((MarqueeLabel)ctrl).UpdateAnimation(LastTickTime, NewTickTime);
719 if (iDisplay.IsOpen())
721 CheckForRequestResults();
726 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
728 tableLayoutPanel.DrawToBitmap(iBmp, tableLayoutPanel.ClientRectangle);
729 //iBmp.Save("D:\\capture.png");
731 //Send it to our display
732 for (int i = 0; i < iBmp.Width; i++)
734 for (int j = 0; j < iBmp.Height; j++)
738 //Get our processed pixel coordinates
739 int x = iScreenX(iBmp, i);
740 int y = iScreenY(iBmp, j);
742 uint color = (uint)iBmp.GetPixel(i, j).ToArgb();
743 //Apply color effects
744 color = iColorFx(x,y,color);
746 iDisplay.SetPixel(x, y, color);
751 iDisplay.SwapBuffers();
755 //Compute instant FPS
756 toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " / " + (1000/timer.Interval).ToString() + " FPS";
758 LastTickTime = NewTickTime;
763 /// Attempt to establish connection with our display hardware.
765 private void OpenDisplayConnection()
767 CloseDisplayConnection();
769 if (!iDisplay.Open((Display.TMiniDisplayType)cds.DisplayType))
772 toolStripStatusLabelConnect.Text = "Connection error";
776 private void CloseDisplayConnection()
778 //Status will be updated upon receiving the closed event
782 private void buttonOpen_Click(object sender, EventArgs e)
784 OpenDisplayConnection();
787 private void buttonClose_Click(object sender, EventArgs e)
789 CloseDisplayConnection();
792 private void buttonClear_Click(object sender, EventArgs e)
795 iDisplay.SwapBuffers();
798 private void buttonFill_Click(object sender, EventArgs e)
801 iDisplay.SwapBuffers();
804 private void trackBarBrightness_Scroll(object sender, EventArgs e)
806 cds.Brightness = trackBarBrightness.Value;
807 Properties.Settings.Default.Save();
808 iDisplay.SetBrightness(trackBarBrightness.Value);
814 /// CDS stands for Current Display Settings
816 private DisplaySettings cds
820 DisplaysSettings settings = Properties.Settings.Default.DisplaysSettings;
821 if (settings == null)
823 settings = new DisplaysSettings();
825 Properties.Settings.Default.DisplaysSettings = settings;
828 //Make sure all our settings have been created
829 while (settings.Displays.Count <= Properties.Settings.Default.CurrentDisplayIndex)
831 settings.Displays.Add(new DisplaySettings());
834 DisplaySettings displaySettings = settings.Displays[Properties.Settings.Default.CurrentDisplayIndex];
835 return displaySettings;
840 /// Check if the given font has a fixed character pitch.
842 /// <param name="ft"></param>
843 /// <returns>0.0f if this is not a monospace font, otherwise returns the character width.</returns>
844 public float IsFixedWidth(Font ft)
846 Graphics g = CreateGraphics();
847 char[] charSizes = new char[] { 'i', 'a', 'Z', '%', '#', 'a', 'B', 'l', 'm', ',', '.' };
848 float charWidth = g.MeasureString("I", ft, Int32.MaxValue, StringFormat.GenericTypographic).Width;
850 bool fixedWidth = true;
852 foreach (char c in charSizes)
853 if (g.MeasureString(c.ToString(), ft, Int32.MaxValue, StringFormat.GenericTypographic).Width != charWidth)
865 /// Synchronize UI with settings
867 private void UpdateStatus()
870 checkBoxShowBorders.Checked = cds.ShowBorders;
871 tableLayoutPanel.CellBorderStyle = (cds.ShowBorders ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
873 //Set the proper font to each of our labels
874 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
876 ctrl.Font = cds.Font;
880 //Check if "run on Windows startup" is enabled
881 checkBoxAutoStart.Checked = iStartupManager.Startup;
883 checkBoxConnectOnStartup.Checked = Properties.Settings.Default.DisplayConnectOnStartup;
884 checkBoxMinimizeToTray.Checked = Properties.Settings.Default.MinimizeToTray;
885 checkBoxStartMinimized.Checked = Properties.Settings.Default.StartMinimized;
886 checkBoxReverseScreen.Checked = cds.ReverseScreen;
887 checkBoxInverseColors.Checked = cds.InverseColors;
888 checkBoxScaleToFit.Checked = cds.ScaleToFit;
889 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
890 labelMinFontSize.Enabled = cds.ScaleToFit;
891 maskedTextBoxMinFontSize.Text = cds.MinFontSize.ToString();
892 maskedTextBoxScrollingSpeed.Text = cds.ScrollingSpeedInPixelsPerSecond.ToString();
893 comboBoxDisplayType.SelectedIndex = cds.DisplayType;
894 timer.Interval = cds.TimerInterval;
895 maskedTextBoxTimerInterval.Text = cds.TimerInterval.ToString();
896 textBoxScrollLoopSeparator.Text = cds.Separator;
898 SetupPixelDelegates();
900 if (iDisplay.IsOpen())
902 //We have a display connection
903 //Reflect that in our UI
905 tableLayoutPanel.Enabled = true;
906 panelDisplay.Enabled = true;
908 //Only setup brightness if display is open
909 trackBarBrightness.Minimum = iDisplay.MinBrightness();
910 trackBarBrightness.Maximum = iDisplay.MaxBrightness();
911 if (cds.Brightness < iDisplay.MinBrightness() || cds.Brightness > iDisplay.MaxBrightness())
913 //Brightness out of range, this can occur when using auto-detect
914 //Use max brightness instead
915 trackBarBrightness.Value = iDisplay.MaxBrightness();
916 iDisplay.SetBrightness(iDisplay.MaxBrightness());
920 trackBarBrightness.Value = cds.Brightness;
921 iDisplay.SetBrightness(cds.Brightness);
924 //Try compute the steps to something that makes sense
925 trackBarBrightness.LargeChange = Math.Max(1, (iDisplay.MaxBrightness() - iDisplay.MinBrightness()) / 5);
926 trackBarBrightness.SmallChange = 1;
929 buttonFill.Enabled = true;
930 buttonClear.Enabled = true;
931 buttonOpen.Enabled = false;
932 buttonClose.Enabled = true;
933 trackBarBrightness.Enabled = true;
934 toolStripStatusLabelConnect.Text = "Connected - " + iDisplay.Vendor() + " - " + iDisplay.Product();
935 //+ " - " + iDisplay.SerialNumber();
937 if (iDisplay.SupportPowerOnOff())
939 buttonPowerOn.Enabled = true;
940 buttonPowerOff.Enabled = true;
944 buttonPowerOn.Enabled = false;
945 buttonPowerOff.Enabled = false;
948 if (iDisplay.SupportClock())
950 buttonShowClock.Enabled = true;
951 buttonHideClock.Enabled = true;
955 buttonShowClock.Enabled = false;
956 buttonHideClock.Enabled = false;
961 //Display is connection not available
962 //Reflect that in our UI
963 tableLayoutPanel.Enabled = false;
964 panelDisplay.Enabled = false;
965 buttonFill.Enabled = false;
966 buttonClear.Enabled = false;
967 buttonOpen.Enabled = true;
968 buttonClose.Enabled = false;
969 trackBarBrightness.Enabled = false;
970 buttonPowerOn.Enabled = false;
971 buttonPowerOff.Enabled = false;
972 buttonShowClock.Enabled = false;
973 buttonHideClock.Enabled = false;
974 toolStripStatusLabelConnect.Text = "Disconnected";
975 toolStripStatusLabelPower.Text = "N/A";
982 private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e)
984 //Save our show borders setting
985 tableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
986 cds.ShowBorders = checkBoxShowBorders.Checked;
987 Properties.Settings.Default.Save();
991 private void checkBoxConnectOnStartup_CheckedChanged(object sender, EventArgs e)
993 //Save our connect on startup setting
994 Properties.Settings.Default.DisplayConnectOnStartup = checkBoxConnectOnStartup.Checked;
995 Properties.Settings.Default.Save();
998 private void checkBoxMinimizeToTray_CheckedChanged(object sender, EventArgs e)
1000 //Save our "Minimize to tray" setting
1001 Properties.Settings.Default.MinimizeToTray = checkBoxMinimizeToTray.Checked;
1002 Properties.Settings.Default.Save();
1006 private void checkBoxStartMinimized_CheckedChanged(object sender, EventArgs e)
1008 //Save our "Start minimized" setting
1009 Properties.Settings.Default.StartMinimized = checkBoxStartMinimized.Checked;
1010 Properties.Settings.Default.Save();
1013 private void checkBoxAutoStart_CheckedChanged(object sender, EventArgs e)
1015 iStartupManager.Startup = checkBoxAutoStart.Checked;
1019 private void checkBoxReverseScreen_CheckedChanged(object sender, EventArgs e)
1021 //Save our reverse screen setting
1022 cds.ReverseScreen = checkBoxReverseScreen.Checked;
1023 Properties.Settings.Default.Save();
1024 SetupPixelDelegates();
1027 private void checkBoxInverseColors_CheckedChanged(object sender, EventArgs e)
1029 //Save our inverse colors setting
1030 cds.InverseColors = checkBoxInverseColors.Checked;
1031 Properties.Settings.Default.Save();
1032 SetupPixelDelegates();
1035 private void checkBoxScaleToFit_CheckedChanged(object sender, EventArgs e)
1037 //Save our scale to fit setting
1038 cds.ScaleToFit = checkBoxScaleToFit.Checked;
1039 Properties.Settings.Default.Save();
1041 labelMinFontSize.Enabled = cds.ScaleToFit;
1042 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
1045 private void MainForm_Resize(object sender, EventArgs e)
1047 if (WindowState == FormWindowState.Minimized)
1050 //iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
1051 iCreateBitmap = true;
1055 private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
1057 CloseDisplayConnection();
1059 e.Cancel = iClosing;
1062 public void StartServer()
1064 iServiceHost = new ServiceHost
1067 new Uri[] { new Uri("net.tcp://localhost:8001/") }
1070 iServiceHost.AddServiceEndpoint(typeof(IService), new NetTcpBinding(SecurityMode.None, true), "DisplayService");
1071 iServiceHost.Open();
1074 public void StopServer()
1076 if (iClients.Count > 0 && !iClosing)
1080 BroadcastCloseEvent();
1084 if (MessageBox.Show("Force exit?", "Waiting for clients...", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
1086 iClosing = false; //We make sure we force close if asked twice
1091 //We removed that as it often lags for some reason
1092 //iServiceHost.Close();
1096 public void BroadcastCloseEvent()
1098 Trace.TraceInformation("BroadcastCloseEvent - start");
1100 var inactiveClients = new List<string>();
1101 foreach (var client in iClients)
1103 //if (client.Key != eventData.ClientName)
1107 Trace.TraceInformation("BroadcastCloseEvent - " + client.Key);
1108 client.Value.Callback.OnCloseOrder(/*eventData*/);
1110 catch (Exception ex)
1112 inactiveClients.Add(client.Key);
1117 if (inactiveClients.Count > 0)
1119 foreach (var client in inactiveClients)
1121 iClients.Remove(client);
1122 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(client, false)[0]);
1126 if (iClients.Count==0)
1133 /// Just remove all our fields.
1135 private void ClearLayout()
1137 tableLayoutPanel.Controls.Clear();
1138 tableLayoutPanel.RowStyles.Clear();
1139 tableLayoutPanel.ColumnStyles.Clear();
1143 /// Just launch a demo client.
1145 private void StartNewClient(string aTopText = "", string aBottomText = "")
1147 Thread clientThread = new Thread(SharpDisplayClient.Program.MainWithParams);
1148 SharpDisplayClient.StartParams myParams = new SharpDisplayClient.StartParams(new Point(this.Right, this.Top),aTopText,aBottomText);
1149 clientThread.Start(myParams);
1153 private void buttonStartClient_Click(object sender, EventArgs e)
1158 private void buttonSuspend_Click(object sender, EventArgs e)
1160 LastTickTime = DateTime.Now; //Reset timer to prevent jump
1161 timer.Enabled = !timer.Enabled;
1164 buttonSuspend.Text = "Run";
1168 buttonSuspend.Text = "Pause";
1172 private void buttonCloseClients_Click(object sender, EventArgs e)
1174 BroadcastCloseEvent();
1177 private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e)
1186 /// <param name="aSessionId"></param>
1187 /// <param name="aCallback"></param>
1188 public void AddClientThreadSafe(string aSessionId, ICallback aCallback)
1190 if (this.InvokeRequired)
1192 //Not in the proper thread, invoke ourselves
1193 AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe);
1194 this.Invoke(d, new object[] { aSessionId, aCallback });
1198 //We are in the proper thread
1199 //Add this session to our collection of clients
1200 ClientData newClient = new ClientData(aSessionId, aCallback);
1201 Program.iMainForm.iClients.Add(aSessionId, newClient);
1202 //Add this session to our UI
1203 UpdateClientTreeViewNode(newClient);
1210 /// <param name="aSessionId"></param>
1211 public void RemoveClientThreadSafe(string aSessionId)
1213 if (this.InvokeRequired)
1215 //Not in the proper thread, invoke ourselves
1216 RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe);
1217 this.Invoke(d, new object[] { aSessionId });
1221 //We are in the proper thread
1222 //Remove this session from both client collection and UI tree view
1223 if (Program.iMainForm.iClients.Keys.Contains(aSessionId))
1225 Program.iMainForm.iClients.Remove(aSessionId);
1226 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(aSessionId, false)[0]);
1229 if (iClients.Count == 0)
1231 //Clear our screen when last client disconnects
1236 //We were closing our form
1237 //All clients are now closed
1238 //Just resume our close operation
1249 /// <param name="aSessionId"></param>
1250 /// <param name="aLayout"></param>
1251 public void SetClientLayoutThreadSafe(string aSessionId, TableLayout aLayout)
1253 if (this.InvokeRequired)
1255 //Not in the proper thread, invoke ourselves
1256 SetLayoutDelegate d = new SetLayoutDelegate(SetClientLayoutThreadSafe);
1257 this.Invoke(d, new object[] { aSessionId, aLayout });
1261 ClientData client = iClients[aSessionId];
1264 client.Layout = aLayout;
1265 UpdateTableLayoutPanel(client);
1267 UpdateClientTreeViewNode(client);
1275 /// <param name="aSessionId"></param>
1276 /// <param name="aField"></param>
1277 public void SetClientFieldThreadSafe(string aSessionId, DataField aField)
1279 if (this.InvokeRequired)
1281 //Not in the proper thread, invoke ourselves
1282 SetFieldDelegate d = new SetFieldDelegate(SetClientFieldThreadSafe);
1283 this.Invoke(d, new object[] { aSessionId, aField });
1287 //We are in the proper thread
1288 //Call the non-thread-safe variant
1289 SetClientField(aSessionId, aField);
1296 /// <param name="aSessionId"></param>
1297 /// <param name="aField"></param>
1298 private void SetClientField(string aSessionId, DataField aField)
1300 SetCurrentClient(aSessionId);
1301 ClientData client = iClients[aSessionId];
1304 bool somethingChanged = false;
1306 //Make sure all our fields are in place
1307 while (client.Fields.Count < (aField.Index + 1))
1309 //Add a text field with proper index
1310 client.Fields.Add(new DataField(client.Fields.Count));
1311 somethingChanged = true;
1314 if (client.Fields[aField.Index].IsSameLayout(aField))
1316 //Same layout just update our field
1317 client.Fields[aField.Index] = aField;
1319 if (aField.IsText && tableLayoutPanel.Controls[aField.Index] is MarqueeLabel)
1321 //Text field control already in place, just change the text
1322 MarqueeLabel label = (MarqueeLabel)tableLayoutPanel.Controls[aField.Index];
1323 somethingChanged = (label.Text != aField.Text || label.TextAlign != aField.Alignment);
1324 label.Text = aField.Text;
1325 label.TextAlign = aField.Alignment;
1327 else if (aField.IsBitmap && tableLayoutPanel.Controls[aField.Index] is PictureBox)
1329 somethingChanged = true; //TODO: Bitmap comp or should we leave that to clients?
1330 //Bitmap field control already in place just change the bitmap
1331 PictureBox pictureBox = (PictureBox)tableLayoutPanel.Controls[aField.Index];
1332 pictureBox.Image = aField.Bitmap;
1336 somethingChanged = true;
1337 //The requested control in our layout it not of the correct type
1338 //Wrong control type, re-create them all
1339 UpdateTableLayoutPanel(iCurrentClientData);
1344 somethingChanged = true;
1345 //Different layout, need to rebuild it
1346 client.Fields[aField.Index] = aField;
1347 UpdateTableLayoutPanel(iCurrentClientData);
1351 if (somethingChanged)
1353 UpdateClientTreeViewNode(client);
1361 /// <param name="aTexts"></param>
1362 public void SetClientFieldsThreadSafe(string aSessionId, System.Collections.Generic.IList<DataField> aFields)
1364 if (this.InvokeRequired)
1366 //Not in the proper thread, invoke ourselves
1367 SetFieldsDelegate d = new SetFieldsDelegate(SetClientFieldsThreadSafe);
1368 this.Invoke(d, new object[] { aSessionId, aFields });
1372 //Put each our text fields in a label control
1373 foreach (DataField field in aFields)
1375 SetClientField(aSessionId, field);
1383 /// <param name="aSessionId"></param>
1384 /// <param name="aName"></param>
1385 public void SetClientNameThreadSafe(string aSessionId, string aName)
1387 if (this.InvokeRequired)
1389 //Not in the proper thread, invoke ourselves
1390 SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe);
1391 this.Invoke(d, new object[] { aSessionId, aName });
1395 //We are in the proper thread
1397 ClientData client = iClients[aSessionId];
1401 client.Name = aName;
1402 //Update our tree-view
1403 UpdateClientTreeViewNode(client);
1411 /// <param name="aClient"></param>
1412 private void UpdateClientTreeViewNode(ClientData aClient)
1414 if (aClient == null)
1419 TreeNode node = null;
1420 //Check that our client node already exists
1421 //Get our client root node using its key which is our session ID
1422 TreeNode[] nodes = treeViewClients.Nodes.Find(aClient.SessionId, false);
1423 if (nodes.Count()>0)
1425 //We already have a node for that client
1427 //Clear children as we are going to recreate them below
1432 //Client node does not exists create a new one
1433 treeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId);
1434 node = treeViewClients.Nodes.Find(aClient.SessionId, false)[0];
1440 if (aClient.Name != "")
1442 //We have a name, us it as text for our root node
1443 node.Text = aClient.Name;
1444 //Add a child with SessionId
1445 node.Nodes.Add(new TreeNode(aClient.SessionId));
1449 //No name, use session ID instead
1450 node.Text = aClient.SessionId;
1453 if (aClient.Fields.Count > 0)
1455 //Create root node for our texts
1456 TreeNode textsRoot = new TreeNode("Fields");
1457 node.Nodes.Add(textsRoot);
1458 //For each text add a new entry
1459 foreach (DataField field in aClient.Fields)
1461 if (!field.IsBitmap)
1463 DataField textField = (DataField)field;
1464 textsRoot.Nodes.Add(new TreeNode("[Text]" + textField.Text));
1468 textsRoot.Nodes.Add(new TreeNode("[Bitmap]"));
1478 /// Update our table layout row styles to make sure each rows have similar height
1480 private void UpdateTableLayoutRowStyles()
1482 foreach (RowStyle rowStyle in tableLayoutPanel.RowStyles)
1484 rowStyle.SizeType = SizeType.Percent;
1485 rowStyle.Height = 100 / tableLayoutPanel.RowCount;
1491 /// Empty and recreate our table layout with the given number of columns and rows.
1492 /// Sizes of rows and columns are uniform.
1494 /// <param name="aColumn"></param>
1495 /// <param name="aRow"></param>
1496 private void UpdateTableLayoutPanel(int aColumn, int aRow)
1498 tableLayoutPanel.Controls.Clear();
1499 tableLayoutPanel.RowStyles.Clear();
1500 tableLayoutPanel.ColumnStyles.Clear();
1501 tableLayoutPanel.RowCount = 0;
1502 tableLayoutPanel.ColumnCount = 0;
1504 while (tableLayoutPanel.RowCount < aRow)
1506 tableLayoutPanel.RowCount++;
1509 while (tableLayoutPanel.ColumnCount < aColumn)
1511 tableLayoutPanel.ColumnCount++;
1514 for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
1516 //Create our column styles
1517 this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100 / tableLayoutPanel.ColumnCount));
1519 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
1523 //Create our row styles
1524 this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100 / tableLayoutPanel.RowCount));
1527 MarqueeLabel control = new SharpDisplayManager.MarqueeLabel();
1528 control.AutoEllipsis = true;
1529 control.AutoSize = true;
1530 control.BackColor = System.Drawing.Color.Transparent;
1531 control.Dock = System.Windows.Forms.DockStyle.Fill;
1532 control.Location = new System.Drawing.Point(1, 1);
1533 control.Margin = new System.Windows.Forms.Padding(0);
1534 control.Name = "marqueeLabelCol" + aColumn + "Row" + aRow;
1535 control.OwnTimer = false;
1536 control.PixelsPerSecond = 64;
1537 control.Separator = cds.Separator;
1538 control.MinFontSize = cds.MinFontSize;
1539 control.ScaleToFit = cds.ScaleToFit;
1540 //control.Size = new System.Drawing.Size(254, 30);
1541 //control.TabIndex = 2;
1542 control.Font = cds.Font;
1543 control.Text = "ABCDEFGHIJKLMNOPQRST-0123456789";
1544 control.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
1545 control.UseCompatibleTextRendering = true;
1547 tableLayoutPanel.Controls.Add(control, i, j);
1556 /// Update our display table layout.
1558 /// <param name="aLayout"></param>
1559 private void UpdateTableLayoutPanel(ClientData aClient)
1561 if (aClient == null)
1568 TableLayout layout = aClient.Layout;
1571 tableLayoutPanel.Controls.Clear();
1572 tableLayoutPanel.RowStyles.Clear();
1573 tableLayoutPanel.ColumnStyles.Clear();
1574 tableLayoutPanel.RowCount = 0;
1575 tableLayoutPanel.ColumnCount = 0;
1577 while (tableLayoutPanel.RowCount < layout.Rows.Count)
1579 tableLayoutPanel.RowCount++;
1582 while (tableLayoutPanel.ColumnCount < layout.Columns.Count)
1584 tableLayoutPanel.ColumnCount++;
1587 for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
1589 //Create our column styles
1590 this.tableLayoutPanel.ColumnStyles.Add(layout.Columns[i]);
1592 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
1596 //Create our row styles
1597 this.tableLayoutPanel.RowStyles.Add(layout.Rows[j]);
1600 //Check if we already have a control
1601 Control existingControl = tableLayoutPanel.GetControlFromPosition(i,j);
1602 if (existingControl!=null)
1604 //We already have a control in that cell as a results of row/col spanning
1605 //Move on to next cell then
1611 //Check if a client field already exists for that cell
1612 if (aClient.Fields.Count <= tableLayoutPanel.Controls.Count)
1614 //No client field specified, create a text field by default
1615 aClient.Fields.Add(new DataField(aClient.Fields.Count));
1618 //Create a control corresponding to the field specified for that cell
1619 DataField field = aClient.Fields[tableLayoutPanel.Controls.Count];
1620 Control control = CreateControlForDataField(field);
1622 //Add newly created control to our table layout at the specified row and column
1623 tableLayoutPanel.Controls.Add(control, i, j);
1624 //Make sure we specify row and column span for that new control
1625 tableLayoutPanel.SetRowSpan(control,field.RowSpan);
1626 tableLayoutPanel.SetColumnSpan(control, field.ColumnSpan);
1631 while (aClient.Fields.Count > fieldCount)
1633 //We have too much fields for this layout
1634 //Just discard them until we get there
1635 aClient.Fields.RemoveAt(aClient.Fields.Count-1);
1642 /// Check our type of data field and create corresponding control
1644 /// <param name="aField"></param>
1645 private Control CreateControlForDataField(DataField aField)
1647 Control control=null;
1648 if (!aField.IsBitmap)
1650 MarqueeLabel label = new SharpDisplayManager.MarqueeLabel();
1651 label.AutoEllipsis = true;
1652 label.AutoSize = true;
1653 label.BackColor = System.Drawing.Color.Transparent;
1654 label.Dock = System.Windows.Forms.DockStyle.Fill;
1655 label.Location = new System.Drawing.Point(1, 1);
1656 label.Margin = new System.Windows.Forms.Padding(0);
1657 label.Name = "marqueeLabel" + aField.Index;
1658 label.OwnTimer = false;
1659 label.PixelsPerSecond = cds.ScrollingSpeedInPixelsPerSecond;
1660 label.Separator = cds.Separator;
1661 label.MinFontSize = cds.MinFontSize;
1662 label.ScaleToFit = cds.ScaleToFit;
1663 //control.Size = new System.Drawing.Size(254, 30);
1664 //control.TabIndex = 2;
1665 label.Font = cds.Font;
1667 label.TextAlign = aField.Alignment;
1668 label.UseCompatibleTextRendering = true;
1669 label.Text = aField.Text;
1675 //Create picture box
1676 PictureBox picture = new PictureBox();
1677 picture.AutoSize = true;
1678 picture.BackColor = System.Drawing.Color.Transparent;
1679 picture.Dock = System.Windows.Forms.DockStyle.Fill;
1680 picture.Location = new System.Drawing.Point(1, 1);
1681 picture.Margin = new System.Windows.Forms.Padding(0);
1682 picture.Name = "pictureBox" + aField;
1684 picture.Image = aField.Bitmap;
1693 /// Called when the user selected a new display type.
1695 /// <param name="sender"></param>
1696 /// <param name="e"></param>
1697 private void comboBoxDisplayType_SelectedIndexChanged(object sender, EventArgs e)
1699 //Store the selected display type in our settings
1700 Properties.Settings.Default.CurrentDisplayIndex = comboBoxDisplayType.SelectedIndex;
1701 cds.DisplayType = comboBoxDisplayType.SelectedIndex;
1702 Properties.Settings.Default.Save();
1704 //Try re-opening the display connection if we were already connected.
1705 //Otherwise just update our status to reflect display type change.
1706 if (iDisplay.IsOpen())
1708 OpenDisplayConnection();
1716 private void maskedTextBoxTimerInterval_TextChanged(object sender, EventArgs e)
1718 if (maskedTextBoxTimerInterval.Text != "")
1720 int interval = Convert.ToInt32(maskedTextBoxTimerInterval.Text);
1724 timer.Interval = interval;
1725 cds.TimerInterval = timer.Interval;
1726 Properties.Settings.Default.Save();
1731 private void maskedTextBoxMinFontSize_TextChanged(object sender, EventArgs e)
1733 if (maskedTextBoxMinFontSize.Text != "")
1735 int minFontSize = Convert.ToInt32(maskedTextBoxMinFontSize.Text);
1737 if (minFontSize > 0)
1739 cds.MinFontSize = minFontSize;
1740 Properties.Settings.Default.Save();
1741 //We need to recreate our layout for that change to take effect
1742 UpdateTableLayoutPanel(iCurrentClientData);
1748 private void maskedTextBoxScrollingSpeed_TextChanged(object sender, EventArgs e)
1750 if (maskedTextBoxScrollingSpeed.Text != "")
1752 int scrollingSpeed = Convert.ToInt32(maskedTextBoxScrollingSpeed.Text);
1754 if (scrollingSpeed > 0)
1756 cds.ScrollingSpeedInPixelsPerSecond = scrollingSpeed;
1757 Properties.Settings.Default.Save();
1758 //We need to recreate our layout for that change to take effect
1759 UpdateTableLayoutPanel(iCurrentClientData);
1764 private void textBoxScrollLoopSeparator_TextChanged(object sender, EventArgs e)
1766 cds.Separator = textBoxScrollLoopSeparator.Text;
1767 Properties.Settings.Default.Save();
1769 //Update our text fields
1770 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
1772 ctrl.Separator = cds.Separator;
1777 private void buttonPowerOn_Click(object sender, EventArgs e)
1782 private void buttonPowerOff_Click(object sender, EventArgs e)
1784 iDisplay.PowerOff();
1787 private void buttonShowClock_Click(object sender, EventArgs e)
1789 iDisplay.ShowClock();
1792 private void buttonHideClock_Click(object sender, EventArgs e)
1794 iDisplay.HideClock();
1797 private void buttonUpdate_Click(object sender, EventArgs e)
1799 InstallUpdateSyncWithInfo();
1803 private void InstallUpdateSyncWithInfo()
1805 UpdateCheckInfo info = null;
1807 if (ApplicationDeployment.IsNetworkDeployed)
1809 ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
1813 info = ad.CheckForDetailedUpdate();
1816 catch (DeploymentDownloadException dde)
1818 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);
1821 catch (InvalidDeploymentException ide)
1823 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);
1826 catch (InvalidOperationException ioe)
1828 MessageBox.Show("This application cannot be updated. It is likely not a ClickOnce application. Error: " + ioe.Message);
1832 if (info.UpdateAvailable)
1834 Boolean doUpdate = true;
1836 if (!info.IsUpdateRequired)
1838 DialogResult dr = MessageBox.Show("An update is available. Would you like to update the application now?", "Update Available", MessageBoxButtons.OKCancel);
1839 if (!(DialogResult.OK == dr))
1846 // Display a message that the app MUST reboot. Display the minimum required version.
1847 MessageBox.Show("This application has detected a mandatory update from your current " +
1848 "version to version " + info.MinimumRequiredVersion.ToString() +
1849 ". The application will now install the update and restart.",
1850 "Update Available", MessageBoxButtons.OK,
1851 MessageBoxIcon.Information);
1859 MessageBox.Show("The application has been upgraded, and will now restart.");
1860 Application.Restart();
1862 catch (DeploymentDownloadException dde)
1864 MessageBox.Show("Cannot install the latest version of the application. \n\nPlease check your network connection, or try again later. Error: " + dde);
1871 MessageBox.Show("You are already running the latest version.", "Application up-to-date");
1880 private void SysTrayHideShow()
1886 WindowState = FormWindowState.Normal;
1891 /// Use to handle minimize events.
1893 /// <param name="sender"></param>
1894 /// <param name="e"></param>
1895 private void MainForm_SizeChanged(object sender, EventArgs e)
1897 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
1910 /// <param name="sender"></param>
1911 /// <param name="e"></param>
1912 private void tableLayoutPanel_SizeChanged(object sender, EventArgs e)
1914 //Our table layout size has changed which means our display size has changed.
1915 //We need to re-create our bitmap.
1916 iCreateBitmap = true;
1922 /// A UI thread copy of a client relevant data.
1923 /// Keeping this copy in the UI thread helps us deal with threading issues.
1925 public class ClientData
1927 public ClientData(string aSessionId, ICallback aCallback)
1929 SessionId = aSessionId;
1931 Fields = new List<DataField>();
1932 Layout = new TableLayout(1, 2); //Default to one column and two rows
1933 Callback = aCallback;
1936 public string SessionId { get; set; }
1937 public string Name { get; set; }
1938 public List<DataField> Fields { get; set; }
1939 public TableLayout Layout { get; set; }
1940 public ICallback Callback { get; set; }