Server/MainForm.cs
author sl
Thu, 15 Jan 2015 19:18:25 +0100
changeset 88 4f09ae97cdcc
parent 79 76564df23849
child 90 aa157e129c42
permissions -rw-r--r--
Adding application update button.
Adding manifest to run as admin.
Disabling ClickOnce security check to run as admin.
The whole thing is not tested.
     1 ´╗┐using System;
     2 using System.Collections.Generic;
     3 using System.ComponentModel;
     4 using System.Data;
     5 using System.Drawing;
     6 using System.Linq;
     7 using System.Text;
     8 using System.Threading.Tasks;
     9 using System.Windows.Forms;
    10 using System.IO;
    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 //
    18 using SharpDisplayClient;
    19 using SharpDisplay;
    20 
    21 namespace SharpDisplayManager
    22 {
    23     //Types declarations
    24     public delegate uint ColorProcessingDelegate(int aX, int aY, uint aPixel);
    25     public delegate int CoordinateTranslationDelegate(System.Drawing.Bitmap aBmp, int aInt);
    26     //Delegates are used for our thread safe method
    27     public delegate void AddClientDelegate(string aSessionId, ICallback aCallback);
    28     public delegate void RemoveClientDelegate(string aSessionId);
    29     public delegate void SetFieldDelegate(string SessionId, DataField aField);
    30     public delegate void SetFieldsDelegate(string SessionId, System.Collections.Generic.IList<DataField> aFields);
    31     public delegate void SetLayoutDelegate(string SessionId, TableLayout aLayout);
    32     public delegate void SetClientNameDelegate(string aSessionId, string aName);
    33 
    34 
    35     /// <summary>
    36     /// Our Display manager main form
    37     /// </summary>
    38     public partial class MainForm : Form
    39     {
    40 
    41         DateTime LastTickTime;
    42         Display iDisplay;
    43         System.Drawing.Bitmap iBmp;
    44         bool iCreateBitmap; //Workaround render to bitmap issues when minimized
    45         ServiceHost iServiceHost;
    46         // Our collection of clients sorted by session id.
    47         public Dictionary<string, ClientData> iClients;
    48         // The name of the client which informations are currently displayed.
    49         public string iCurrentClientSessionId;
    50         ClientData iCurrentClientData;
    51         //
    52         public bool iClosing;
    53         //Function pointer for pixel color filtering
    54         ColorProcessingDelegate iColorFx;
    55         //Function pointer for pixel X coordinate intercept
    56         CoordinateTranslationDelegate iScreenX;
    57         //Function pointer for pixel Y coordinate intercept
    58         CoordinateTranslationDelegate iScreenY;
    59 
    60         public MainForm()
    61         {
    62             iCurrentClientSessionId = "";
    63             iCurrentClientData = null;
    64             LastTickTime = DateTime.Now;
    65             iDisplay = new Display();
    66             iClients = new Dictionary<string, ClientData>();
    67 
    68             InitializeComponent();
    69             UpdateStatus();
    70             //We have a bug when drawing minimized and reusing our bitmap
    71             iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
    72             iCreateBitmap = false;
    73         }
    74 
    75         private void MainForm_Load(object sender, EventArgs e)
    76         {
    77             StartServer();
    78 
    79             if (Properties.Settings.Default.DisplayConnectOnStartup)
    80             {
    81                 OpenDisplayConnection();
    82             }
    83         }
    84 
    85         /// <summary>
    86         /// Set our current client.
    87         /// This will take care of applying our client layout and set data fields.
    88         /// </summary>
    89         /// <param name="aSessionId"></param>
    90         void SetCurrentClient(string aSessionId)
    91         {
    92             if (aSessionId == iCurrentClientSessionId)
    93             {
    94                 //Given client is already the current one.
    95                 //Don't bother changing anything then.
    96                 return;
    97             }
    98 
    99             //Set current client ID.
   100             iCurrentClientSessionId = aSessionId;
   101             //Fetch and set current client data.
   102             iCurrentClientData = iClients[aSessionId];
   103             //Apply layout and set data fields.
   104             UpdateTableLayoutPanel(iCurrentClientData);
   105         }
   106 
   107         private void buttonFont_Click(object sender, EventArgs e)
   108         {
   109             //fontDialog.ShowColor = true;
   110             //fontDialog.ShowApply = true;
   111             fontDialog.ShowEffects = true;
   112             MarqueeLabel label = (MarqueeLabel)tableLayoutPanel.Controls[0];
   113             fontDialog.Font = label.Font;
   114 
   115             fontDialog.FixedPitchOnly = checkBoxFixedPitchFontOnly.Checked;
   116 
   117             //fontDialog.ShowHelp = true;
   118 
   119             //fontDlg.MaxSize = 40;
   120             //fontDlg.MinSize = 22;
   121 
   122             //fontDialog.Parent = this;
   123             //fontDialog.StartPosition = FormStartPosition.CenterParent;
   124 
   125             //DlgBox.ShowDialog(fontDialog);
   126 
   127             //if (fontDialog.ShowDialog(this) != DialogResult.Cancel)
   128             if (DlgBox.ShowDialog(fontDialog) != DialogResult.Cancel)
   129             {
   130 
   131                 //MsgBox.Show("MessageBox MsgBox", "MsgBox caption");
   132 
   133                 //MessageBox.Show("Ok");
   134                 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
   135                 {
   136                     ctrl.Font = fontDialog.Font;
   137                 }
   138                 cds.Font = fontDialog.Font;
   139                 Properties.Settings.Default.Save();
   140                 //
   141                 CheckFontHeight();
   142             }
   143         }
   144 
   145         /// <summary>
   146         ///
   147         /// </summary>
   148         void CheckFontHeight()
   149         {
   150             //Show font height and width
   151             labelFontHeight.Text = "Font height: " + cds.Font.Height;
   152             float charWidth = IsFixedWidth(cds.Font);
   153             if (charWidth == 0.0f)
   154             {
   155                 labelFontWidth.Visible = false;
   156             }
   157             else
   158             {
   159                 labelFontWidth.Visible = true;
   160                 labelFontWidth.Text = "Font width: " + charWidth;
   161             }
   162 
   163             MarqueeLabel label = null;
   164             //Get the first label control we can find
   165             foreach (Control ctrl in tableLayoutPanel.Controls)
   166             {
   167                 if (ctrl is MarqueeLabel)
   168                 {
   169                     label = (MarqueeLabel)ctrl;
   170                     break;
   171                 }
   172             }
   173 
   174             //Now check font height and show a warning if needed.
   175             if (label != null && label.Font.Height > label.Height)
   176             {
   177                 labelWarning.Text = "WARNING: Selected font is too height by " + (label.Font.Height - label.Height) + " pixels!";
   178                 labelWarning.Visible = true;
   179             }
   180             else
   181             {
   182                 labelWarning.Visible = false;
   183             }
   184 
   185         }
   186 
   187         private void buttonCapture_Click(object sender, EventArgs e)
   188         {
   189             System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height);
   190             tableLayoutPanel.DrawToBitmap(bmp, tableLayoutPanel.ClientRectangle);
   191             //Bitmap bmpToSave = new Bitmap(bmp);
   192             bmp.Save("D:\\capture.png");
   193 
   194             ((MarqueeLabel)tableLayoutPanel.Controls[0]).Text = "Captured";
   195 
   196             /*
   197             string outputFileName = "d:\\capture.png";
   198             using (MemoryStream memory = new MemoryStream())
   199             {
   200                 using (FileStream fs = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
   201                 {
   202                     bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
   203                     byte[] bytes = memory.ToArray();
   204                     fs.Write(bytes, 0, bytes.Length);
   205                 }
   206             }
   207              */
   208 
   209         }
   210 
   211         private void CheckForRequestResults()
   212         {
   213             if (iDisplay.IsRequestPending())
   214             {
   215                 switch (iDisplay.AttemptRequestCompletion())
   216                 {
   217                     case Display.TMiniDisplayRequest.EMiniDisplayRequestFirmwareRevision:
   218                         toolStripStatusLabelConnect.Text += " v" + iDisplay.FirmwareRevision();
   219                         //Issue next request then
   220                         iDisplay.RequestPowerSupplyStatus();
   221                         break;
   222 
   223                     case Display.TMiniDisplayRequest.EMiniDisplayRequestPowerSupplyStatus:
   224                         if (iDisplay.PowerSupplyStatus())
   225                         {
   226                             toolStripStatusLabelPower.Text = "ON";
   227                         }
   228                         else
   229                         {
   230                             toolStripStatusLabelPower.Text = "OFF";
   231                         }
   232                         //Issue next request then
   233                         iDisplay.RequestDeviceId();
   234                         break;
   235 
   236                     case Display.TMiniDisplayRequest.EMiniDisplayRequestDeviceId:
   237                         toolStripStatusLabelConnect.Text += " - " + iDisplay.DeviceId();
   238                         //No more request to issue
   239                         break;
   240                 }
   241             }
   242         }
   243 
   244         public static uint ColorWhiteIsOn(int aX, int aY, uint aPixel)
   245         {
   246             if ((aPixel & 0x00FFFFFF) == 0x00FFFFFF)
   247             {
   248                 return 0xFFFFFFFF;
   249             }
   250             return 0x00000000;
   251         }
   252 
   253         public static uint ColorUntouched(int aX, int aY, uint aPixel)
   254         {
   255             return aPixel;
   256         }
   257 
   258         public static uint ColorInversed(int aX, int aY, uint aPixel)
   259         {
   260             return ~aPixel;
   261         }
   262 
   263         public static uint ColorChessboard(int aX, int aY, uint aPixel)
   264         {
   265             if ((aX % 2 == 0) && (aY % 2 == 0))
   266             {
   267                 return ~aPixel;
   268             }
   269             else if ((aX % 2 != 0) && (aY % 2 != 0))
   270             {
   271                 return ~aPixel;
   272             }
   273             return 0x00000000;
   274         }
   275 
   276 
   277         public static int ScreenReversedX(System.Drawing.Bitmap aBmp, int aX)
   278         {
   279             return aBmp.Width - aX - 1;
   280         }
   281 
   282         public int ScreenReversedY(System.Drawing.Bitmap aBmp, int aY)
   283         {
   284             return iBmp.Height - aY - 1;
   285         }
   286 
   287         public int ScreenX(System.Drawing.Bitmap aBmp, int aX)
   288         {
   289             return aX;
   290         }
   291 
   292         public int ScreenY(System.Drawing.Bitmap aBmp, int aY)
   293         {
   294             return aY;
   295         }
   296 
   297         /// <summary>
   298         /// Select proper pixel delegates according to our current settings.
   299         /// </summary>
   300         private void SetupPixelDelegates()
   301         {
   302             //Select our pixel processing routine
   303             if (cds.InverseColors)
   304             {
   305                 //iColorFx = ColorChessboard;
   306                 iColorFx = ColorInversed;
   307             }
   308             else
   309             {
   310                 iColorFx = ColorWhiteIsOn;
   311             }
   312 
   313             //Select proper coordinate translation functions
   314             //We used delegate/function pointer to support reverse screen without doing an extra test on each pixels
   315             if (cds.ReverseScreen)
   316             {
   317                 iScreenX = ScreenReversedX;
   318                 iScreenY = ScreenReversedY;
   319             }
   320             else
   321             {
   322                 iScreenX = ScreenX;
   323                 iScreenY = ScreenY;
   324             }
   325 
   326         }
   327 
   328         //This is our timer tick responsible to perform our render
   329         private void timer_Tick(object sender, EventArgs e)
   330         {
   331             //Update our animations
   332             DateTime NewTickTime = DateTime.Now;
   333 
   334             //Update animation for all our marquees
   335             foreach (Control ctrl in tableLayoutPanel.Controls)
   336             {
   337                 if (ctrl is MarqueeLabel)
   338                 {
   339                     ((MarqueeLabel)ctrl).UpdateAnimation(LastTickTime, NewTickTime);
   340                 }
   341             }
   342 
   343 
   344             //Update our display
   345             if (iDisplay.IsOpen())
   346             {
   347                 CheckForRequestResults();
   348 
   349                 //Draw to bitmap
   350                 if (iCreateBitmap)
   351                 {
   352                     iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
   353                 }
   354                 tableLayoutPanel.DrawToBitmap(iBmp, tableLayoutPanel.ClientRectangle);
   355                 //iBmp.Save("D:\\capture.png");
   356 
   357                 //Send it to our display
   358                 for (int i = 0; i < iBmp.Width; i++)
   359                 {
   360                     for (int j = 0; j < iBmp.Height; j++)
   361                     {
   362                         unchecked
   363                         {
   364                             //Get our processed pixel coordinates
   365                             int x = iScreenX(iBmp, i);
   366                             int y = iScreenY(iBmp, j);
   367                             //Get pixel color
   368                             uint color = (uint)iBmp.GetPixel(i, j).ToArgb();
   369                             //Apply color effects
   370                             color = iColorFx(x,y,color);
   371                             //Now set our pixel
   372                             iDisplay.SetPixel(x, y, color);
   373                         }
   374                     }
   375                 }
   376 
   377                 iDisplay.SwapBuffers();
   378 
   379             }
   380 
   381             //Compute instant FPS
   382             toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " / " + (1000/timer.Interval).ToString() + " FPS";
   383 
   384             LastTickTime = NewTickTime;
   385 
   386         }
   387 
   388         private void OpenDisplayConnection()
   389         {
   390             CloseDisplayConnection();
   391 
   392             if (iDisplay.Open((Display.TMiniDisplayType)cds.DisplayType))
   393             {
   394                 UpdateStatus();
   395                 iDisplay.RequestFirmwareRevision();
   396             }
   397             else
   398             {
   399                 UpdateStatus();
   400                 toolStripStatusLabelConnect.Text = "Connection error";
   401             }
   402         }
   403 
   404         private void CloseDisplayConnection()
   405         {
   406             iDisplay.Close();
   407             UpdateStatus();
   408         }
   409 
   410         private void buttonOpen_Click(object sender, EventArgs e)
   411         {
   412             OpenDisplayConnection();
   413         }
   414 
   415         private void buttonClose_Click(object sender, EventArgs e)
   416         {
   417             CloseDisplayConnection();
   418         }
   419 
   420         private void buttonClear_Click(object sender, EventArgs e)
   421         {
   422             iDisplay.Clear();
   423             iDisplay.SwapBuffers();
   424         }
   425 
   426         private void buttonFill_Click(object sender, EventArgs e)
   427         {
   428             iDisplay.Fill();
   429             iDisplay.SwapBuffers();
   430         }
   431 
   432         private void trackBarBrightness_Scroll(object sender, EventArgs e)
   433         {
   434             cds.Brightness = trackBarBrightness.Value;
   435             Properties.Settings.Default.Save();
   436             iDisplay.SetBrightness(trackBarBrightness.Value);
   437 
   438         }
   439 
   440 
   441         /// <summary>
   442         /// CDS stands for Current Display Settings
   443         /// </summary>
   444         private DisplaySettings cds
   445         {
   446             get
   447             {
   448                 DisplaysSettings settings = Properties.Settings.Default.DisplaysSettings;
   449                 if (settings == null)
   450                 {
   451                     settings = new DisplaysSettings();
   452                     settings.Init();
   453                     Properties.Settings.Default.DisplaysSettings = settings;
   454                 }
   455 
   456                 //Make sure all our settings have been created
   457                 while (settings.Displays.Count <= Properties.Settings.Default.CurrentDisplayIndex)
   458                 {
   459                     settings.Displays.Add(new DisplaySettings());
   460                 }
   461 
   462                 DisplaySettings displaySettings = settings.Displays[Properties.Settings.Default.CurrentDisplayIndex];
   463                 return displaySettings;
   464             }
   465         }
   466 
   467         /// <summary>
   468         /// Check if the given font has a fixed character pitch.
   469         /// </summary>
   470         /// <param name="ft"></param>
   471         /// <returns>0.0f if this is not a monospace font, otherwise returns the character width.</returns>
   472         public float IsFixedWidth(Font ft)
   473         {
   474             Graphics g = CreateGraphics();
   475             char[] charSizes = new char[] { 'i', 'a', 'Z', '%', '#', 'a', 'B', 'l', 'm', ',', '.' };
   476             float charWidth = g.MeasureString("I", ft, Int32.MaxValue, StringFormat.GenericTypographic).Width;
   477 
   478             bool fixedWidth = true;
   479 
   480             foreach (char c in charSizes)
   481                 if (g.MeasureString(c.ToString(), ft, Int32.MaxValue, StringFormat.GenericTypographic).Width != charWidth)
   482                     fixedWidth = false;
   483 
   484             if (fixedWidth)
   485             {
   486                 return charWidth;
   487             }
   488 
   489             return 0.0f;
   490         }
   491 
   492         private void UpdateStatus()
   493         {
   494             //Synchronize UI with settings
   495             //Load settings
   496             checkBoxShowBorders.Checked = cds.ShowBorders;
   497             tableLayoutPanel.CellBorderStyle = (cds.ShowBorders ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
   498 
   499             //Set the proper font to each of our labels
   500             foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
   501             {
   502                 ctrl.Font = cds.Font;
   503             }
   504 
   505             CheckFontHeight();
   506             checkBoxConnectOnStartup.Checked = Properties.Settings.Default.DisplayConnectOnStartup;
   507             checkBoxReverseScreen.Checked = cds.ReverseScreen;
   508             checkBoxInverseColors.Checked = cds.InverseColors;
   509             comboBoxDisplayType.SelectedIndex = cds.DisplayType;
   510             timer.Interval = cds.TimerInterval;
   511             maskedTextBoxTimerInterval.Text = cds.TimerInterval.ToString();
   512             //
   513             SetupPixelDelegates();
   514 
   515             if (iDisplay.IsOpen())
   516             {
   517                 //Only setup brightness if display is open
   518                 trackBarBrightness.Minimum = iDisplay.MinBrightness();
   519                 trackBarBrightness.Maximum = iDisplay.MaxBrightness();
   520                 trackBarBrightness.Value = cds.Brightness;
   521                 trackBarBrightness.LargeChange = Math.Max(1, (iDisplay.MaxBrightness() - iDisplay.MinBrightness()) / 5);
   522                 trackBarBrightness.SmallChange = 1;
   523                 iDisplay.SetBrightness(cds.Brightness);
   524                 //
   525                 buttonFill.Enabled = true;
   526                 buttonClear.Enabled = true;
   527                 buttonOpen.Enabled = false;
   528                 buttonClose.Enabled = true;
   529                 trackBarBrightness.Enabled = true;
   530                 toolStripStatusLabelConnect.Text = "Connected - " + iDisplay.Vendor() + " - " + iDisplay.Product();
   531                 //+ " - " + iDisplay.SerialNumber();
   532 
   533                 if (iDisplay.SupportPowerOnOff())
   534                 {
   535                     buttonPowerOn.Enabled = true;
   536                     buttonPowerOff.Enabled = true;
   537                 }
   538                 else
   539                 {
   540                     buttonPowerOn.Enabled = false;
   541                     buttonPowerOff.Enabled = false;
   542                 }
   543 
   544                 if (iDisplay.SupportClock())
   545                 {
   546                     buttonShowClock.Enabled = true;
   547                     buttonHideClock.Enabled = true;
   548                 }
   549                 else
   550                 {
   551                     buttonShowClock.Enabled = false;
   552                     buttonHideClock.Enabled = false;
   553                 }
   554             }
   555             else
   556             {
   557                 buttonFill.Enabled = false;
   558                 buttonClear.Enabled = false;
   559                 buttonOpen.Enabled = true;
   560                 buttonClose.Enabled = false;
   561                 trackBarBrightness.Enabled = false;
   562                 buttonPowerOn.Enabled = false;
   563                 buttonPowerOff.Enabled = false;
   564                 buttonShowClock.Enabled = false;
   565                 buttonHideClock.Enabled = false;
   566                 toolStripStatusLabelConnect.Text = "Disconnected";
   567                 toolStripStatusLabelPower.Text = "N/A";
   568             }
   569         }
   570 
   571 
   572 
   573         private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e)
   574         {
   575             //Save our show borders setting
   576             tableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
   577             cds.ShowBorders = checkBoxShowBorders.Checked;
   578             Properties.Settings.Default.Save();
   579             CheckFontHeight();
   580         }
   581 
   582         private void checkBoxConnectOnStartup_CheckedChanged(object sender, EventArgs e)
   583         {
   584             //Save our connect on startup setting
   585             Properties.Settings.Default.DisplayConnectOnStartup = checkBoxConnectOnStartup.Checked;
   586             Properties.Settings.Default.Save();
   587         }
   588 
   589         private void checkBoxReverseScreen_CheckedChanged(object sender, EventArgs e)
   590         {
   591             //Save our reverse screen setting
   592             cds.ReverseScreen = checkBoxReverseScreen.Checked;
   593             Properties.Settings.Default.Save();
   594             SetupPixelDelegates();
   595         }
   596 
   597         private void checkBoxInverseColors_CheckedChanged(object sender, EventArgs e)
   598         {
   599             //Save our inverse colors setting
   600             cds.InverseColors = checkBoxInverseColors.Checked;
   601             Properties.Settings.Default.Save();
   602             SetupPixelDelegates();
   603         }
   604 
   605         private void MainForm_Resize(object sender, EventArgs e)
   606         {
   607             if (WindowState == FormWindowState.Minimized)
   608             {
   609                 // Do some stuff
   610                 //iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
   611                 iCreateBitmap = true;
   612             }
   613         }
   614 
   615         private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
   616         {
   617             StopServer();
   618             e.Cancel = iClosing;
   619         }
   620 
   621         public void StartServer()
   622         {
   623             iServiceHost = new ServiceHost
   624                 (
   625                     typeof(Session),
   626                     new Uri[] { new Uri("net.tcp://localhost:8001/") }
   627                 );
   628 
   629             iServiceHost.AddServiceEndpoint(typeof(IService), new NetTcpBinding(SecurityMode.None, true), "DisplayService");
   630             iServiceHost.Open();
   631         }
   632 
   633         public void StopServer()
   634         {
   635             if (iClients.Count > 0 && !iClosing)
   636             {
   637                 //Tell our clients
   638                 iClosing = true;
   639                 BroadcastCloseEvent();
   640             }
   641             else if (iClosing)
   642             {
   643                 if (MessageBox.Show("Force exit?", "Waiting for clients...", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
   644                 {
   645                     iClosing = false; //We make sure we force close if asked twice
   646                 }
   647             }
   648             else
   649             {
   650                 //We removed that as it often lags for some reason
   651                 //iServiceHost.Close();
   652             }
   653         }
   654 
   655         public void BroadcastCloseEvent()
   656         {
   657             Trace.TraceInformation("BroadcastCloseEvent - start");
   658 
   659             var inactiveClients = new List<string>();
   660             foreach (var client in iClients)
   661             {
   662                 //if (client.Key != eventData.ClientName)
   663                 {
   664                     try
   665                     {
   666                         Trace.TraceInformation("BroadcastCloseEvent - " + client.Key);
   667                         client.Value.Callback.OnCloseOrder(/*eventData*/);
   668                     }
   669                     catch (Exception ex)
   670                     {
   671                         inactiveClients.Add(client.Key);
   672                     }
   673                 }
   674             }
   675 
   676             if (inactiveClients.Count > 0)
   677             {
   678                 foreach (var client in inactiveClients)
   679                 {
   680                     iClients.Remove(client);
   681                     Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(client, false)[0]);
   682                 }
   683             }
   684         }
   685 
   686         private void buttonStartClient_Click(object sender, EventArgs e)
   687         {
   688             Thread clientThread = new Thread(SharpDisplayClient.Program.Main);
   689             clientThread.Start();
   690             BringToFront();
   691         }
   692 
   693         private void buttonSuspend_Click(object sender, EventArgs e)
   694         {
   695             LastTickTime = DateTime.Now; //Reset timer to prevent jump
   696             timer.Enabled = !timer.Enabled;
   697             if (!timer.Enabled)
   698             {
   699                 buttonSuspend.Text = "Run";
   700             }
   701             else
   702             {
   703                 buttonSuspend.Text = "Pause";
   704             }
   705         }
   706 
   707         private void buttonCloseClients_Click(object sender, EventArgs e)
   708         {
   709             BroadcastCloseEvent();
   710         }
   711 
   712         private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e)
   713         {
   714 
   715         }
   716 
   717 
   718         /// <summary>
   719         ///
   720         /// </summary>
   721         /// <param name="aSessionId"></param>
   722         /// <param name="aCallback"></param>
   723         public void AddClientThreadSafe(string aSessionId, ICallback aCallback)
   724         {
   725             if (this.InvokeRequired)
   726             {
   727                 //Not in the proper thread, invoke ourselves
   728                 AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe);
   729                 this.Invoke(d, new object[] { aSessionId, aCallback });
   730             }
   731             else
   732             {
   733                 //We are in the proper thread
   734                 //Add this session to our collection of clients
   735                 ClientData newClient = new ClientData(aSessionId, aCallback);
   736                 Program.iMainForm.iClients.Add(aSessionId, newClient);
   737                 //Add this session to our UI
   738                 UpdateClientTreeViewNode(newClient);
   739             }
   740         }
   741 
   742         /// <summary>
   743         ///
   744         /// </summary>
   745         /// <param name="aSessionId"></param>
   746         public void RemoveClientThreadSafe(string aSessionId)
   747         {
   748             if (this.InvokeRequired)
   749             {
   750                 //Not in the proper thread, invoke ourselves
   751                 RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe);
   752                 this.Invoke(d, new object[] { aSessionId });
   753             }
   754             else
   755             {
   756                 //We are in the proper thread
   757                 //Remove this session from both client collection and UI tree view
   758                 if (Program.iMainForm.iClients.Keys.Contains(aSessionId))
   759                 {
   760                     Program.iMainForm.iClients.Remove(aSessionId);
   761                     Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(aSessionId, false)[0]);
   762                 }
   763 
   764                 if (iClosing && iClients.Count == 0)
   765                 {
   766                     //We were closing our form
   767                     //All clients are now closed
   768                     //Just resume our close operation
   769                     iClosing = false;
   770                     Close();
   771                 }
   772             }
   773         }
   774 
   775         /// <summary>
   776         ///
   777         /// </summary>
   778         /// <param name="aSessionId"></param>
   779         /// <param name="aLayout"></param>
   780         public void SetClientLayoutThreadSafe(string aSessionId, TableLayout aLayout)
   781         {
   782             if (this.InvokeRequired)
   783             {
   784                 //Not in the proper thread, invoke ourselves
   785                 SetLayoutDelegate d = new SetLayoutDelegate(SetClientLayoutThreadSafe);
   786                 this.Invoke(d, new object[] { aSessionId, aLayout });
   787             }
   788             else
   789             {
   790                 ClientData client = iClients[aSessionId];
   791                 if (client != null)
   792                 {
   793                     client.Layout = aLayout;
   794                     UpdateTableLayoutPanel(client);
   795                     //
   796                     UpdateClientTreeViewNode(client);
   797                 }
   798             }
   799         }
   800 
   801         /// <summary>
   802         ///
   803         /// </summary>
   804         /// <param name="aSessionId"></param>
   805         /// <param name="aField"></param>
   806         public void SetClientFieldThreadSafe(string aSessionId, DataField aField)
   807         {
   808             if (this.InvokeRequired)
   809             {
   810                 //Not in the proper thread, invoke ourselves
   811                 SetFieldDelegate d = new SetFieldDelegate(SetClientFieldThreadSafe);
   812                 this.Invoke(d, new object[] { aSessionId, aField });
   813             }
   814             else
   815             {
   816                 //We are in the proper thread
   817                 //Call the non-thread-safe variant
   818                 SetClientField(aSessionId, aField);
   819             }
   820         }
   821 
   822         /// <summary>
   823         ///
   824         /// </summary>
   825         /// <param name="aSessionId"></param>
   826         /// <param name="aField"></param>
   827         private void SetClientField(string aSessionId, DataField aField)
   828         {
   829             SetCurrentClient(aSessionId);
   830             ClientData client = iClients[aSessionId];
   831             if (client != null)
   832             {
   833                 bool somethingChanged = false;
   834 
   835                 //Make sure all our fields are in place
   836                 while (client.Fields.Count < (aField.Index + 1))
   837                 {
   838                     //Add a text field with proper index
   839                     client.Fields.Add(new DataField(client.Fields.Count));
   840                     somethingChanged = true;
   841                 }
   842 
   843                 if (client.Fields[aField.Index].IsSameLayout(aField))
   844                 {
   845                     //Same layout just update our field
   846                     client.Fields[aField.Index] = aField;
   847                     //
   848                     if (aField.IsText && tableLayoutPanel.Controls[aField.Index] is MarqueeLabel)
   849                     {
   850                         //Text field control already in place, just change the text
   851                         MarqueeLabel label = (MarqueeLabel)tableLayoutPanel.Controls[aField.Index];
   852                         somethingChanged = (label.Text != aField.Text || label.TextAlign != aField.Alignment);
   853                         label.Text = aField.Text;
   854                         label.TextAlign = aField.Alignment;
   855                     }
   856                     else if (aField.IsBitmap && tableLayoutPanel.Controls[aField.Index] is PictureBox)
   857                     {
   858                         somethingChanged = true; //TODO: Bitmap comp or should we leave that to clients?
   859                         //Bitmap field control already in place just change the bitmap
   860                         PictureBox pictureBox = (PictureBox)tableLayoutPanel.Controls[aField.Index];
   861                         pictureBox.Image = aField.Bitmap;
   862                     }
   863                     else
   864                     {
   865                         somethingChanged = true;
   866                         //The requested control in our layout it not of the correct type
   867                         //Wrong control type, re-create them all
   868                         UpdateTableLayoutPanel(iCurrentClientData);
   869                     }
   870                 }
   871                 else
   872                 {
   873                     somethingChanged = true;
   874                     //Different layout, need to rebuild it
   875                     client.Fields[aField.Index] = aField;
   876                     UpdateTableLayoutPanel(iCurrentClientData);
   877                 }
   878 
   879                 //
   880                 if (somethingChanged)
   881                 {
   882                     UpdateClientTreeViewNode(client);
   883                 }
   884             }
   885         }
   886 
   887         /// <summary>
   888         ///
   889         /// </summary>
   890         /// <param name="aTexts"></param>
   891         public void SetClientFieldsThreadSafe(string aSessionId, System.Collections.Generic.IList<DataField> aFields)
   892         {
   893             if (this.InvokeRequired)
   894             {
   895                 //Not in the proper thread, invoke ourselves
   896                 SetFieldsDelegate d = new SetFieldsDelegate(SetClientFieldsThreadSafe);
   897                 this.Invoke(d, new object[] { aSessionId, aFields });
   898             }
   899             else
   900             {
   901                 //Put each our text fields in a label control
   902                 foreach (DataField field in aFields)
   903                 {
   904                     SetClientField(aSessionId, field);
   905                 }
   906             }
   907         }
   908 
   909         /// <summary>
   910         ///
   911         /// </summary>
   912         /// <param name="aSessionId"></param>
   913         /// <param name="aName"></param>
   914         public void SetClientNameThreadSafe(string aSessionId, string aName)
   915         {
   916             if (this.InvokeRequired)
   917             {
   918                 //Not in the proper thread, invoke ourselves
   919                 SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe);
   920                 this.Invoke(d, new object[] { aSessionId, aName });
   921             }
   922             else
   923             {
   924                 //We are in the proper thread
   925                 //Get our client
   926                 ClientData client = iClients[aSessionId];
   927                 if (client != null)
   928                 {
   929                     //Set its name
   930                     client.Name = aName;
   931                     //Update our tree-view
   932                     UpdateClientTreeViewNode(client);
   933                 }
   934             }
   935         }
   936 
   937         /// <summary>
   938         ///
   939         /// </summary>
   940         /// <param name="aClient"></param>
   941         private void UpdateClientTreeViewNode(ClientData aClient)
   942         {
   943             if (aClient == null)
   944             {
   945                 return;
   946             }
   947 
   948             TreeNode node = null;
   949             //Check that our client node already exists
   950             //Get our client root node using its key which is our session ID
   951             TreeNode[] nodes = treeViewClients.Nodes.Find(aClient.SessionId, false);
   952             if (nodes.Count()>0)
   953             {
   954                 //We already have a node for that client
   955                 node = nodes[0];
   956                 //Clear children as we are going to recreate them below
   957                 node.Nodes.Clear();
   958             }
   959             else
   960             {
   961                 //Client node does not exists create a new one
   962                 treeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId);
   963                 node = treeViewClients.Nodes.Find(aClient.SessionId, false)[0];
   964             }
   965 
   966             if (node != null)
   967             {
   968                 //Change its name
   969                 if (aClient.Name != "")
   970                 {
   971                     //We have a name, us it as text for our root node
   972                     node.Text = aClient.Name;
   973                     //Add a child with SessionId
   974                     node.Nodes.Add(new TreeNode(aClient.SessionId));
   975                 }
   976                 else
   977                 {
   978                     //No name, use session ID instead
   979                     node.Text = aClient.SessionId;
   980                 }
   981 
   982                 if (aClient.Fields.Count > 0)
   983                 {
   984                     //Create root node for our texts
   985                     TreeNode textsRoot = new TreeNode("Fields");
   986                     node.Nodes.Add(textsRoot);
   987                     //For each text add a new entry
   988                     foreach (DataField field in aClient.Fields)
   989                     {
   990                         if (!field.IsBitmap)
   991                         {
   992                             DataField textField = (DataField)field;
   993                             textsRoot.Nodes.Add(new TreeNode("[Text]" + textField.Text));
   994                         }
   995                         else
   996                         {
   997                             textsRoot.Nodes.Add(new TreeNode("[Bitmap]"));
   998                         }
   999                     }
  1000                 }
  1001 
  1002                 node.ExpandAll();
  1003             }
  1004         }
  1005 
  1006         private void buttonAddRow_Click(object sender, EventArgs e)
  1007         {
  1008             if (tableLayoutPanel.RowCount < 6)
  1009             {
  1010                 UpdateTableLayoutPanel(tableLayoutPanel.ColumnCount, tableLayoutPanel.RowCount + 1);
  1011             }
  1012         }
  1013 
  1014         private void buttonRemoveRow_Click(object sender, EventArgs e)
  1015         {
  1016             if (tableLayoutPanel.RowCount > 1)
  1017             {
  1018                 UpdateTableLayoutPanel(tableLayoutPanel.ColumnCount, tableLayoutPanel.RowCount - 1);
  1019             }
  1020 
  1021             UpdateTableLayoutRowStyles();
  1022         }
  1023 
  1024         private void buttonAddColumn_Click(object sender, EventArgs e)
  1025         {
  1026             if (tableLayoutPanel.ColumnCount < 8)
  1027             {
  1028                 UpdateTableLayoutPanel(tableLayoutPanel.ColumnCount + 1, tableLayoutPanel.RowCount);
  1029             }
  1030         }
  1031 
  1032         private void buttonRemoveColumn_Click(object sender, EventArgs e)
  1033         {
  1034             if (tableLayoutPanel.ColumnCount > 1)
  1035             {
  1036                 UpdateTableLayoutPanel(tableLayoutPanel.ColumnCount - 1, tableLayoutPanel.RowCount);
  1037             }
  1038         }
  1039 
  1040 
  1041         /// <summary>
  1042         /// Update our table layout row styles to make sure each rows have similar height
  1043         /// </summary>
  1044         private void UpdateTableLayoutRowStyles()
  1045         {
  1046             foreach (RowStyle rowStyle in tableLayoutPanel.RowStyles)
  1047             {
  1048                 rowStyle.SizeType = SizeType.Percent;
  1049                 rowStyle.Height = 100 / tableLayoutPanel.RowCount;
  1050             }
  1051         }
  1052 
  1053         /// DEPRECATED
  1054         /// <summary>
  1055         /// Empty and recreate our table layout with the given number of columns and rows.
  1056         /// Sizes of rows and columns are uniform.
  1057         /// </summary>
  1058         /// <param name="aColumn"></param>
  1059         /// <param name="aRow"></param>
  1060         private void UpdateTableLayoutPanel(int aColumn, int aRow)
  1061         {
  1062             tableLayoutPanel.Controls.Clear();
  1063             tableLayoutPanel.RowStyles.Clear();
  1064             tableLayoutPanel.ColumnStyles.Clear();
  1065             tableLayoutPanel.RowCount = 0;
  1066             tableLayoutPanel.ColumnCount = 0;
  1067 
  1068             while (tableLayoutPanel.RowCount < aRow)
  1069             {
  1070                 tableLayoutPanel.RowCount++;
  1071             }
  1072 
  1073             while (tableLayoutPanel.ColumnCount < aColumn)
  1074             {
  1075                 tableLayoutPanel.ColumnCount++;
  1076             }
  1077 
  1078             for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
  1079             {
  1080                 //Create our column styles
  1081                 this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100 / tableLayoutPanel.ColumnCount));
  1082 
  1083                 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
  1084                 {
  1085                     if (i == 0)
  1086                     {
  1087                         //Create our row styles
  1088                         this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100 / tableLayoutPanel.RowCount));
  1089                     }
  1090 
  1091                     MarqueeLabel control = new SharpDisplayManager.MarqueeLabel();
  1092                     control.AutoEllipsis = true;
  1093                     control.AutoSize = true;
  1094                     control.BackColor = System.Drawing.Color.Transparent;
  1095                     control.Dock = System.Windows.Forms.DockStyle.Fill;
  1096                     control.Location = new System.Drawing.Point(1, 1);
  1097                     control.Margin = new System.Windows.Forms.Padding(0);
  1098                     control.Name = "marqueeLabelCol" + aColumn + "Row" + aRow;
  1099                     control.OwnTimer = false;
  1100                     control.PixelsPerSecond = 64;
  1101                     control.Separator = "|";
  1102                     //control.Size = new System.Drawing.Size(254, 30);
  1103                     //control.TabIndex = 2;
  1104                     control.Font = cds.Font;
  1105                     control.Text = "ABCDEFGHIJKLMNOPQRST-0123456789";
  1106                     control.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
  1107                     control.UseCompatibleTextRendering = true;
  1108                     //
  1109                     tableLayoutPanel.Controls.Add(control, i, j);
  1110                 }
  1111             }
  1112 
  1113             CheckFontHeight();
  1114         }
  1115 
  1116 
  1117         /// <summary>
  1118         /// Update our display table layout.
  1119         /// </summary>
  1120         /// <param name="aLayout"></param>
  1121         private void UpdateTableLayoutPanel(ClientData aClient)
  1122         {
  1123             TableLayout layout = aClient.Layout;
  1124             int fieldCount = 0;
  1125 
  1126             tableLayoutPanel.Controls.Clear();
  1127             tableLayoutPanel.RowStyles.Clear();
  1128             tableLayoutPanel.ColumnStyles.Clear();
  1129             tableLayoutPanel.RowCount = 0;
  1130             tableLayoutPanel.ColumnCount = 0;
  1131 
  1132             while (tableLayoutPanel.RowCount < layout.Rows.Count)
  1133             {
  1134                 tableLayoutPanel.RowCount++;
  1135             }
  1136 
  1137             while (tableLayoutPanel.ColumnCount < layout.Columns.Count)
  1138             {
  1139                 tableLayoutPanel.ColumnCount++;
  1140             }
  1141 
  1142             for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
  1143             {
  1144                 //Create our column styles
  1145                 this.tableLayoutPanel.ColumnStyles.Add(layout.Columns[i]);
  1146 
  1147                 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
  1148                 {
  1149                     if (i == 0)
  1150                     {
  1151                         //Create our row styles
  1152                         this.tableLayoutPanel.RowStyles.Add(layout.Rows[j]);
  1153                     }
  1154 
  1155                     //Check if we already have a control
  1156                     Control existingControl = tableLayoutPanel.GetControlFromPosition(i,j);
  1157                     if (existingControl!=null)
  1158                     {
  1159                         //We already have a control in that cell as a results of row/col spanning
  1160                         //Move on to next cell then
  1161                         continue;
  1162                     }
  1163 
  1164                     fieldCount++;
  1165 
  1166                     //Check if a client field already exists for that cell
  1167                     if (aClient.Fields.Count <= tableLayoutPanel.Controls.Count)
  1168                     {
  1169                         //No client field specified, create a text field by default
  1170                         aClient.Fields.Add(new DataField(aClient.Fields.Count));
  1171                     }
  1172 
  1173                     //Create a control corresponding to the field specified for that cell
  1174                     DataField field = aClient.Fields[tableLayoutPanel.Controls.Count];
  1175                     Control control = CreateControlForDataField(field);
  1176 
  1177                     //Add newly created control to our table layout at the specified row and column
  1178                     tableLayoutPanel.Controls.Add(control, i, j);
  1179                     //Make sure we specify row and column span for that new control
  1180                     tableLayoutPanel.SetRowSpan(control,field.RowSpan);
  1181                     tableLayoutPanel.SetColumnSpan(control, field.ColumnSpan);
  1182                 }
  1183             }
  1184 
  1185             //
  1186             while (aClient.Fields.Count > fieldCount)
  1187             {
  1188                 //We have too much fields for this layout
  1189                 //Just discard them until we get there
  1190                 aClient.Fields.RemoveAt(aClient.Fields.Count-1);
  1191             }
  1192 
  1193             CheckFontHeight();
  1194         }
  1195 
  1196         /// <summary>
  1197         /// Check our type of data field and create corresponding control
  1198         /// </summary>
  1199         /// <param name="aField"></param>
  1200         private Control CreateControlForDataField(DataField aField)
  1201         {
  1202             Control control=null;
  1203             if (!aField.IsBitmap)
  1204             {
  1205                 MarqueeLabel label = new SharpDisplayManager.MarqueeLabel();
  1206                 label.AutoEllipsis = true;
  1207                 label.AutoSize = true;
  1208                 label.BackColor = System.Drawing.Color.Transparent;
  1209                 label.Dock = System.Windows.Forms.DockStyle.Fill;
  1210                 label.Location = new System.Drawing.Point(1, 1);
  1211                 label.Margin = new System.Windows.Forms.Padding(0);
  1212                 label.Name = "marqueeLabel" + aField.Index;
  1213                 label.OwnTimer = false;
  1214                 label.PixelsPerSecond = 64;
  1215                 label.Separator = "|";
  1216                 //control.Size = new System.Drawing.Size(254, 30);
  1217                 //control.TabIndex = 2;
  1218                 label.Font = cds.Font;
  1219 
  1220                 label.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
  1221                 label.UseCompatibleTextRendering = true;
  1222                 label.Text = aField.Text;
  1223                 //
  1224                 control = label;
  1225             }
  1226             else
  1227             {
  1228                 //Create picture box
  1229                 PictureBox picture = new PictureBox();
  1230                 picture.AutoSize = true;
  1231                 picture.BackColor = System.Drawing.Color.Transparent;
  1232                 picture.Dock = System.Windows.Forms.DockStyle.Fill;
  1233                 picture.Location = new System.Drawing.Point(1, 1);
  1234                 picture.Margin = new System.Windows.Forms.Padding(0);
  1235                 picture.Name = "pictureBox" + aField;
  1236                 //Set our image
  1237                 picture.Image = aField.Bitmap;
  1238                 //
  1239                 control = picture;
  1240             }
  1241 
  1242             return control;
  1243         }
  1244 
  1245 
  1246         private void buttonAlignLeft_Click(object sender, EventArgs e)
  1247         {
  1248             foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
  1249             {
  1250                 ctrl.TextAlign = ContentAlignment.MiddleLeft;
  1251             }
  1252         }
  1253 
  1254         private void buttonAlignCenter_Click(object sender, EventArgs e)
  1255         {
  1256             foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
  1257             {
  1258                 ctrl.TextAlign = ContentAlignment.MiddleCenter;
  1259             }
  1260         }
  1261 
  1262         private void buttonAlignRight_Click(object sender, EventArgs e)
  1263         {
  1264             foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
  1265             {
  1266                 ctrl.TextAlign = ContentAlignment.MiddleRight;
  1267             }
  1268         }
  1269 
  1270         private void comboBoxDisplayType_SelectedIndexChanged(object sender, EventArgs e)
  1271         {
  1272             Properties.Settings.Default.CurrentDisplayIndex = comboBoxDisplayType.SelectedIndex;
  1273             cds.DisplayType = comboBoxDisplayType.SelectedIndex;
  1274             Properties.Settings.Default.Save();
  1275             if (iDisplay.IsOpen())
  1276             {
  1277                 OpenDisplayConnection();
  1278             }
  1279             else
  1280             {
  1281                 UpdateStatus();
  1282             }
  1283         }
  1284 
  1285 
  1286         private void maskedTextBoxTimerInterval_TextChanged(object sender, EventArgs e)
  1287         {
  1288             if (maskedTextBoxTimerInterval.Text != "")
  1289             {
  1290                 int interval = Convert.ToInt32(maskedTextBoxTimerInterval.Text);
  1291 
  1292                 if (interval > 0)
  1293                 {
  1294                     timer.Interval = interval;
  1295                     cds.TimerInterval = timer.Interval;
  1296                     Properties.Settings.Default.Save();
  1297                 }
  1298             }
  1299         }
  1300 
  1301         private void buttonPowerOn_Click(object sender, EventArgs e)
  1302         {
  1303             iDisplay.PowerOn();
  1304         }
  1305 
  1306         private void buttonPowerOff_Click(object sender, EventArgs e)
  1307         {
  1308             iDisplay.PowerOff();
  1309         }
  1310 
  1311         private void buttonShowClock_Click(object sender, EventArgs e)
  1312         {
  1313             iDisplay.ShowClock();
  1314         }
  1315 
  1316         private void buttonHideClock_Click(object sender, EventArgs e)
  1317         {
  1318             iDisplay.HideClock();
  1319         }
  1320 
  1321         private void buttonUpdate_Click(object sender, EventArgs e)
  1322         {
  1323             InstallUpdateSyncWithInfo();
  1324         }
  1325 
  1326 
  1327         private void InstallUpdateSyncWithInfo()
  1328         {
  1329             UpdateCheckInfo info = null;
  1330 
  1331             if (ApplicationDeployment.IsNetworkDeployed)
  1332             {
  1333                 ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
  1334 
  1335                 try
  1336                 {
  1337                     info = ad.CheckForDetailedUpdate();
  1338 
  1339                 }
  1340                 catch (DeploymentDownloadException dde)
  1341                 {
  1342                     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);
  1343                     return;
  1344                 }
  1345                 catch (InvalidDeploymentException ide)
  1346                 {
  1347                     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);
  1348                     return;
  1349                 }
  1350                 catch (InvalidOperationException ioe)
  1351                 {
  1352                     MessageBox.Show("This application cannot be updated. It is likely not a ClickOnce application. Error: " + ioe.Message);
  1353                     return;
  1354                 }
  1355 
  1356                 if (info.UpdateAvailable)
  1357                 {
  1358                     Boolean doUpdate = true;
  1359 
  1360                     if (!info.IsUpdateRequired)
  1361                     {
  1362                         DialogResult dr = MessageBox.Show("An update is available. Would you like to update the application now?", "Update Available", MessageBoxButtons.OKCancel);
  1363                         if (!(DialogResult.OK == dr))
  1364                         {
  1365                             doUpdate = false;
  1366                         }
  1367                     }
  1368                     else
  1369                     {
  1370                         // Display a message that the app MUST reboot. Display the minimum required version.
  1371                         MessageBox.Show("This application has detected a mandatory update from your current " +
  1372                             "version to version " + info.MinimumRequiredVersion.ToString() +
  1373                             ". The application will now install the update and restart.",
  1374                             "Update Available", MessageBoxButtons.OK,
  1375                             MessageBoxIcon.Information);
  1376                     }
  1377 
  1378                     if (doUpdate)
  1379                     {
  1380                         try
  1381                         {
  1382                             ad.Update();
  1383                             MessageBox.Show("The application has been upgraded, and will now restart.");
  1384                             Application.Restart();
  1385                         }
  1386                         catch (DeploymentDownloadException dde)
  1387                         {
  1388                             MessageBox.Show("Cannot install the latest version of the application. \n\nPlease check your network connection, or try again later. Error: " + dde);
  1389                             return;
  1390                         }
  1391                     }
  1392                 }
  1393             }
  1394         }
  1395     }
  1396 
  1397     /// <summary>
  1398     /// A UI thread copy of a client relevant data.
  1399     /// Keeping this copy in the UI thread helps us deal with threading issues.
  1400     /// </summary>
  1401     public class ClientData
  1402     {
  1403         public ClientData(string aSessionId, ICallback aCallback)
  1404         {
  1405             SessionId = aSessionId;
  1406             Name = "";
  1407             Fields = new List<DataField>();
  1408             Layout = new TableLayout(1, 2); //Default to one column and two rows
  1409             Callback = aCallback;
  1410         }
  1411 
  1412         public string SessionId { get; set; }
  1413         public string Name { get; set; }
  1414         public List<DataField> Fields { get; set; }
  1415         public TableLayout Layout { get; set; }
  1416         public ICallback Callback { get; set; }
  1417     }
  1418 }