StackOverflowException with DataGridView - c#

This is an odd one. I have a DataGridView. I'm setting its DataSource with a List containing objects of my own custom class. There are about 50,000 items in the list. I defined all of the columns I wanted to be visible in the Designer and set AutoGenerateColumns to false.
As soon as I set the DataSource to my list, it is immediately populated correctly. I can scroll up and down, select different rows. Everything is good. But when I scroll all of the way down and then let the window containing the DataGridView lose focus everything freezes up and after a short while the stack overflows as such:
System.Drawing.dll!System.Drawing.SafeNativeMethods.Gdip.GdipDeleteGraphics(System.Runtime.InteropServices.HandleRef graphics) + 0x2a bytes
System.Drawing.dll!System.Drawing.Graphics.Dispose(bool disposing) + 0x56 bytes
System.Drawing.dll!System.Drawing.Graphics.Dispose() + 0x12 bytes
System.Drawing.dll!System.Drawing.Font.GetHeight() + 0xc8 bytes
System.Drawing.dll!System.Drawing.Font.Height.get() + 0xb bytes
System.Windows.Forms.dll!System.Windows.Forms.DataGridViewRow.DataGridViewRow() + 0x44 bytes
System.Windows.Forms.dll!System.Windows.Forms.DataGridViewRow.Clone() + 0x44 bytes
System.Windows.Forms.dll!System.Windows.Forms.DataGridViewRowCollection.this[int].get(int index) + 0xa8 bytes
System.Windows.Forms.dll!System.Windows.Forms.DataGridView.DataGridViewAccessibleObject.GetChild(int index) + 0xbd bytes
System.Windows.Forms.dll!System.Windows.Forms.DataGridViewRow.DataGridViewRowAccessibleObject.Bounds.get() + 0x76 bytes
System.Windows.Forms.dll!System.Windows.Forms.DataGridViewRow.DataGridViewRowAccessibleObject.Bounds.get() + 0x83 bytes
System.Windows.Forms.dll!System.Windows.Forms.DataGridViewRow.DataGridViewRowAccessibleObject.Bounds.get() + 0x83 bytes
System.Windows.Forms.dll!System.Windows.Forms.DataGridViewRow.DataGridViewRowAccessibleObject.Bounds.get() + 0x83 bytes
System.Windows.Forms.dll!System.Windows.Forms.DataGridViewRow.DataGridViewRowAccessibleObject.Bounds.get() + 0x83 bytes
System.Windows.Forms.dll!System.Windows.Forms.DataGridViewRow.DataGridViewRowAccessibleObject.Bounds.get() + 0x83 bytes
System.Windows.Forms.dll!System.Windows.Forms.DataGridViewRow.DataGridViewRowAccessibleObject.Bounds.get() + 0x83 bytes
System.Windows.Forms.dll!System.Windows.Forms.DataGridViewRow.DataGridViewRowAccessibleObject.Bounds.get() + 0x83 bytes
System.Windows.Forms.dll!System.Windows.Forms.DataGridViewRow.DataGridViewRowAccessibleObject.Bounds.get() + 0x83 bytes
System.Windows.Forms.dll!System.Windows.Forms.DataGridViewRow.DataGridViewRowAccessibleObject.Bounds.get() + 0x83 bytes
System.Windows.Forms.dll!System.Windows.Forms.DataGridViewRow.DataGridViewRowAccessibleObject.Bounds.get() + 0x83 bytes
System.Windows.Forms.dll!System.Windows.Forms.DataGridViewRow.DataGridViewRowAccessibleObject.Bounds.get() + 0x83 bytes
...
For some reason the DataGridViewRow.DataGridViewRowAccessibleObject.Bounds.get() method is calling itself into oblivion. The whole stack seems rather strange to me. Why would Font.Height.get() ever call the DataGridViewRow?
EDIT:
I was asked for some code. This is the designer generated code for the DataGridView and its columns:
//
// dataGridView
//
this.dataGridView.AllowUserToAddRows = false;
this.dataGridView.AllowUserToDeleteRows = false;
this.dataGridView.AllowUserToOrderColumns = true;
this.dataGridView.AllowUserToResizeRows = false;
this.dataGridView.BackgroundColor = System.Drawing.SystemColors.Window;
this.dataGridView.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.dataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dataGridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
this.Date,
this.Type,
this.Job,
this.Mix,
this.Entry});
this.dataGridView.Location = new System.Drawing.Point(8, 96);
this.dataGridView.Name = "dataGridView";
this.dataGridView.ReadOnly = true;
this.dataGridView.RowHeadersVisible = false;
this.dataGridView.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
this.dataGridView.Size = new System.Drawing.Size(1152, 504);
this.dataGridView.TabIndex = 10;
this.dataGridView.SelectionChanged += new System.EventHandler(this.dataGridView_SelectionChanged);
//
// Date
//
this.Date.DataPropertyName = "FormattedTime";
this.Date.HeaderText = "Date/Time";
this.Date.Name = "Date";
this.Date.ReadOnly = true;
//
// Type
//
this.Type.DataPropertyName = "FormattedType";
this.Type.FillWeight = 60F;
this.Type.HeaderText = "Type";
this.Type.Name = "Type";
this.Type.ReadOnly = true;
this.Type.Width = 60;
//
// Job
//
this.Job.DataPropertyName = "Job";
this.Job.FillWeight = 80F;
this.Job.HeaderText = "Job No.";
this.Job.Name = "Job";
this.Job.ReadOnly = true;
this.Job.Width = 80;
//
// Mix
//
this.Mix.DataPropertyName = "Mix";
this.Mix.FillWeight = 80F;
this.Mix.HeaderText = "Mix No.";
this.Mix.Name = "Mix";
this.Mix.ReadOnly = true;
this.Mix.Width = 80;
//
// Entry
//
this.Entry.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
this.Entry.DataPropertyName = "FormattedSummary";
this.Entry.HeaderText = "Entry";
this.Entry.Name = "Entry";
this.Entry.ReadOnly = true;
When time comes around to populate the grid view, I simply do a:
dataGridView.DataSource = myList;

