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