用于查看122x161网格数字的WPF / Silverlight控件

时间:2012-09-21 15:33:29

标签: wpf silverlight controls

我正在编写一个UI应用程序,允许用户将数字网格粘贴到其中,然后发送到远程服务器。客户端可以是WPF或Silverlight。

我尝试使用Grid TextboxDataObject.AddPastingHandler控件来挂钩用户粘贴输入,将字符串中以制表符分隔的行转换为Textbox控件。用户在Excel中仅粘贴122x161单元格后,需要12秒(!)才能自动更新,这种情况速度慢得令人无法接受。解析字符串需要0.1秒,添加行和列需要1秒,构造和插入TextBox控件需要2秒,而12秒的其余部分似乎花费了第一次绘制TextBox控件。

我也尝试了DataGrid,但它似乎没有很好地处理2D​​数组,更喜欢使用反射分析属性的一维对象数组。

现在我正在考虑使用Canvas并自己绘制数字,这看起来很疯狂。是否有一种简单的方法可以使用内置的WPF或Silverlight控件来完成这项工作,而不会给用户带来痛苦的延迟?

修改

我已经确定TextBox是罪魁祸首,因为它太疯狂了。这里有一些说明问题的F#代码(将TextBox替换为TextBlock,速度提高了40倍,这仍然比它应该的要低几个数量级,但在这种情况下是可接受的):

open System.Windows

let app = Application()

let readClipboard() =
  let data = (Clipboard.GetData "Text") :?> string
  [|for row in data.Split[|'\n'|] do
    match row.Split[|'\t'|] with
    | [||] | [|""|] -> ()
    | row -> yield row|]

[<System.STAThreadAttribute>]
do
  let grid = Controls.Grid()
  let row = Controls.RowDefinition()
  Controls.RowDefinition() |> grid.RowDefinitions.Add
  Controls.ColumnDefinition() |> grid.ColumnDefinitions.Add
  let add i j ctrl =
    Controls.Grid.SetRow(ctrl, i)
    Controls.Grid.SetColumn(ctrl, j)
    grid.Children.Add ctrl |> ignore
  let paste() =
    let timer = System.Diagnostics.Stopwatch.StartNew()
    let data = readClipboard()
    printfn "Read clipboard %fs" timer.Elapsed.TotalSeconds
    let rows = data.Length
    let cols = data |> Array.fold (fun n xs -> xs.Length |> max n) 0
    printfn "%dx%d" rows cols
    grid.RowDefinitions.Clear()
    grid.ColumnDefinitions.Clear()
    grid.Children.Clear()
    for row in 1..rows do
      Controls.RowDefinition(Height=GridLength 24.0) |> grid.RowDefinitions.Add
    for col in 1..cols do
      Controls.ColumnDefinition(Width=GridLength 64.0) |> grid.ColumnDefinitions.Add
    printfn "Add rows and columns complete %fs" timer.Elapsed.TotalSeconds
    for i in 0..rows-1 do
      for j in 0..data.[i].Length-1 do
        Controls.TextBox(Text=data.[i].[j]) |> add i j
    printfn "Insert complete %fs" timer.Elapsed.TotalSeconds
    Media.CompositionTarget.Rendering.Add(fun _ ->
      printfn "Next Rendering event at %fs" timer.Elapsed.TotalSeconds
      app.Shutdown())
  let scroll = Controls.ScrollViewer(Content=grid)
  scroll.HorizontalScrollBarVisibility <- Controls.ScrollBarVisibility.Visible
  let window = Window(Content=scroll)
  window.Focusable <- true
  window.Focus() |> ignore
  window.PreviewKeyDown.Add(fun e ->
    let ctrl = Input.ModifierKeys.Control
    if Input.Keyboard.Modifiers &&& ctrl = ctrl then
      if e.Key = Input.Key.V then
        paste())
  app.Run window |> ignore

2 个答案:

答案 0 :(得分:0)

我有一些使用后面的代码添加列的代码。如果你使用MVVM方法,这并不理想,但我没有注意到它的任何性能问题(尽管我还没有把它推到数百列)。

以下代码用于向包含“参与者”的数据网格添加“块”列。由于在编译时不知道块的数量,我使用XAML代码生成添加它们。

public void AddParticipantGridViewColumns()
{
    var setupViewModel = (SetupPanelViewModel)DataContext;
    if (setupViewModel.BlockSlotViewModels == null) return;
    var blockColumnCount = setupViewModel.BlockSlotViewModels.Count();
    var dataGrid = (DataGrid)ParticipantDataGrid;
    if (dataGrid.Columns.Count == blockColumnCount + 1) return;
    for (var blockIndex = 0; blockIndex < blockColumnCount; blockIndex++)
    {
        var column = BuildParticipantGridViewColumn(blockIndex);
        dataGrid.Columns.Add(column);
    }
}

private DataGridTemplateColumn BuildParticipantGridViewColumn(int blockIndex)
{
    var columnXaml = string.Format(@"
        <DataGridTemplateColumn
            xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
            xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml""
            Header=""Block {1}"">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text=""{{Binding BlockSlotViewModels[{0}].ConditionLabel}}""
                               Foreground=""{{Binding BlockSlotViewModels[{0}].TextBrush}}"" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>",
        blockIndex, blockIndex + 1);
    var column = (DataGridTemplateColumn)XamlReader.Parse(columnXaml);
    return column;
}

答案 1 :(得分:0)

原来问题是内置的WPF TextBox控件非常慢。因此,我们必须避免或替换它。

在这种情况下,我只想要文本,所以我可以通过使用TextBlock来避免它,这会花费时间将单元格从12s粘贴到0.25s,这几乎是可以接受的。

对于想要可编辑单元格的任何人来说,显然你应该在任何地方使用TextBlock并且只将正在编辑的一个单元格转换为TextBox,因为它太慢了。