2012-09-21 23 views
1

我正在編寫一個UI應用程序,允許用戶將數字網格粘貼到該應用程序中,並將其發送到遠程服務器。客戶端可以是WPF或Silverlight。用於查看122x161數字網格的WPF/Silverlight控件

我使用DataObject.AddPastingHandler鉤住使用者粘貼輸入,在一個字符串從製表符分隔的行轉換爲Textbox控制試圖TextboxGrid一個控件。這需要12秒(!)在用戶粘貼來自Excel的122x161單元格後進行自我更新,這是非常慢的。解剖字符串需要0.1s,添加行和列需要1s,構建並插入TextBox控件需要2s,剩餘的12s似乎花費在第一次繪製TextBox控件。

我也試過DataGrid,但它似乎並沒有很好地處理2D​​數組,它更喜歡使用反射爲屬性解析的1D對象數組。

現在我正在考慮只使用Canvas並自己繪製數字,這似乎很瘋狂。有沒有簡單的方法來使用內置的WPF或Silverlight控件來完成這一任務,而不會對用戶造成痛苦的延遲?

編輯

因爲我已經確定TextBox的罪魁禍首,因爲它是出奇的慢。下面是說明了這個問題的一些F#代碼(與TextBlock和40X取代TextBox更快,這仍然是數量比它慢應該的訂單,但可以接受在這種情況下):

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

您可以顯示當前的代碼和XAML以顯示您的網格嗎? – Rachel

+0

@Rachel我已經發布了F#代碼,我已經說明了這個問題。 –

回答

0

原來問題是內置的WPF TextBox控件非常慢。所以我們必須避免或取代它。

在這種情況下,我只是想要文本,所以我可以通過使用TextBlock來避免這種情況,它可以將時間從12s粘貼到0.25s,這是可以接受的。

對於任何想要編輯單元格的人,顯然你應該在任何地方都使用TextBlock,並且只將正在編輯的單元格轉換爲TextBox,因爲它太慢了。

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; 
}