I create a huge sheet with 1048576 rows.
If 1. Create a Workbook; 2. Add a Worksheet to Workbook; 3. Fill Worksheet with 1048576 rows; 4. Set Workbook to XamSpreadsheet.Workbook property.It works(CreateBackground_OnClick, LoadButton_OnClick). If 1. Get Workbook from XamSpreadsheet.Workbook property. 2. Get ActiveWorksheet; 3. Fill ActiveWorksheet with 1048576 rows.It throws OutOfMemoryException(CreateDirectly_OnClick).Redo/Undo and other capabilities need memory? If so, how can avoid this?For example: var workbook = spread.Workbook; workbook.SetCurrentFormat(WorkbookFormat.Excel2007); spread.BeginUpdate(); // mark update beginning FillWorksheet(spread.ActiveWorksheet, 1048576); spread.EndUpdate(); // mark update endBetween BeginUpdate() and EndUpdate(), XamSpreadsheet seems as lost memory.Thus create some rows, just do once, also undo once.
Thank you!
What's the difference?
===============================================================================<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ig="http://schemas.infragistics.com/xaml" x:Class="SpreadTest.MainWindow" Title="MainWindow" Height="350" Width="525"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions>
<StackPanel Orientation="Horizontal"> <Button Margin="5" Content="Create Directly" Click="CreateDirectly_OnClick"/> <Button Margin="5" Content="Create Background" Click="CreateBackground_OnClick"/> <Button Margin="5" Content="Create File" Click="CreateFile_OnClick"/> <Button Margin="5" Content="Load File" Click="LoadButton_OnClick"/> </StackPanel> <ig:XamSpreadsheet x:Name="spread" Grid.Row="1"/> </Grid></Window>
using System;using System.Diagnostics;using System.IO;using System.Windows;using Infragistics.Documents.Excel;
namespace SpreadTest{ /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); }
private void CreateDirectly_OnClick(object sender, RoutedEventArgs e) { var workbook = spread.Workbook; workbook.SetCurrentFormat(WorkbookFormat.Excel2007); FillWorksheet(spread.ActiveWorksheet, 1048576); }
private void CreateBackground_OnClick(object sender, RoutedEventArgs e) { var workbook = new Workbook(); workbook.SetCurrentFormat(WorkbookFormat.Excel2007); var sheet = workbook.Worksheets.Add(string.Format("Data Sheet")); FillWorksheet(sheet, 1048576); spread.Workbook = workbook; }
private void LoadButton_OnClick(object sender, RoutedEventArgs e) { var workbook = Workbook.Load(@"F:\test.xlsx"); if (workbook != null) { spread.Workbook = workbook; } }
private void CreateFile_OnClick(object sender, RoutedEventArgs e) { // Create a Workbook object var workbook = new Workbook(); workbook.SetCurrentFormat(WorkbookFormat.Excel2007);
// Create a Worksheet object that contains the actual data in the workbook var sheet = workbook.Worksheets.Add(string.Format("Data Sheet")); FillWorksheet(sheet, 1048576);
using (Stream stream = File.Open(@"F:\test.xlsx", FileMode.OpenOrCreate)) { Console.WriteLine("Begin writing file ...");
workbook.Save(stream);
Console.WriteLine("Complete, Pressure any key to continue ..."); } }
private void FillWorksheet(Worksheet sheet, int numberOfRows) { var sw = new Stopwatch(); sw.Start();
for (int index = 0; index < numberOfRows; index++) { var row = sheet.Rows[index]; row.Cells[0].Value = string.Format("{0:X8}", index);
var timeSpan = sw.Elapsed; row.Cells[1].Value = timeSpan.Hours * 10000000 + timeSpan.Minutes * 100000 + timeSpan.Seconds * 1000 + timeSpan.Milliseconds; row.Cells[2].Value = timeSpan.TotalMilliseconds; row.Cells[3].Value = timeSpan.Ticks;
row.Cells[4].Value = "F"; row.Cells[5].Value = "400"; row.Cells[6].Value = "50"; row.Cells[7].Value = "50, 30, 567"; row.Cells[8].Value = "2015-12-12"; row.Cells[9].Value = "Feature Co. Ltd."; row.Cells[10].Value = "Star War Plan"; row.Cells[11].Value = "+1-212-123-1234"; row.Cells[12].Value = "0123456789-1234567890"; row.Cells[13].Value = "abcdefgabcdefg hijklnm"; row.Cells[14].Value = "OPQRST UVWXYZ"; row.Cells[15].Value = "Description: For free use"; row.Cells[16].Value = "Indoors use only"; row.Cells[17].Value = "Maybe you know world trip is a nice thing, o ha ..."; row.Cells[18].Value = "E-Mail: your.name@outlook.com"; row.Cells[19].Value = "Say something, I feel lonely, thank you.";
if (index % 1000 == 0) { Console.WriteLine(index); }
}
sw.Stop(); }
}}
Most likely it would be due to the undo operations that the control is creating and the queuing of the changes for subsequent updating by the control. If you need to do that latter then try temporarily setting IsUndoEnabled to false while populating the Worksheet and then set it back to true. It will still queue up the changes which might be a problem but we can look to change that aspect in a later build.
Yes, it is due to undo operation. It is not a good idea to disable undo.I think it should be as only one operation, for example: var workbook = spread.Workbook; workbook.SetCurrentFormat(WorkbookFormat.Excel2007); spread.BeginUpdate(); // mark update beginning FillWorksheet(spread.ActiveWorksheet, 1048576); spread.EndUpdate(); // mark update endBetween BeginUpdate() and EndUpdate(), XamSpreadsheet only act as one operation.Thus only one huge operation, undo/redo also works. It's better.
Thank you.
So you want the 1m row change to be undoable? Having multiple changes be a single action is not an issue - one can do that by starting a transaction on the UndoManager. The problem is that such a large operation (setting 18m cell values) will require so much information to be undone that you are likely to still run out of memory. Setting the IsUndoEnabled temporarily is the most memory efficient way to accomplish what you are asking. Yes that means that that operation will not be undoable but I doubt that will be feasible anyway. A less efficient alternative would be to call Suspend and Resume on the UndoManager around the set of changes but that will be a little less efficient than setting IsUndoEnabled because the UndoUnits will still be created - they just won't be stored by the UndoManager. If you really need the Undo of such a change then likely you would need to create your own UndoUnit and manage the information in a more efficient manner (since at least in this test case you're dealing with lots of duplicate data).
It might be possible for us to (in situations where we know there are consistent - i.e. same value - large changes such as a large paste operation or row/column selection edit commit) store the undo information differently so that undo is possible but I'm not sure that is the case here. While you are using the same values across each row I'm assuming this is just test code and not indicative of what you are going to try to use it for which in all likelihood would have different values for each cell in each row (or at least not in any consistent pattern). It just seemed to me like you were doing some initialization which is not something that one would want to have undoable which is why I recommended disabling undo temporarily.
BTW, it would also be more efficient if you used the SetCellValue method on the WorksheetRow rather than allocating a WorksheetCell just to set the value.
Hi Linden,
Do you have any questions regarding Andrew's explanation?