Fix
No fix is known at this time.
Workaround
Disabling the Tablet PC Input Service (a.k.a. tabtip.exe) from the Services panel in Control Panel.
Details
A few weeks ago I contacted Microsoft Developer support about this issue. I corresponded with a support engineer on the WinForms team and we figured out that this issue was in some way caused by Accessibility.dll being loaded into my application whenever Microsoft's Tablet PC Input Service (tabtip.exe) was running. This service is present on at least all Windows 7 Enterprise installations but is generally only running if you have a tablet PC or have ever installed any sort of pen input device on your PC. I had used a Wacom Bamboo pen tablet on my PC in the past so it made sense why this service was running. Note that it was determined that neither the method I used to populate the DataGridView nor any of the properties I set had anything to do with the issue.
Accessibility.dll is a Microsoft library that allows certain peripherals (pen tablets and especially assistive devices) to more easily interact and get extra information about forms and the controls contained on forms. I'm not 100% sure on this but I believe that this library is loaded into every running Windows process automatically if such a peripheral is installed.
After examining a dump that I had provided, the Microsoft engineer was puzzled by the code path that the DataGridView decided to go down and found that Accessibility.dll was the culprit that allowed let it happen. He admitted that DataGridView should not be doing this and that this seemed like a problem on their end. However, he was unable to reproduce the problem on his end even after turning on the Tablet PC Input Service on both his PC and a freshly made Windows 7 VM. So while he was able to identify the key players that caused the problem on my PC he was not able to find root cause and hence unable to pursue it further.
This may be attributed to my particular pen tablet device (Wacom) or something else all together. Its unknown. If anyone else encounters this problem, please contact me. The engineer invited me to contact him if I was ever able to narrow down the cause. For now, just keeping the service off prevents the issue.

I have encountered a very similar scenario of a StackOverflowsException in DataGridView.
Conditions:
A WinForm hosting a datagridview control. DataGridView properties ReadOnly and VirtualMode set to true.
VirtualMode's OnCellValueNeeded implemented as per MSFT documentation (http://msdn.microsoft.com/en-us/library/2b177d6d.aspx)
Load a sizable amount of data (15 columns x 1 x 10^5 rows)
Behavior of datagridview is normal (i.e.: responsiveness of GUI, row and cell selection, vertical & horizontal scrolling) if displaying only 10 Columns X 30 rows [300 cells]. When I maximize the display such that the number of displayed cells is much larger, a StackOverflowsException in DataGridView is fired.
I, too, have an attached Wacom tablet to my PC.
The problem disappears if I stop the Tablet PC Input Service (tabtip.exe)
So...in my case, indeed, Franks workaround is in the right direction and supports the idea that 1. the Tablet PC Input Service is a culprit and 2. the number of displayed (active) cells in the screen has an effect on firing the exception.

