Server/MainForm.cs
author sl
Tue, 19 Aug 2014 17:29:52 +0200
changeset 36 a3aa661da810
parent 34 59bfa4ebcbbb
child 37 405a2590eda4
permissions -rw-r--r--
Adding warning when newly selected font is too height.
Reworking our UI.
     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 //
    17 using SharpDisplayInterface;
    18 using SharpDisplayClient;
    19 
    20 
    21 namespace SharpDisplayManager
    22 {
    23     public partial class MainForm : Form
    24     {
    25         DateTime LastTickTime;
    26         Display iDisplay;
    27         System.Drawing.Bitmap iBmp;
    28         bool iCreateBitmap; //Workaround render to bitmap issues when minimized
    29         ServiceHost iServiceHost;
    30         /// <summary>
    31         /// Our collection of clients
    32         /// </summary>
    33         public Dictionary<string, ClientData> iClients;
    34         public bool iClosing;
    35 
    36         public MainForm()
    37         {
    38             LastTickTime = DateTime.Now;
    39             iDisplay = new Display();
    40             iClients = new Dictionary<string, ClientData>();
    41 
    42             InitializeComponent();
    43             UpdateStatus();
    44 
    45             //Load settings
    46             marqueeLabelTop.Font = Properties.Settings.Default.DisplayFont;
    47             marqueeLabelBottom.Font = Properties.Settings.Default.DisplayFont;
    48             checkBoxShowBorders.Checked = Properties.Settings.Default.DisplayShowBorders;
    49             checkBoxConnectOnStartup.Checked = Properties.Settings.Default.DisplayConnectOnStartup;
    50             checkBoxReverseScreen.Checked = Properties.Settings.Default.DisplayReverseScreen;
    51             //
    52             tableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
    53             //We have a bug when drawing minimized and reusing our bitmap
    54             iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
    55             iCreateBitmap = false;
    56         }
    57 
    58         private void MainForm_Load(object sender, EventArgs e)
    59         {
    60             StartServer();
    61 
    62             if (Properties.Settings.Default.DisplayConnectOnStartup)
    63             {
    64                 iDisplay.Open();
    65                 UpdateStatus();
    66             }
    67         }
    68 
    69 
    70         private void buttonFont_Click(object sender, EventArgs e)
    71         {
    72             //fontDialog.ShowColor = true;
    73             //fontDialog.ShowApply = true;
    74             fontDialog.ShowEffects = true;
    75             fontDialog.Font = marqueeLabelTop.Font;
    76 
    77             fontDialog.FixedPitchOnly = checkBoxFixedPitchFontOnly.Checked;
    78 
    79             //fontDialog.ShowHelp = true;
    80 
    81             //fontDlg.MaxSize = 40;
    82             //fontDlg.MinSize = 22;
    83 
    84             //fontDialog.Parent = this;
    85             //fontDialog.StartPosition = FormStartPosition.CenterParent;
    86 
    87             //DlgBox.ShowDialog(fontDialog);
    88 
    89             //if (fontDialog.ShowDialog(this) != DialogResult.Cancel)
    90             if (DlgBox.ShowDialog(fontDialog) != DialogResult.Cancel)
    91             {
    92 
    93                 //MsgBox.Show("MessageBox MsgBox", "MsgBox caption");
    94 
    95                 //MessageBox.Show("Ok");
    96                 marqueeLabelTop.Font = fontDialog.Font;
    97                 marqueeLabelBottom.Font = fontDialog.Font;
    98                 Properties.Settings.Default.DisplayFont = fontDialog.Font;
    99                 Properties.Settings.Default.Save();
   100                 //
   101                 if (fontDialog.Font.Height > marqueeLabelBottom.Height)
   102                 {
   103                     labelWarning.Text = "WARNING: Selected font is too height by " + (fontDialog.Font.Height - marqueeLabelBottom.Height) + " pixels!";
   104                     labelWarning.Visible = true;
   105                 }
   106                 else
   107                 {
   108                     labelWarning.Visible = false;
   109                 }
   110 
   111             }
   112         }
   113 
   114         private void buttonCapture_Click(object sender, EventArgs e)
   115         {
   116             System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height);
   117             tableLayoutPanel.DrawToBitmap(bmp, tableLayoutPanel.ClientRectangle);
   118             //Bitmap bmpToSave = new Bitmap(bmp);
   119             bmp.Save("D:\\capture.png");
   120 
   121             marqueeLabelTop.Text = "Sweet";
   122 
   123             /*
   124             string outputFileName = "d:\\capture.png";
   125             using (MemoryStream memory = new MemoryStream())
   126             {
   127                 using (FileStream fs = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
   128                 {
   129                     bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
   130                     byte[] bytes = memory.ToArray();
   131                     fs.Write(bytes, 0, bytes.Length);
   132                 }
   133             }
   134              */
   135 
   136         }
   137 
   138         private void CheckForRequestResults()
   139         {
   140             if (iDisplay.IsRequestPending())
   141             {
   142                 switch (iDisplay.AttemptRequestCompletion())
   143                 {
   144                     case Display.TMiniDisplayRequest.EMiniDisplayRequestPowerSupplyStatus:
   145                         if (iDisplay.PowerSupplyStatus())
   146                         {
   147                             toolStripStatusLabelPower.Text = "ON";
   148                         }
   149                         else
   150                         {
   151                             toolStripStatusLabelPower.Text = "OFF";
   152                         }
   153                         //Issue next request then
   154                         iDisplay.RequestDeviceId();
   155                         break;
   156 
   157                     case Display.TMiniDisplayRequest.EMiniDisplayRequestDeviceId:
   158                         toolStripStatusLabelConnect.Text += " - " + iDisplay.DeviceId();
   159                         //Issue next request then
   160                         iDisplay.RequestFirmwareRevision();
   161                         break;
   162 
   163                     case Display.TMiniDisplayRequest.EMiniDisplayRequestFirmwareRevision:
   164                         toolStripStatusLabelConnect.Text += " v" + iDisplay.FirmwareRevision();
   165                         //No more request to issue
   166                         break;
   167                 }
   168             }
   169         }
   170 
   171 
   172         public delegate int CoordinateTranslationDelegate(System.Drawing.Bitmap aBmp, int aInt);
   173 
   174 
   175         public static int ScreenReversedX(System.Drawing.Bitmap aBmp, int aX)
   176         {
   177             return aBmp.Width - aX - 1;
   178         }
   179 
   180         public int ScreenReversedY(System.Drawing.Bitmap aBmp, int aY)
   181         {
   182             return iBmp.Height - aY - 1;
   183         }
   184 
   185         public int ScreenX(System.Drawing.Bitmap aBmp, int aX)
   186         {
   187             return aX;
   188         }
   189 
   190         public int ScreenY(System.Drawing.Bitmap aBmp, int aY)
   191         {
   192             return aY;
   193         }
   194 
   195 
   196         //This is our timer tick responsible to perform our render
   197         private void timer_Tick(object sender, EventArgs e)
   198         {
   199             //Update our animations
   200             DateTime NewTickTime = DateTime.Now;
   201 
   202             marqueeLabelTop.UpdateAnimation(LastTickTime, NewTickTime);
   203             marqueeLabelBottom.UpdateAnimation(LastTickTime, NewTickTime);
   204 
   205             //Update our display
   206             if (iDisplay.IsOpen())
   207             {
   208                 CheckForRequestResults();
   209 
   210                 //Draw to bitmap
   211                 if (iCreateBitmap)
   212                 {
   213                     iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
   214                 }
   215                 tableLayoutPanel.DrawToBitmap(iBmp, tableLayoutPanel.ClientRectangle);
   216                 //iBmp.Save("D:\\capture.png");
   217 
   218                 //Select proper coordinate translation functions
   219                 //We used delegate/function pointer to support reverse screen without doing an extra test on each pixels
   220                 CoordinateTranslationDelegate screenX;
   221                 CoordinateTranslationDelegate screenY;
   222 
   223                 if (Properties.Settings.Default.DisplayReverseScreen)
   224                 {
   225                     screenX = ScreenReversedX;
   226                     screenY = ScreenReversedY;
   227                 }
   228                 else
   229                 {
   230                     screenX = ScreenX;
   231                     screenY = ScreenY;
   232                 }
   233 
   234                 //Send it to our display
   235                 for (int i = 0; i < iBmp.Width; i++)
   236                 {
   237                     for (int j = 0; j < iBmp.Height; j++)
   238                     {
   239                         unchecked
   240                         {
   241                             uint color = (uint)iBmp.GetPixel(i, j).ToArgb();
   242                             //For some reason when the app is minimized in the task bar only the alpha of our color is set.
   243                             //Thus that strange test for rendering to work both when the app is in the task bar and when it isn't.
   244                             iDisplay.SetPixel(screenX(iBmp, i), screenY(iBmp, j), Convert.ToInt32(!(color != 0xFF000000)));
   245                         }
   246                     }
   247                 }
   248 
   249                 iDisplay.SwapBuffers();
   250 
   251             }
   252 
   253             //Compute instant FPS
   254             toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " FPS";
   255 
   256             LastTickTime = NewTickTime;
   257 
   258         }
   259 
   260         private void buttonOpen_Click(object sender, EventArgs e)
   261         {
   262             if (iDisplay.Open())
   263             {
   264                 UpdateStatus();
   265                 iDisplay.RequestPowerSupplyStatus();
   266             }
   267             else
   268             {
   269                 UpdateStatus();
   270                 toolStripStatusLabelConnect.Text = "Connection error";
   271             }
   272 
   273         }
   274 
   275         private void buttonClose_Click(object sender, EventArgs e)
   276         {
   277             iDisplay.Close();
   278             UpdateStatus();
   279         }
   280 
   281         private void buttonClear_Click(object sender, EventArgs e)
   282         {
   283             iDisplay.Clear();
   284             iDisplay.SwapBuffers();
   285         }
   286 
   287         private void buttonFill_Click(object sender, EventArgs e)
   288         {
   289             iDisplay.Fill();
   290             iDisplay.SwapBuffers();
   291         }
   292 
   293         private void trackBarBrightness_Scroll(object sender, EventArgs e)
   294         {
   295             Properties.Settings.Default.DisplayBrightness = trackBarBrightness.Value;
   296             Properties.Settings.Default.Save();
   297             iDisplay.SetBrightness(trackBarBrightness.Value);
   298 
   299         }
   300 
   301         private void UpdateStatus()
   302         {
   303             if (iDisplay.IsOpen())
   304             {
   305                 buttonFill.Enabled = true;
   306                 buttonClear.Enabled = true;
   307                 buttonOpen.Enabled = false;
   308                 buttonClose.Enabled = true;
   309                 trackBarBrightness.Enabled = true;
   310                 trackBarBrightness.Minimum = iDisplay.MinBrightness();
   311                 trackBarBrightness.Maximum = iDisplay.MaxBrightness();
   312                 trackBarBrightness.Value = Properties.Settings.Default.DisplayBrightness;
   313                 trackBarBrightness.LargeChange = Math.Max(1,(iDisplay.MaxBrightness() - iDisplay.MinBrightness())/5);
   314                 trackBarBrightness.SmallChange = 1;
   315                 iDisplay.SetBrightness(Properties.Settings.Default.DisplayBrightness);
   316 
   317                 toolStripStatusLabelConnect.Text = "Connected - " + iDisplay.Vendor() + " - " + iDisplay.Product();
   318                 //+ " - " + iDisplay.SerialNumber();
   319             }
   320             else
   321             {
   322                 buttonFill.Enabled = false;
   323                 buttonClear.Enabled = false;
   324                 buttonOpen.Enabled = true;
   325                 buttonClose.Enabled = false;
   326                 trackBarBrightness.Enabled = false;
   327                 toolStripStatusLabelConnect.Text = "Disconnected";
   328             }
   329         }
   330 
   331 
   332 
   333         private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e)
   334         {
   335             //Save our show borders setting
   336             tableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
   337             Properties.Settings.Default.DisplayShowBorders = checkBoxShowBorders.Checked;
   338             Properties.Settings.Default.Save();
   339         }
   340 
   341         private void checkBoxConnectOnStartup_CheckedChanged(object sender, EventArgs e)
   342         {
   343             //Save our connect on startup setting
   344             Properties.Settings.Default.DisplayConnectOnStartup = checkBoxConnectOnStartup.Checked;
   345             Properties.Settings.Default.Save();
   346         }
   347 
   348         private void checkBoxReverseScreen_CheckedChanged(object sender, EventArgs e)
   349         {
   350             //Save our reverse screen setting
   351             Properties.Settings.Default.DisplayReverseScreen = checkBoxReverseScreen.Checked;
   352             Properties.Settings.Default.Save();
   353         }
   354 
   355         private void MainForm_Resize(object sender, EventArgs e)
   356         {
   357             if (WindowState == FormWindowState.Minimized)
   358             {
   359                 // Do some stuff
   360                 //iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
   361                 iCreateBitmap = true;
   362             }
   363         }
   364 
   365         private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
   366         {
   367             StopServer();
   368             e.Cancel = iClosing;
   369         }
   370 
   371         public void StartServer()
   372         {
   373             iServiceHost = new ServiceHost
   374                 (
   375                     typeof(DisplayServer),
   376                     new Uri[] { new Uri("net.tcp://localhost:8001/") }
   377                 );
   378 
   379             iServiceHost.AddServiceEndpoint(typeof(IDisplayService), new NetTcpBinding(SecurityMode.None,true), "DisplayService");
   380             iServiceHost.Open();
   381         }
   382 
   383         public void StopServer()
   384         {
   385             if (iClients.Count > 0 && !iClosing)
   386             {
   387                 //Tell our clients
   388                 iClosing = true;
   389                 BroadcastCloseEvent();
   390             }
   391             else if (iClosing)
   392             {
   393                 if (MessageBox.Show("Force exit?", "Waiting for clients...", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
   394                 {
   395                     iClosing = false; //We make sure we force close if asked twice
   396                 }
   397             }
   398             else
   399             {
   400                 //We removed that as it often lags for some reason
   401                 //iServiceHost.Close();
   402             }
   403         }
   404 
   405         public void BroadcastCloseEvent()
   406         {
   407             Trace.TraceInformation("BroadcastCloseEvent - start");
   408 
   409             var inactiveClients = new List<string>();
   410             foreach (var client in iClients)
   411             {
   412                 //if (client.Key != eventData.ClientName)
   413                 {
   414                     try
   415                     {
   416                         Trace.TraceInformation("BroadcastCloseEvent - " + client.Key);
   417                         client.Value.Callback.OnCloseOrder(/*eventData*/);
   418                     }
   419                     catch (Exception ex)
   420                     {
   421                         inactiveClients.Add(client.Key);
   422                     }
   423                 }
   424             }
   425 
   426             if (inactiveClients.Count > 0)
   427             {
   428                 foreach (var client in inactiveClients)
   429                 {
   430                     iClients.Remove(client);
   431                     Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(client, false)[0]);
   432                 }
   433             }
   434         }
   435 
   436         private void buttonStartClient_Click(object sender, EventArgs e)
   437         {
   438             Thread clientThread = new Thread(SharpDisplayClient.Program.Main);
   439             clientThread.Start();
   440             BringToFront();
   441         }
   442 
   443         private void buttonSuspend_Click(object sender, EventArgs e)
   444         {
   445             timer.Enabled = !timer.Enabled;
   446             if (!timer.Enabled)
   447             {
   448                 buttonSuspend.Text = "Suspend";
   449             }
   450             else
   451             {
   452                 buttonSuspend.Text = "Pause";
   453             }
   454         }
   455 
   456         private void buttonCloseClients_Click(object sender, EventArgs e)
   457         {
   458             BroadcastCloseEvent();
   459         }
   460 
   461         private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e)
   462         {
   463 
   464         }
   465 
   466         //Delegates are used for our thread safe method
   467         public delegate void AddClientDelegate(string aSessionId, IDisplayServiceCallback aCallback);
   468         public delegate void RemoveClientDelegate(string aSessionId);
   469         public delegate void SetTextDelegate(string SessionId, int aLineIndex, string aText);
   470         public delegate void SetTextsDelegate(string SessionId, System.Collections.Generic.IList<string> aTexts);
   471         public delegate void SetClientNameDelegate(string aSessionId, string aName);
   472 
   473 
   474         /// <summary>
   475         ///
   476         /// </summary>
   477         /// <param name="aSessionId"></param>
   478         /// <param name="aCallback"></param>
   479         public void AddClientThreadSafe(string aSessionId, IDisplayServiceCallback aCallback)
   480         {
   481             if (this.InvokeRequired)
   482             {
   483                 //Not in the proper thread, invoke ourselves
   484                 AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe);
   485                 this.Invoke(d, new object[] { aSessionId, aCallback });
   486             }
   487             else
   488             {
   489                 //We are in the proper thread
   490                 //Add this session to our collection of clients
   491                 ClientData newClient = new ClientData(aSessionId, aCallback);
   492                 Program.iMainForm.iClients.Add(aSessionId, newClient);
   493                 //Add this session to our UI
   494                 UpdateClientTreeViewNode(newClient);
   495             }
   496         }
   497 
   498         /// <summary>
   499         ///
   500         /// </summary>
   501         /// <param name="aSessionId"></param>
   502         public void RemoveClientThreadSafe(string aSessionId)
   503         {
   504             if (this.InvokeRequired)
   505             {
   506                 //Not in the proper thread, invoke ourselves
   507                 RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe);
   508                 this.Invoke(d, new object[] { aSessionId });
   509             }
   510             else
   511             {
   512                 //We are in the proper thread
   513                 //Remove this session from both client collection and UI tree view
   514                 if (Program.iMainForm.iClients.Keys.Contains(aSessionId))
   515                 {
   516                     Program.iMainForm.iClients.Remove(aSessionId);
   517                     Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(aSessionId, false)[0]);
   518                 }
   519 
   520                 if (iClosing && iClients.Count == 0)
   521                 {
   522                     //We were closing our form
   523                     //All clients are now closed
   524                     //Just resume our close operation
   525                     iClosing = false;
   526                     Close();
   527                 }
   528             }
   529         }
   530 
   531         /// <summary>
   532         ///
   533         /// </summary>
   534         /// <param name="aLineIndex"></param>
   535         /// <param name="aText"></param>
   536         public void SetTextThreadSafe(string aSessionId, int aLineIndex, string aText)
   537         {
   538             if (this.InvokeRequired)
   539             {
   540                 //Not in the proper thread, invoke ourselves
   541                 SetTextDelegate d = new SetTextDelegate(SetTextThreadSafe);
   542                 this.Invoke(d, new object[] { aSessionId, aLineIndex, aText });
   543             }
   544             else
   545             {
   546                 ClientData client = iClients[aSessionId];
   547                 if (client != null)
   548                 {
   549                     //Make sure all our texts are in place
   550                     while (client.Texts.Count < (aLineIndex + 1))
   551                     {
   552                         client.Texts.Add("");
   553                     }
   554                     client.Texts[aLineIndex] = aText;
   555 
   556                     //We are in the proper thread
   557                     //Only support two lines for now
   558                     if (aLineIndex == 0)
   559                     {
   560                         marqueeLabelTop.Text = aText;
   561                     }
   562                     else if (aLineIndex == 1)
   563                     {
   564                         marqueeLabelBottom.Text = aText;
   565                     }
   566 
   567 
   568                     UpdateClientTreeViewNode(client);
   569                 }
   570             }
   571         }
   572 
   573         /// <summary>
   574         ///
   575         /// </summary>
   576         /// <param name="aTexts"></param>
   577         public void SetTextsThreadSafe(string aSessionId, System.Collections.Generic.IList<string> aTexts)
   578         {
   579             if (this.InvokeRequired)
   580             {
   581                 //Not in the proper thread, invoke ourselves
   582                 SetTextsDelegate d = new SetTextsDelegate(SetTextsThreadSafe);
   583                 this.Invoke(d, new object[] { aSessionId, aTexts });
   584             }
   585             else
   586             {
   587                 ClientData client = iClients[aSessionId];
   588                 if (client != null)
   589                 {
   590                     //Populate our client with the given texts
   591                     int j = 0;
   592                     foreach (string text in aTexts)
   593                     {
   594                         if (client.Texts.Count < (j + 1))
   595                         {
   596                             client.Texts.Add(text);
   597                         }
   598                         else
   599                         {
   600                             client.Texts[j]=text;
   601                         }
   602                         j++;
   603                     }
   604                     //We are in the proper thread
   605                     //Only support two lines for now
   606                     for (int i = 0; i < aTexts.Count; i++)
   607                     {
   608                         if (i == 0)
   609                         {
   610                             marqueeLabelTop.Text = aTexts[i];
   611                         }
   612                         else if (i == 1)
   613                         {
   614                             marqueeLabelBottom.Text = aTexts[i];
   615                         }
   616                     }
   617 
   618 
   619                     UpdateClientTreeViewNode(client);
   620                 }
   621             }
   622         }
   623 
   624 
   625         /// <summary>
   626         ///
   627         /// </summary>
   628         /// <param name="aSessionId"></param>
   629         /// <param name="aName"></param>
   630         public void SetClientNameThreadSafe(string aSessionId, string aName)
   631         {
   632             if (this.InvokeRequired)
   633             {
   634                 //Not in the proper thread, invoke ourselves
   635                 SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe);
   636                 this.Invoke(d, new object[] { aSessionId, aName });
   637             }
   638             else
   639             {
   640                 //We are in the proper thread
   641                 //Get our client
   642                 ClientData client = iClients[aSessionId];
   643                 if (client != null)
   644                 {
   645                     //Set its name
   646                     client.Name = aName;
   647                     //Update our tree-view
   648                     UpdateClientTreeViewNode(client);
   649                 }
   650             }
   651         }
   652 
   653         /// <summary>
   654         ///
   655         /// </summary>
   656         /// <param name="aClient"></param>
   657         private void UpdateClientTreeViewNode(ClientData aClient)
   658         {
   659             if (aClient == null)
   660             {
   661                 return;
   662             }
   663 
   664             TreeNode node = null;
   665             //Check that our client node already exists
   666             //Get our client root node using its key which is our session ID
   667             TreeNode[] nodes = treeViewClients.Nodes.Find(aClient.SessionId, false);
   668             if (nodes.Count()>0)
   669             {
   670                 //We already have a node for that client
   671                 node = nodes[0];
   672                 //Clear children as we are going to recreate them below
   673                 node.Nodes.Clear();
   674             }
   675             else
   676             {
   677                 //Client node does not exists create a new one
   678                 treeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId);
   679                 node = treeViewClients.Nodes.Find(aClient.SessionId, false)[0];
   680             }
   681 
   682             if (node != null)
   683             {
   684                 //Change its name
   685                 if (aClient.Name != "")
   686                 {
   687                     //We have a name, us it as text for our root node
   688                     node.Text = aClient.Name;
   689                     //Add a child with SessionId
   690                     node.Nodes.Add(new TreeNode(aClient.SessionId));
   691                 }
   692                 else
   693                 {
   694                     //No name, use session ID instead
   695                     node.Text = aClient.SessionId;
   696                 }
   697 
   698                 if (aClient.Texts.Count > 0)
   699                 {
   700                     //Create root node for our texts
   701                     TreeNode textsRoot = new TreeNode("Text");
   702                     node.Nodes.Add(textsRoot);
   703                     //For each text add a new entry
   704                     foreach (string text in aClient.Texts)
   705                     {
   706                         textsRoot.Nodes.Add(new TreeNode(text));
   707                     }
   708                 }
   709 
   710                 node.ExpandAll();
   711             }
   712         }
   713 
   714 
   715     }
   716 
   717     /// <summary>
   718     /// A UI thread copy of a client relevant data.
   719     /// Keeping this copy in the UI thread helps us deal with threading issues.
   720     /// </summary>
   721     public class ClientData
   722     {
   723         public ClientData(string aSessionId, IDisplayServiceCallback aCallback)
   724         {
   725             SessionId = aSessionId;
   726             Name = "";
   727             Texts = new List<string>();
   728             Callback = aCallback;
   729         }
   730 
   731         public string SessionId { get; set; }
   732         public string Name { get; set; }
   733         public List<string> Texts { get; set; }
   734         public IDisplayServiceCallback Callback { get; set; }
   735     }
   736 }