Here is the example (clipboard is accessible through javascript only in IE, mind you):
I will make the full code available on Monday. Below, is a runthrough of how this was done.
There are plenty of examples on how to access the clipboard in Silverlight through javascript (for IE, anyways). Here is the code that I used, from Jeff Wilcox's example:
1: public static void SetText(string text)
2:          {3:              var clipboardData = (ScriptObject)HtmlPage.Window.GetProperty("clipboardData");4: if (clipboardData != null)
5:              {6:                  bool success = (bool)clipboardData.Invoke("setData", "text", text);7: if (!success)
8:                  {9: HtmlPage.Window.Alert(ClipboardFailure);
10: }
11: }
12: else
13:              {14:                  HtmlPage.Window.Alert("clipboard not available");15: }
16: }
You can always ctrl+C to copy contents of a textbox or textblock to clipboard, but DataGrid does not allow that. To do this, I take the cell in context, loop through it's children recursively, and write out the contents of the child if it is TextBlock or TextBox:
1: private static string GetText(UIElement uie)
2:          {3: string ret = string.Empty;
4: if (uie != null)
5:              {6: if (uie is TextBlock)
7: ret = (uie as TextBlock).Text;
8: else if (uie is TextBox)
9: ret = (uie as TextBox).Text;
10: else if (uie is Panel)
11:                  {12: foreach (var element in (uie as Panel).Children)
13: ret += GetText(element);
14: }
15: }
16: return ret;
17: }
Now, what if we want to have a menu command that copies whole row data? I have a generic serializer that takes the datacontext of the row as object and, using reflection, recursively creates a string representation:
1: private static string Serialize(object data)
   2:          {3: if (data != null)
   4:              {5: var sb = new StringBuilder();
   6:                  var props = data.GetType().GetProperties();   7:                  sb.Append(data.GetType().Name);8: foreach (var prop in props)
   9:                  {10: var o = prop.GetValue(data, null);
11: sb.Append(Environment.NewLine).Append(prop.Name).Append(" = ");
12: if (o == null || o is string || o is DateTime || o.GetType().IsPrimitive)
  13:                          sb.Append(o);14: else
15: sb.Append(Serialize(o).Replace(Environment.NewLine, Environment.NewLine + "\t"));
  16:                  }17: return sb.ToString();
  18:              }19: else return string.Empty;
  20:          }How do we get to a reference to the cell? When context menu is requested, the DataGrid finds the cell where the right-click occurred, and keeps a reference to in a private field, to be used when the menu selection occurs. So the GetContextMenuContent method changes like so:
1: public virtual List<ContextMenuItemInfo> GetContextMenuContent(System.Windows.Browser.HtmlEventArgs e)
   2:          {3: Point curr = new Point(e.OffsetX, e.OffsetY);
4: _CellInContext = null;
5: for (var i = 0; i < presenter.Children.Count; i++)
   6:              {   7:                  var row = (DataGridRow)presenter.Children[i];8: if (ContextMenuInterceptor.IsPointWithinBounds(curr, row))
   9:                  {10: foreach (var col in Columns)
  11:                      {12: var cell = col.GetCellContent(row.DataContext).Parent as DataGridCell;
13: if (cell != null && ContextMenuInterceptor.IsPointWithinBounds(curr, cell))
  14:                          {  15:                              _CellInContext = cell;16: break;
  17:                          }  18:                      }  19:   20: foreach (ContextMenuItemInfo m in ContextMenuList)
  21:                          m.Tag = row;22: return ContextMenuList;
  23:                  }  24:              }25: return null;
  26:          }And the HandleContextMenu actually handles some stuff instead of just raising an event:
1: public virtual void HandleContextMenuClick(object sender, ContextMenuClickEventArgs e)
   2:          {3: switch (e.ClickedItem.Key)
   4:              {5: case CMD_COPY_ROW:
   6:                      Clipboard.SetText(Serialize(((DataGridRow)e.ClickedItem.Value).DataContext));7: break;
8: case CMD_COPY_CELL:
9: if (_CellInContext != null)
10: Clipboard.SetText(GetText(_CellInContext.Content as UIElement));
11: break;
12: default:
13: if (ContextMenuClicked != null)
  14:                          ContextMenuClicked(sender, e);15: break;
  16:              }  17:          }Both of these have worked very well for our application. Both "Copy Cell Content" and "Copy Row Data" are context menu commands in all our datagrids.
No comments:
Post a Comment