I have a grid on a windows form which needs to be filled when the form loads. Data comes from the DB. Currently im calling a LoadData() within form onload. So it takes considerable amount of time to load the screen as there is a DB call.
Is this a good approach ?
Can't i use a background worker and invoke the LoadData() asynchronously so that there wont be any delay in loading the window ?
Can someone explain me what is the best approach ?
Loading data synchronously from database on form load is not a good approach, you should load it asynchronously on separate thread.
You can use either of the following ways:
Thread class
Background Worker
Notes :
Query optimization and indexing the search traget tables
datagrids are complex objects and get time in updating and filling its ordinary( spcially when have more column )
your System hardware (Ram , CPU ,VGA, ... ) that must be considered
i think best approach is sort your query result and paging your result by Last code accessed and use this :
void NextPage()
{
Qr="select Top N from tbl where Code>LastCode order by Code asc";
//reading data
FirstCode=Drr["Code"];//for first record result
LastCode=Drr["Code"];//for last record result
}
void PreviousPage()
{
Qr="select Top N from tbl where Code < FirstCode order by Code asc";
//reading data
FirstCode=Drr["Code"];//for first record result
LastCode=Drr["Code"];//for last record result
}
Hope this Help
you can simply do that using Dispatcher to load data Asynchronously After form Has Rendered
/// <summary>
/// Handles the Loaded event of the Window control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.Dispatcher.BeginInvoke(DispatcherPriority.Background, new LoadDataDelegate(LoadData));
}
private delegate void LoadDataDelegate();
/// <summary>
/// Loads the data.
/// </summary>
private void LoadData()
{
List<string> numberDescriptions = new List<string>();
for (int i = 1; i <= 10000000; i++)
{
numberDescriptions.Add("Number " + i.ToString());
}
listBox1.ItemsSource = numberDescriptions;
}
Related
I have a Winforms application where I am trying to print a pdf document which has multiple layers on it.
But the problem is, This all operation are running on UI thread and it is hanging the UI(not responding) for long time.
I know, this is happening because of UI thread is blocked so, I have tried to make this operation asynchronous by the help of powerful async/await keyword but still my long running method is not being asynchronous. It is not coming forward from the await tasks and still opearation is taking the same time as like synchronous operation.
What I tried:
Please see below:
/// <summary>
/// Show Print Dialog
/// </summary>
private void ShowPrintDialog()
{
// Initialize print dialog
System.Windows.Controls.PrintDialog prtDialog = new System.Windows.Controls.PrintDialog();
prtDialog.PageRangeSelection = PageRangeSelection.AllPages;
prtDialog.UserPageRangeEnabled = false;
_printOptions.PrintQueue = null;
_printOptions.PrintTicket = null;
Enabled = false;
// if there is a default printer then set it
string defaulPrinter = prtDialog.PrintQueue == null ? string.Empty : prtDialog.PrintQueue.FullName;
// Display the dialog. This returns true if the user selects the Print button.
if (prtDialog.ShowDialog() == true)
{
_printOptions.PrintQueue = prtDialog.PrintQueue;
_printOptions.PrintTicket = prtDialog.PrintTicket;
_printOptions.UseDefaultPrinter = (defaulPrinter == prtDialog.PrintQueue.FullName);
}
// Re-enable the form
Enabled = true;
}
/// <summary>
/// Event raised when user clicks Print
/// </summary>
/// <param name="sender">Source of the event</param>
/// <param name="e">Event specific arguments</param>
private void cmdOk_Click(object sender, EventArgs e)
{
ShowPrintDialog();
if (_printOptions.PrintTicket != null)
{
//Set search Options
_print.ExportDataItem = true;
_print.FileName = SearchTemplateName;
//shows progress bar form.
using (frmPrintSearchResultsProgress frmProgress =
new frmPrintSearchResultsProgress(_print, this, _printOptions))
{
frmProgress.ShowDialog(this);
}
if (_print.ExportDataItem && !_print.DataItemExported && !_print.CancelExport)
{
MessageBox.Show("No Document printed.");
}
}
//Store selected options for current user
SaveOptions();
if (!SkipExport)
Close();
}
/// <summary>
/// Event raised when progress form is shown.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void frmExportSearchResultsProgress_Shown(object sender, EventArgs e)
{
try
{
Application.DoEvents();
dispatcher = Dispatcher.CurrentDispatcher;
// record export/print job start time
_startedUtc = DateTime.UtcNow;
_print.WritingToPdfIndicator = lblWritingPdfFile;
lblProgress.Text = Properties.Resources.PrintSearchResults;
await dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(DoDataItemPrint));
}
}
/// <summary>
/// Prints the selected data items.
/// </summary>
private void DoDataItemPrint()
{
// LONG RUNNING OPERATIONS..
// THIS OPERATION IS BLOCKING THE UI.
}
So, as per mentioned in above code when I opened the PringDialogForm then it is opening a Progress Bar form to see the progress of printing the document and from here frmExportSearchResultsProgress_Shown() event is fired and inside it, I am calling the DoDataItemPrint() method which is time consuming.
So, I tried to make frmExportSearchResultsProgress_Shown event as async/await but still operation is taking the same time as previous.
Can anyone please suggest me where I am doing wrong?
your frmExportSearchResultsProgress_Shown method starts on the UI thread
it then dispatches DoDataItemPrint to the ... same UI thread
it schedules a continuation (via await) so that when that incomplete thing happens, we get back into frmExportSearchResultsProgress_Shown, and since there's probably a sync-context in play here, the sync-context capture (implicit in await) would push us to ... the UI thread
As you can see: everything is happening on the UI thread.
If you want to not block the UI, you need to get off the UI thread. That could be as simple as using Task.Run to invoke DoDataItemPrint, but without knowing what that code contains, it is impossible to know whether you're using thread-bound controls to do the printing. If you are... it will be hard to get away from that.
(Background)
I am creating a stock market emulation program to further my C# knowledge.
I previously created this program in Java.
(Issue)
In Java, I was able to create a new thread in order to have it loop and update my GUI's labels every x amount of time. I researched methods to do this in C# and came across the timer, this didn't work for me and so I resorted to multi-threading.
Upon startup of the form I create a new thread
/// <summary>
/// stock emulation startup
/// </summary>
public stockEmu()
{
CheckForIllegalCrossThreadCalls = false; //if this is true, then cross-thread changes cannot be made (repeater cannot set labels if true)
initializeValues(); //this will set the startup values e.g stock prices and user money
ThreadStart loopThread = new ThreadStart( repeater ); //opens a new thread
Thread openThread = new Thread( loopThread ); //opens a new thread
openThread.Start(); //opens a new thread
InitializeComponent(); //initializes the form
this.updateLabels(); //needs to be after initializecomponent or null exception is thrown (because the labels are not drawn yet)
}
Here is the new thread method:
/// <summary>
/// infinite loop to execute every x seconds (using a new thread)
/// repeater uses cross-thread operation(s) and so CheckForIllegalCrossThreadCalls has been set to false
/// MSDN recommends against this, it is executed safely however
/// </summary>
private void repeater()
{
while( true )
{
Thread.Sleep( 5000 ); //sleep (pause) the thread for 5 seconds
instance = instance + 1; //add to the current instance (this is used to display what "day" we're on
changePrices(); //change the prices of the stocks
updateLabels(); //update the form's labels to display the new values
}
}
Here are the methods the repeater calls every 5 seconds
/// <summary>
/// this will change the prices every x seconds based on current prices etc
/// </summary>
private void changePrices()
{
marketController mC = new marketController();
for( int i = 0 ; i < stocks.Length ; i++ )
{
mC.changePrices( stocks [ i ] , i ); //mc for marketController, object reference, change prices will calc the price changes
}
return;
}
mC.changePrices doesn't actually do anything yet, but it does not get stuck there.
/// <summary>
/// method used to update all display labels every x seconds (based on repeater)
/// </summary>
public void updateLabels()
{
try
{
this.userMoneyLabel.Text = "Your money: " + this.getUserMoney().ToString(); //changes the user's money label
this.currentDayLabel.Text = "Day: " + this.getInstance().ToString(); //changes the day label
this.setStocksCombined(); //sets the array of combined stock prices and stock names
this.stockListBox.Items.Clear(); //clear the list box because it will constantly stack the items
this.stockListBox.Items.AddRange( stocksCombined ); //adds the combined array to the list box (stocks + stockNames)
}
catch( Exception e )
{
MessageBox.Show( "Error: " + e );
}
}
All of the relevant labels update fine, this problem also persisted before I added setStocksCombined() and so I don't believe the problem lies there.
This is the error that is thrown:
An unhandled exception of type 'System.OutOfMemoryException' occurred in mscorlib.dll
I haven't opened any additional threads apart from repeater, the repeater normally throws this error when it reached instance 7.
Thanks in advance (hopefully)
Edit:
Thanks to #RichardSzalay and #MichaelThePotato I have implemented a timer using this example: https://stackoverflow.com/a/12535833/6639187
One of the methods you use but have not listed is likely to be holding on to references. Use a memory profiler to find out where your app is leaking RedGate does a 14 day free trial.
If your app is not leaking then it is simply trying to load in too much data in one go.
You can also use the free tool ClrDbg to inspect the heap of your .Net application and find the cause of the memory issue
https://sourceforge.net/projects/clrdbg/
I am having a hard time creating a class where I can use ListBox to log events.
I know i have tonnes of articles on what I am asking on SO and google but that i didn't make out of it. So i am asking a little help here:
I have three classes:
1: WinForm - Where my list box is placed. From here I pass my listbox in the FileEnumeratorClass() constructor.
2: FileEnumeratorClass - Recieving listbox here and then passing it to logger class.
class FileEnumeratorClass {
UILogger logger;
/// <summary>
/// ctor
/// </summary>
public FileEnumeratorClass (ListBox lbLog)
{
logger = new UILogger(lbLog);
}
/// <summary>
/// Figures out what has changed between the src and destination
/// What is currently implemented does not work......the solution is to compare SRC and DESTINATION for the first time.
/// And verify with end user.
/// </summary>
public List<FileDetails> IdentifyChanges()
{
logger.AddToLog("Change detection initiated...");
//Deletes any existing cached package. Assuming it is in incosistent form
logger.AddToLog( "Cleaning up any cached local package.");
DeleteLocalPackage();
}
}
3: UILogger
public class UILogger
{
public UILogger(ListBox lb)
{
lbLog = lb;
}
// This delegate enables asynchronous calls for setting
// the text property on a control.
delegate void AddToLogCallback(string text);
public void AddToLog( string message)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (lbLog.InvokeRequired)
{
var d = new AddToLogCallback(AddToLog);
lbLog.Invoke(d, new object[] { message });
}
else
{
// add this line at the top of the log
lbLog.Items.Insert(0, message);
}
// keep only a few lines in the log
while (lbLog.Items.Count > 1000)
{
lbLog.Items.RemoveAt(lbLog.Items.Count - 1);
}
}
}
But the above code does not work as expected. All showing up when thread is done. What I need is to call the methods AddToLog() in the same sequence as they are written/called in FileEnumeratorClass -> IdentifyChanges().
You need to call Control.Invalidate() method whenever you want the listbox to update its contents to force listbox to redraw itself.
Please note that these control are thread safe which means any async or child thread must not update any UI control. Use proper callbacks to UI thread and let the UI thread update the control.
public List<FileDetails> IdentifyChanges()
{
logger.AddToLog("Change detection initiated...");
//Deletes any existing cached package. Assuming it is in incosistent form
logger.AddToLog( "Cleaning up any cached local package.");
//Invalidate the control to redraw.
logger.Invalidate();
DeleteLocalPackage();
}
With this code:
using (pbDialog = new pbDialogs())
{
ProgressBar = new frmProgress(this, false);
ProgressBar.SetProgressLabelText("Inventory Data");
MessageBox.Show("Set progress label text to Inventory data");
typeProgress = (int) ProgressStates.ProgressQRY;
ProgressBar.label1.Text += " (Receiving)";
if (pbDialog != null)
{
MessageBox.Show("pbDialog is not null");
//pbDialog.ShowDialog(ProgressBar, this);
}
else
{
MessageBox.Show("pbDialog IS null");
ProgressBar.ShowDialog();
}
ProgressBar = null;
MessageBox.Show("Made it to compressDB()");
compressDB();
. . .
}
I see "Set progress label text to Inventory data"
then "pbDialog is not null"
then "Made it to compressDB()"
Nothing too odd there; but if I uncomment the line that is commented above, I see only "pbDialog is not null"
It is hanging for some reason as a result to the call to ShowDialog(); what is really strange, though, is that this prevents "Set progress label text to Inventory data" from displaying. Why would that be the case?
Note: I think the "pb" in the code stands for "peanut brittle" or some such; I'm pretty sure about the "brittle" part, anyway.
UPDATE
Yeah, the use of ShowDialog() with pbDialog is one of scads of examples that the original coder was practicing job security by obscurity - but then he [un]fortunately skedaddled, leaving in his wake a cesspool of spaghetti/eggshell code with no comments, misleading names and every sort of bizarre and convoluted, counterintuitive practice imaginable in the witches brew he purportedly considered a masterpiece of elegant design and clever-clever tricks.
pbDialog is an instance of a class (pbDialogs). Just to give you a taste of how macabre and convoluted and tangled it all is, here is that class:
public class pbDialogs : IDisposable
{
private static Form m_top;
public pbDialogs()
{
} // pbDialogs Constructor
public static void Activate( Form form )
{
form.Capture = true;
IntPtr hwnd = OpenNETCF.Win32.Win32Window.GetCapture();
form.Capture = false;
OpenNETCF.Win32.Win32Window.SetForegroundWindow( hwnd );
} // Activate
/// <summary>
/// This method makes ShowDialog work the way I want, I think.
/// </summary>
/// <remarks>
/// Here is what it does:
/// 1. Sets the caption of the new window to the same as the caller's.
/// 2. Clears the caption of the parent so it won't show up in the task list.
/// 3. When the ShowDialog call returns, brings the previous window
/// back to the foreground.
/// </remarks>
/// <param name="dialog"></param>
/// <param name="parent"></param>
public void ShowDialog( Form dialog, Control parent )
{
Control top = parent.TopLevelControl;
string caption = top.Text;
dialog.Text = caption;
top.Text = "--pending--"; // Don't show parent in task list
dialog.Activated += new EventHandler( form_Activated );
dialog.Closed += new EventHandler( form_Closed );
m_top = dialog; // New top-most form
dialog.ShowDialog();
m_top = (Form)top; // The top dialog just changed
dialog.Activated -= new EventHandler( form_Activated );
dialog.Closed -= new EventHandler( form_Closed );
top.Text = caption; // Make visible in task list again
Activate( (Form)top ); // And make it the active window
} // ShowDialog
/// <summary>
/// If one of our other windows, such as the main window,
/// receives an activate event, it will activate the current
/// top-most window instead.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void form_Activated( object sender, EventArgs e )
{
if( (m_top != null) && !(sender == m_top) ) // Is this the top-most window?
Activate( m_top ); // No, activate the top dialog
} // form_Activated
private static void form_Closed( object sender, EventArgs e )
{
m_top = null; // When you close the top dialog, it's not top anymore
} // form_Closed
#region IDisposable Members
public void Dispose()
{
// TODO: Add pbDialogs.Dispose implementation
}
#endregion
} // class pbDialogs
There is also a "related" ProgressBar -- a form which shares a file with pbDialogs, and whose instance variable is defined in the file that contains the code above thusly:
public static frmProgress ProgressBar;
This is definitely "whack-a-mole" code - if I make one small, seemingly innocuous change, all Dallas breaks loose in what even a semi-sane person would consider a completely unrelated part of the code.
This may be an indication of just how squirrelly this code/project is: I will make a new build after commenting out a couple of lines, and the size of the file will change from 400KB to 408, or from 412 to 408, etc. It's not normal behavior for an .exe to change that much in size (in a relative sense) with such a small change, is it?
UPATE 2
This, in frmProgress (which has both "public class frmProgress : System.Windows.Forms.Form" and "public class pbDialogs : IDisposable") scares me:
using System.Windows.Forms;
using OpenNETCF.Windows.Forms;
The second (OpenNETCF) is grayed out, indicating it's not really used, but it may be that it was previously used, and somehow that "Windows.Forms" code inadvertently got switched to "System" code, and that may be contributing to its current groundsquirellyness.
ShowDialog is generally a blocking call. The code will not continue past this until the dialog is closed.
I need some help on showing Progress bar upon loading data on my DataGridView. Any sample code?
Thanks.
regards,
kurt
Start the fetching of the data on another thread in order to not lock up the UI thread. You could expose a method on the UI (presume you have separation of layers here) and call that method from your business layer. Windows Form controls expose a InvokeRequired flag that you can use to check if you are calling the control from the correct thread. If you are not on the right thread you can call a delegate to do so.
/// <summary>
/// Delegate to notify UI thread of worker thread progress.
/// </summary>
/// <param name="total">The total to be downloaded.</param>
/// <param name="downloaded">The amount already downloaded.</param>
public delegate void UpdateProgressDelegate(int total, int downloaded);
/// <summary>
/// Updates the progress in a thread-safe manner.
/// </summary>
/// <param name="total">The total.</param>
/// <param name="downloaded">The downloaded.</param>
public void UpdateProgress(int total, int downloaded)
{
// Check we are on the right thread.
if (!this.InvokeRequired)
{
this.ProgressBar.Maximum = total;
this.ProgressBar.Value = downloaded;
}
else
{
if (this != null)
{
UpdateProgressDelegate updateProgress = new UpdateProgressDelegate(this.UpdateProgress);
// Executes a delegate on the thread that owns the control's underlying window handle.
this.Invoke(updateProgress, new object[] { total, downloaded });
}
}
}
Or you could just use a BackgroundWoker ;)