1

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

我尝试了一个Grid控件TextboxDataObject.AddPastingHandler挂钩用户粘贴输入,将字符串中的制表符分隔行转换为Textbox控件。在用户从 Excel 粘贴 122x161 单元格后,这需要 12 秒 (!) 进行自我更新,这速度慢得让人无法接受。剖析字符串需要 0.1 秒,添加行和列需要 1 秒,构造和插入TextBox控件需要 2 秒,其余 12 秒似乎是TextBox第一次绘制控件。

我也尝试过DataGrid,但它似乎不能很好地处理二维数组,更喜欢它使用反射剖析属性的一维对象数组。

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

编辑

我已经确定TextBox为罪魁祸首,因为它非常缓慢。这是一些说明问题的 F# 代码(替换TextBoxTextBlock40 倍,它仍然比应有的速度慢几个数量级,但在这种情况下是可以接受的):

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
4

2 回答 2

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;
}
于 2012-09-21T16:31:18.513 回答
0

原来问题是内置的 WPFTextBox控件非常慢。所以我们必须要么避免它,要么替换它。

在这种情况下,我只想要文本,因此我可以通过使用TextBlock来避免它,这将粘贴单元格的时间从 12 秒缩短到 0.25 秒,这(几乎)是可以接受的。

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

于 2012-09-22T10:48:31.780 回答