First of all, thanks to #Frank Weindel for pointing to this WEIRD problem (only microsoft can come up with this).
For those who doesn't want to disable the "Tabtip.exe" as part of the requirement, say your application is for a tablet and only possible input method is Tabtip, there is an workaround.
You can kill the Tabtip at the event where the crash occurs. For me, it is when i select items at row beyond 60,000. This is what I did.
Private Sub DataGridView1_Scroll(sender As Object, e As ScrollEventArgs) Handles DataGridView1.Scroll
Call closeKeyboard()
End Sub
'When a user have to type something in textbox'
Private Sub Lookup_Tbox_MouseDown(sender As Object, e As MouseEventArgs) Handles Lookup_Tbox.MouseDown
Call OpenKeyboard()
End Sub
Public Sub OpenKeyboard()
System.Diagnostics.Process.Start("tabtip.exe")
End Sub
Public Sub closeKeyboard()
Dim proc() As System.Diagnostics.Process = Process.GetProcessesByName("tabtip")
For i As Integer = 0 To proc.Length - 1
proc(i).Kill()
Next i
End Sub
Hope it helps someone :) Happy fighting :)

I am not completly understand ur promblem but I think ur problem is slowness
of datagridview and memory
I Have a solution :
1:Break datagridview row into pages like only show maximum 100 row in Datagridview and next hundread in button or other control click it help to reduce ur video memory
2: Enable Double Buffred on to DataGridView
3: Use Suspendlayout and resume layout function to easily draw item

I had the same problem GridView with 50K records. Using the ScrollBar or simply moving large number of records caused the StackOverflowsException in DataGridView. After reading the above I simply stopped the virtual on screen keyboard (my screen is touch screen) Service description "Enables Touch Keyboard and Handwriting Panel pen and ink functionality"
As soon as this was stopped the GridView worked perfectly.

