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).
Hi Linden,
Do you have any questions on what Andrew said about grouping transactions?
You can group your multiple changes (the 5 or 500) into a single undo unit by wrapping the changes within a transaction - e.g. using the ExecuteInTransaction method of the UndoManager.
I don't want 1M rows change to be undoable. I just want the capability: a group operation as one unit undo.
May be 5 rows, or may be 500 row.
Do you have any questions regarding Andrew's explanation?