我正在编写一个 UI 应用程序,它允许用户将一个数字网格粘贴到其中,然后发送到远程服务器。客户端可以是 WPF 或 Silverlight。
我尝试了一个Grid
控件Textbox
来DataObject.AddPastingHandler
挂钩用户粘贴输入,将字符串中的制表符分隔行转换为Textbox
控件。在用户从 Excel 粘贴 122x161 单元格后,这需要 12 秒 (!) 进行自我更新,这速度慢得让人无法接受。剖析字符串需要 0.1 秒,添加行和列需要 1 秒,构造和插入TextBox
控件需要 2 秒,其余 12 秒似乎是TextBox
第一次绘制控件。
我也尝试过DataGrid
,但它似乎不能很好地处理二维数组,更喜欢它使用反射剖析属性的一维对象数组。
现在我正在考虑只使用 aCanvas
并自己绘制数字,这似乎很疯狂。有没有一种简单的方法来使用内置的 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