I had exact problem as described in question, after reading here and my own experimenting, this solved my problem:
private void dataGridView1_Scroll(object sender, ScrollEventArgs ex)
{
try
{
dataGridView1.Focus();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}

Related

Enforce program settings

I have made a small c# winforms program which among other stuff, prints barcodes. On some client machines where it operates, some other software for printing barcodes overrides my settings for printing the barcode and it resizes just the barcode.
If I change the code just for that machine, it either repositions the image (makes it smaller as well), or stretches it so wide that it cuts off one third of it. Currently I am only using the printing settings to set the margins to 0 and set the paper mode to landscape in the print dialog, and the image size is fixed in the printing process(I used constants). Below is the code for when the user clicks the print button, and also two constants defined outside of it.
const int barcodeX = 570;
const int barcodeY = 135;
private void Print_Click(object sender, EventArgs e)
{
DocPrint.DocumentName = "Document";
elements = 0;
PrintDialog.Document = DocumentDrucker;
DocPrint.DefaultPageSettings.Landscape = true;
DocPrint.DefaultPageSettings.Margins.Top = 0;
DocPrint.DefaultPageSettings.Margins.Left = 0;
DocPrint.DefaultPageSettings.Margins.Right = 0;
DocPrint.DefaultPageSettings.Margins.Bottom = 0;
//DocumentDrucker.OriginAtMargins = false;
if (PrintDialog.ShowDialog() == DialogResult.OK)
DocPrint.Print();
}
Also the lines to encode and print the barcode in the printing process. The barcodePoint is where the barcode is positioned, on the paper.
b.Encode(TYPE.CODE128A, "SBD" + currentItem.Text.Substring(0, 3) + currentItem.Text.Substring(4), Color.Black, Color.Transparent, barcodeX, barcodeY);
graphic.DrawImage(b.EncodedImage, barcodePoint);
The program runs fine under any other circumstances, so I have identified the problem, I just do not know how to go around the other software's settings. I have already reinstalled the drivers for the printer (no success), as well logged in as a different user on the same computer, and that made my program work. So it has something to do with the software installed on it, I just don't know what.
At first I thought it was a problem with the drivers and the OS, but I tried it on other computers and it worked fine as well. The problem persists just on the computers with the software.
Is there any way to make my program force it's printing settings for the barcode image, since the software installed on the machines always uses it's own settings? Or to make sure that the settings in my code will not be changed?
Uninstalling the software is not an option, as it is used for other processes.
EDIT: I have run the program from both my user logon (no software) and the target machine user (both on the same machine). The settings were identical in both cases, the only thing different was that on the target machine, the software was installed. What are some ways to make sure that my settings are enforced, instead of an X program overwriting them?

Get status of night light mode in Windows 10

I am using the desktop duplication api to grab the screen content and as it turns out, the new night light mode ('Nachtmodus' in German) is not applied in the grabbed screen content.
How do I read (if possible directly in c#) the night mode status (enabled, color shift amount)?
or
How can I tell Windows to give me the color shifted image using the desktop duplication api?
Basically, I want to know the state of what is configured inside these red boxes:
Background: I am working on an ambilight implementation and if the night light mode is enabled, the color shift is not reflected in the LEDs around my screen and so the colors are off between screen content and 'around screen'.
This method works for me in Windows 10 Version 2004
private static bool IsNightLightEnabled()
{
const string BlueLightReductionStateKey = #"SOFTWARE\Microsoft\Windows\CurrentVersion\CloudStore\Store\DefaultAccount\Current\default$windows.data.bluelightreduction.bluelightreductionstate\windows.data.bluelightreduction.bluelightreductionstate";
using (var key = Registry.CurrentUser.OpenSubKey(BlueLightReductionStateKey))
{
var data = key?.GetValue("Data");
if (data is null)
return false;
var byteData = (byte[])data;
return byteData.Length > 24 && byteData[23] == 0x10 && byteData[24] == 0x00;
}
}
You can check the output of
GetDeviceGammaRamp
Function from the Win API. Compare the output to Night Light ON and OFF and you should detect it.
Or you can try to monitor this Reg key for changes
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\CloudStore\Store\Cache\DefaultAccount\$$windows.data.bluelightreduction.settings\Current
I think I found the registry entry that reflects the current night light status.
[HKEY_CURRENT_USER\Control Panel\Quick Actions\Control Center\QuickActionsStateCapture]
"Toggles"="Toggles,...,Microsoft.QuickAction.BlueLightReduction:true,..."
Although it is not very reliable because I think it requires that this particular toggle should be visible...

How to determine specific printer properties for a printer job?

I am working on some code that monitors the printer queue and then uses the event information to collect some specifics about the job, including # pages, orientation, whether or not it was color and how many copies were requested.
I catch the events using the code from Merrion Computing (which is now open source); which handles the interop.
In the case of Color, it is supposed to be stored in JOB_INFO_2.pDeviceMode.dmColor; however no matter how I submit the job (color or black and white using the printer properties printing from several apps, including word and adobe) it always indicates color. I debugged through that code directly, and the interop appears to be correct, so then I used the JobId from the event to query the print system via .NET with the code (below); and it contains exactly the same settings for copies and color.
int iJobId = e.PrintJob.JobId;
LocalPrintServer printServer = new LocalPrintServer();
PrintQueueCollection queueCollection = printServer.GetPrintQueues();
foreach (PrintQueue queue in queueCollection)
{
queue.Refresh();
if(queue.FullName.Equals(e.PrintJob.PrinterName,StringComparison.OrdinalIgnoreCase))
{
int? iPageCount;
PrintJobInfoCollection jobs = queue.GetPrintJobInfoCollection();
foreach(PrintSystemJobInfo job in jobs)
{
job.Refresh();
if(job.JobIdentifier==iJobId)
{
iPageCount = job.NumberOfPages;
}
}
//-- Found the Printer...
int? iCopyCount=queue.CurrentJobSettings.CurrentPrintTicket.CopyCount;
PageOrientation? eOrientation = queue.CurrentJobSettings.CurrentPrintTicket.PageOrientation;
OutputColor? eColor = queue.CurrentJobSettings.CurrentPrintTicket.OutputColor;
Debug.WriteLine("queue=" + queue.FullName + ", Copies=" + iCopyCount.Value + ",Color=" + eColor.ToString() + ", pagecount=" + "unk" /*iPageCount.Value*/ + ", Orientation=", eOrientation.ToString());
Debug.WriteLine("---");
}
}
Has anyone seen a reliable way to retrieve the number of copies and page count (preferably using .NET) for a specific printer job?
I
I did find this post describing the same type of problem, but there wasn't a resolution there.
Determine current print job color using C#
It should also be noted that the WMI Code from the above article also returns color.
I went in an enabled the eventlog for printing (http://www.papercut.com/kb/Main/LogPrintJobsInEventViewer). Looking at the details of the print event; the color setting is as expected "2", which indicates grayscale.
It is pretty clear that the windows subsystem is receiving the requested setting; however I have been unsuccessful to retrieve the value using WMI, System.Printing's namespace, or the interop from Merrion's print monitoring library where the values all indicate that the job is color with the correct number of pages and copies.
Is it possible to grab the spool file generated for this print to check that it is setting the dmColor setting itself?
The setting you are getting from the event log, 2, corresponds to DMCOLOR_COLOR not DMCOLOR_MONOCHROME so it seems the colour setting in the log thinks it is colour too.
It may be that the printer driver is being a bit sneaky in submitting the job as colour when it creates it but then sending a "set device settings" message in the spool that changes it to monochrome? If so there should be an SPT_DEVMODE record in the spool file.
Check this article for a spool file reader: http://www.codeproject.com/Articles/10586/EMF-Printer-Spool-File-Viewer
You need to refresh your jog until the flag IsSpooling becomes false.
for (int i = 0; i < jobs.Count(); i++)
{
try
{
int timeOut = 20000;
var jobInfo = jobs.ElementAt(i);
while (jobInfo.IsSpooling && timeOut > 0)
{
Thread.Sleep(100);
timeOut-=100;
jobInfo.Refresh();
} var pages = Math.Max(jobInfo.NumberOfPages,jobInfo.NumberOfPagesPrinted);
}
}

Squeezing more speed out of Windows print spooler

Our customer and I are trying to find a way to eliminate our printing bottleneck in a labeling system my company integrated at their warehouse.
The way our system is set up, a box scans, my software does this:
PrintDocument pd = new PrintDocument();
pd.PrinterSettings.PrinterName = PrinterName;
pd.PrintController = new StandardPrintController(); //Prevent print windows
pd.DefaultPageSettings.Landscape = false;
pd.DefaultPageSettings.Margins = new Margins(0, 25, 25, 0);
pd.PrintPage += (sender, args) =>
{
Image i = Image.FromFile(#"D:\Product\Labels\" + LabelID + ".png");
Rectangle m = args.MarginBounds;
args.Graphics.DrawImage(i, m);
};
pd.Print();
which sends it to the spooler. The program runs on a rack server with Windows Server 2012, and we're printing shipping labels to five label tampers powered by Intermec PX4i label printers. Each print job is sent from a different thread, because our scanpoint processing is asynchronous. Right now, the fastest our system can handle is 5 boxes, one on each line, every 2.75 seconds, and they want to speed it up to 5 boxes every 2 seconds - the problem is, if we let it go any faster, print jobs start getting held, and spoolsv.exe starts eating up whatever CPU load my program and SQL Server aren't.
Unfortunately, our customer can only provide the monochrome .PNG image files for the labels (each about 27kb, which you wouldn't think would bog down a print spooler...) and not IPL/ZPL equivalents (which we could just send to the printers via TCP in a matter of milliseconds).
Is there a way - either in the code or in the print settings - that I can eliminate this bottleneck? Please let me know if there's anything on which you'd like me to elaborate.

Direct2D / GDI+ and slow Windows forms drawing - What can be done?

I'm working a lot with Visual Studio 2008, .NET C# 2.0-3.5 and Windows Forms and I have noticed, like many before me, that GDI+ is extremely slow in drawing Controls. Note that I do not deal with images (JPG, GIF etc) very much. Images are only as icons in certain places. This is actually Controls/Forms/etc that are slow to draw.
The issue is that you can see Controls being drawn and it can take several seconds for a seemingly easy set of Controls to be drawn. Ie, its lagging and horrible.
I have made tests where I just put a number of Labels (40-50) on a form, hitting F5 to run and have to wait for them to be drawn. Again, lag and not a very nice experience.
So, then there is WPF that might address this problem, but I/we are not ready to move to WPF. So I'm looking around for workarounds or fixes and I stumbled upon Direct2D, and when reading on that some other libraries.
Put Im a tad puzzled and thus these questions:
1)
First of, what I want is a fairly neat and simple way to just replace GDI+ with something faster and hardware accelerated approach. Is it possible to do that without going over to WPF and without having to rewrite all my Windows Forms code?
Whenever I read anything on Direct2D I see long blocks of usually horrible C++ code, telling me on how to manually write code to for drawing. I do not want that.
2)
While reading on the net, I stumbled upon SlimDX, but I cannot figure out how to use it (and I admit, I havent tried very much as of writing). Lets say I already have a GUI-application (Windows Forms, standard C# code) - can I somehow use SlimDX (or something like it) to just "replace" GDI+ without too much rewriting?
My problem is I cannot find any examples or such telling me if it is possible to use SlimDX, Direct2D or other similiar things in my already-created Windows Forms software, and if it is possible - how to do it.
Hope Im not too fuzzy =)
==EDIT== 2010-09-22
I have made some tests in my real app, and isolated one of the slow things to this:
When I add text to some Labels in a UserControl, the Controls resize themselves to fit the text. For example, the containing GroupControl adapts a bit to the size of the text that was just added to the .Text-property of the Labels.
There are about 10 Label controls. The first time the labels are updated, and thus sizes are changed, the whole process takes about 500 ms. The second time the labels are updated, and no size changes, it takes about 0 ms.
==EDIT 2== 2010-09-22
Found one of the slow-downs. Then adding a String to the Text-property it is slow if the text that is being added differs in string length from the text that was there before the update.
Im using DevExpress libraries, and the LabelControls can be set to AutoSizeMode. If I set that to "None" then the lag will go away when adding text that differs in length from the previous text. I guess this problem will be the same for the normal Label-control as it also has a AutoSize = true/false setting.
However, its a "workaround" but still proves my point - its really slow when resizing which is pretty lame.
Many of the posters above come with good points. I've created a 3D CAD application in GDI+ myself, and found it plenty fast enough if it's implemented correctly. Using Controls, however, immediately strikes me as a very awkward way to do things. A Control is a fairly big object and there are numerous reasons to make your own in this case.
I'd advise you to look into a retained mode drawing system. It's easy to implement and would cover your situation in most cases. You'd have to create the drawing logic yourself, but that's just fun and would give you more flexibility :)
On your first question, I had to use GDI to do some image processing stuff which was taking ages under GDI+. This was 4-5 years ago and working with GDI using managed C# was a pain - not sure how much it has changed now. There are many good and fast functions such as BitBlt which are very fast in drawing but you need to be very careful with releasing resources (handles) and memory. I also had another issue and that was saving the result as JPEG and it is non-existent in GDI so I had to use CxImage to read the HBitmap and then save it.
All in all, GDI is very fast and robust. If there better abstractions in DirectX, probably you are better off using them.
I'm looking at some of the same issues. I am writing an application that needs to be able to render 2d graphics very efficiently since some users could have 10 - 50 windows open simultaneously. One thing to consider that no one else talked about here is the fact that direct2d can only be used on computers with Vista with service pack 2 and up. Also, according to this link:
http://www.tomshardware.com/news/msft-windows-xp-windows-7-market-share-win7,13876.html
38.5% of all Windows users were still using XP as of Nov, 2011. So, if selling the app to a significant amount of users still running XP is a concern (or your marketbase is to 3rd world countries that are mainly using XP), then you should either go with:
Direct2d for newer operating systems and GDI+ for XP systems.
XNA - which is compatible with XP and can also be used with newer operating systems. See this link: http://msdn.microsoft.com/en-us/library/bb203925.aspx
SlimDX - mentioned in the first answer. Supports XP as well as newer operating systems. See:
http://slimdx.org/ and http://slimdx.org/features.php
OpenTK if you care about compatibility between Windows, Linux, Max, etc.
You should also be aware that there was a bug with GDI+ that caused it to suffer very poor performance when it was initially released. See the following link why some developers claim that Microsoft broke the gui for Windows7 for apps using GDI+:
http://www.windows7taskforce.com/view/3607
or do a web search from your favorite search engine with this string: "gdi+ bug slow on windows 7".
You could try managed directx, but they no longer support it (moved on to XNA). Honestly, unless you've got a shitty computer or a ton of controls, I don't know why it'd be lagging so bad. If you're doing some cpu intensive stuff on your main thread, move it to a separate thread. That's the only other reason I can think of that'd cause that kind of lag.
We use SlimDX in our C# app.... But we're actually doing 3D. We wrote our own 2D lib to be able to do simple 2D drawing. SlimDX is just a lightweight wrapper around DirectX. So you'll get all of the pro's and cons of DirectX. Like that it's your problem to emulate the videocard if its not present.
If you want something for drawing to offscreen bitmaps, I'd go for WPF, as it is well integrated with C#, works mostly everywhere, and accellerated when there's hardware available. You can copy the output to a bitmap and use that in regular GDI/Winforms. But it will only be faster than GDI+ if you do fairly complex stuff (lots of filters, mixing textures etc...).
Edit:
In response to the comments, I built a little sample form. There is a seconds long wait when switching the first time, but after that it's responsive. Slower than I'd expect, but by all means usable. Would like Ted to comment if this is about the performance he is seeing in his app.
public class Form1 : Form
{
public Form1()
{
InitializeComponent();
// *** EDIT ***
this.tabPage1.SuspendLayout();
this.tabPage2.SuspendLayout();
this.tabControl1.SuspendLayout();
this.SuspendLayout();
FillTab(tabPage1, Color.White);
FillTab(tabPage2, Color.Yellow);
// *** EDIT ***
this.tabPage1.ResumeLayout(false);
this.tabPage2.ResumeLayout(false);
this.tabControl1.ResumeLayout(false);
this.ResumeLayout(false);
}
private static void FillTab(TabPage tabPage, Color color)
{
for (int i = 0; i < 200; ++ i)
{
int left = (i % 5) * 320;
int topOffset = (i / 5) * 23;
tabPage.Controls.Add(new Label { Left = left, Top = topOffset, Width = 100, Text = "Label" });
tabPage.Controls.Add(new TextBox() { BackColor = color, Left = left + 100, Top = topOffset, Width = 100, Text = tabPage.Text });
tabPage.Controls.Add(new ComboBox { BackColor = color, Left = left + 200, Top = topOffset, Width = 100 });
}
}
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.tabControl1 = new System.Windows.Forms.TabControl();
this.tabPage1 = new System.Windows.Forms.TabPage();
this.tabPage2 = new System.Windows.Forms.TabPage();
this.tabControl1.SuspendLayout();
this.SuspendLayout();
//
// tabControl1
//
this.tabControl1.Controls.Add(this.tabPage1);
this.tabControl1.Controls.Add(this.tabPage2);
this.tabControl1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tabControl1.Location = new System.Drawing.Point(0, 0);
this.tabControl1.Name = "tabControl1";
this.tabControl1.SelectedIndex = 0;
this.tabControl1.Size = new System.Drawing.Size(292, 266);
this.tabControl1.TabIndex = 0;
//
// tabPage1
//
this.tabPage1.Location = new System.Drawing.Point(4, 22);
this.tabPage1.Name = "tabPage1";
this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
this.tabPage1.Size = new System.Drawing.Size(284, 240);
this.tabPage1.TabIndex = 0;
this.tabPage1.Text = "tabPage1";
this.tabPage1.UseVisualStyleBackColor = true;
//
// tabPage2
//
this.tabPage2.Location = new System.Drawing.Point(4, 22);
this.tabPage2.Name = "tabPage2";
this.tabPage2.Padding = new System.Windows.Forms.Padding(3);
this.tabPage2.Size = new System.Drawing.Size(245, 217);
this.tabPage2.TabIndex = 1;
this.tabPage2.Text = "tabPage2";
this.tabPage2.UseVisualStyleBackColor = true;
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(292, 266);
this.Controls.Add(this.tabControl1);
this.Name = "Form1";
this.Text = "Form1";
this.tabControl1.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.TabControl tabControl1;
private System.Windows.Forms.TabPage tabPage1;
private System.Windows.Forms.TabPage tabPage2;
}

Categories