我正在编写一个UI应用程序,允许用户将数字网格粘贴到其中,然后发送到远程服务器。客户端可以是WPF或Silverlight。
我尝试使用Grid
Textbox
个DataObject.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
答案 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
,因为它太慢了。