Hi again Nicholas/Mark,
After attempting to load the data on another thread it seems that - as we
predicted - the "choke" still occurs ON the databind.
Now that we've tried, I will not ask that we search for another similar
method since we all knew this was a bad idea to begin with and will see what
I can do about laying down the law on my supervisors who want things done
"their way".
If you are interested, I've posted my code to show you how I tried:
private SqlConnection sqlConnGetAccountsOnAnotherThread = null;
// delegate to fill grid/combobox from a thread other than the UI thread
private delegate void DatabindTheControlHandler(SqlDataReader reader);
// delegate to update status
private delegate void DisplayStatusHandler(string Text);
// boolean to indicate whether the asynchronous command is active or closed
private bool boolIsGetAccountsOnAnotherThreadExecuting = false;
private void cbxAccountID_GetAccountsOnAnotherThread()
{
if (boolIsGetAccountsOnAnotherThreadExecuting)
{
// do nothing, the button should be disabled as the process is
already running.
// MessageBox.Show("List is already refreshing.\r\n\r\nPlease wait
until the current refresh has completed.", "Information",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else
{
// disable the refresh button until the process is complete
btnAccountRefresh.Enabled = false;
SqlCommand sqlCommand = null;
string strSQL = "";
try
{
DisplayStatus("Connecting...");
sqlConnGetAccountsOnAnotherThread = new SqlConnection(@"Data
Source=MyDataSource;Server=MyServer;DataBase=MyDatabase;Asynchronous
Processing=true");
sqlCommand = new SqlCommand();
strSQL = "MySQLCode";
sqlCommand.CommandText = strSQL;
sqlCommand.Connection = sqlConnGetAccountsOnAnotherThread;
sqlConnGetAccountsOnAnotherThread.Open();
DisplayStatus("Executing...");
boolIsGetAccountsOnAnotherThreadExecuting = true;
// passing SqlCommand as a parameter makes it easier to call
EndExecuteReader();
AsyncCallback acGetAccountsOnAnotherThread = new
AsyncCallback(callbackGetAccountsOnAnotherThread);
sqlCommand.BeginExecuteReader(acGetAccountsOnAnotherThread,
sqlCommand);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
if (sqlConnGetAccountsOnAnotherThread != null)
sqlConnGetAccountsOnAnotherThread.Close();
}
}
}
private void callbackGetAccountsOnAnotherThread(IAsyncResult iarResult)
{
try
{
// retrieve the original command object
SqlCommand sqlCommand = (SqlCommand)iarResult.AsyncState;
SqlDataReader sqlDataReader = sqlCommand.EndExecuteReader(iarResult);
// execute the code from a different thread instead of main thread.
DatabindTheControlHandler dchGetAccountsOnAnotherThread = new
DatabindTheControlHandler(DatabindTheControl);
// call the UI delegate.
Invoke(dchGetAccountsOnAnotherThread, sqlDataReader);
}
catch (Exception ex)
{
// we are running the code in a seperate thread so we need to catch
the exception.
// Else we are unable to catch the exception anywhere.
Invoke(new DisplayStatusHandler(DisplayStatus), ex.ToString());
}
finally
{
boolIsGetAccountsOnAnotherThreadExecuting = false;
}
}
private void DisplayStatus(string Text)
{
this.Text = Text;
}
private void DatabindTheControl(SqlDataReader sqlDataReader)
{
try
{
DataTable dt = new DataTable();
dt.Load(sqlDataReader);
cbxAccountID.DataSource = dt;
// dgvAccountID.DataSource = dt; // DataGridView instead of ComboBox
DisplayStatus("Ready");
}
catch (Exception ex)
{
DisplayStatus(string.Format("Ready: Last attempt failed.",
ex.Message));
}
finally
{
// Don't forget to close the connection, as well.
if (sqlDataReader != null)
{
sqlDataReader.Close();
}
if (sqlConnGetAccountsOnAnotherThread != null)
{
sqlConnGetAccountsOnAnotherThread.Close();
}
// Renable our button
btnAccountRefresh.Enabled = true;
}
}
"Nicholas Paldino [.NET/C# MVP]" wrote:
> RR,
>
> Yes, you are still going to have the issue of the binding being slow.
> As I mentioned, no matter what, you MUST bind on the UI thread, and like
> Marc said, on other controls, virtualizing the list might be an option, but
> not in this case (the ComboBox does not support virtualization).
>
> This is simply the price you pay for binding so many items to the list
> and it must be done in the UI thread. I mentioned loading the data in a
> background thread only to help minimize some of the time. This is the best
> you can do in this situation, since you MUST use the list with 115K items.
>
> That's why I mentioned what I did in my last paragraph in my previous
> post about filtering, how it would be a better user experience. The benefit
> though of doing it that way is that you could probably get a much faster
> "list" as well.
>
>
> --
> - Nicholas Paldino [.NET/C# MVP]
> - (E-Mail Removed)
>
> "R Reyes" <(E-Mail Removed)> wrote in message
> news:CFFA851A-994F-458A-8B51-(E-Mail Removed)...
> > Hi Nicholas and thanks for your input - I will give this a shot tomorrow
> > morning first thing and let you know what happens.
> >
> > My thoughts:
> > Currently, loading 115,000 items on the UI thread (which is really just a
> > simple key/value pair converted into a DataSet/DataTable returned from a
> > SqlServer query) loads in a few seconds or so, I believe. This is NOT
> > where
> > things slow down. I will confirm this tomorrow.
> >
> > I THINK the problem is that the "slow/choke" occurs DURING the actual BIND
> > CODE and NOT at the loading of items:
> >
> > // some code that gets our data (My code: UI thread; Your code: Another
> > thread)
> > [...]
> >
> > // this BIND CODE is WHEN the slow down really occurs not during the load
> > above
> > cbxAccounts = dt; // or ds.Tables[0];
> > // bs.datasource = dt; // ...wondering if bindingsource works in this case
> > also?
> >
> > I will test and confirm the exact line of code WHEN the time lapse occurs.
> >
> > What I'm questioning here is that even if the data loads well on another
> > thread (or the UI thread as I've been doing it so far) and a call to the
> > main
> > thread is marshaled, would the problem still "slow/choke" for the same
> > minute
> > or two during the - as you put - "bind to newly loaded data" (BIND CODE)?
> > Or, is that how the marshaling/threading resolves this problem?
> >
> > My logic is only how I see things because I'm not the best at
> > understanding
> > threading/marshaling yet, but maybe will understand more with your input
> > after I dive into the code you suggested a bit further =)
> >
> > Will work on this in the AM and keep you posted. Thanks again, Nicholas
> >
> > RR
> >
> > "Nicholas Paldino [.NET/C# MVP]" wrote:
> >
> >> RR,
> >>
> >> You aren't going to be able to bind to the ComboBox asynchronously.
> >> It's just not possible, since binding the data to the ComboBox MUST be on
> >> the UI thread.
> >>
> >> However, that doesn't mean you can't load the data on another thread.
> >> This is really your best bet. Load the data on another thread, then
> >> marshal
> >> a call to the main thread when done loading the data, and bind to the
> >> newly
> >> loaded data.
> >>
> >> Of course, I am sure you know that from a UI design perspective,
> >> having
> >> a list of 100K items just doesn't work that well, and filtering is really
> >> a
> >> better user experience (and probably more productive, since you won't
> >> have
> >> to scroll down 100K items if you have to get to something relatively
> >> close
> >> to the end of the list). You might want to try and impress that on your
> >> superiors.
> >>
> >> --
> >> - Nicholas Paldino [.NET/C# MVP]
> >> - (E-Mail Removed)
> >>
> >> "R Reyes" <(E-Mail Removed)> wrote in message
> >> news:2D302E7A-A596-49E5-8B7E-(E-Mail Removed)...
> >> > Hi,
> >> >
> >> > Problem: How can I databind (or put) a SqlServer query's row return of
> >> > 115,000 items into a ComboBox quickly? Not much longer than a matter
> >> > of
> >> > seconds, that is...
> >> >
> >> > Scenario: I am rebuilding my company's Access 97 VBA database app. It
> >> > pulls
> >> > 115,000 items (of account names) from SqlServer and the data is bound
> >> > to a
> >> > single Access 97 ComboBox control. My C# version needs to work exactly
> >> > like
> >> > this one. Our executive employees want to be able to select from the
> >> > entire
> >> > list of 115,000 items in the Combo as it is in Access 97 app. They
> >> > prefer
> >> > not to use filters to populate the ComboBox with less items as it
> >> > "slows"
> >> > down their production by having to take extra steps.
> >> >
> >> > My attempt using C#.NET: Currently, I can databind the 115,000 objects
> >> > to
> >> > the ComboBox and as you can already guess it takes at least a minute or
> >> > two
> >> > to finish the process.
> >> >
> >> > What to do now?: Is "asynchronous databinding" possible? I see tons of
> >> > articles on asynchronous programming including progressbar reporting
> >> > however
> >> > none of these articles explain how to use databinding into the picture
> >> > to
> >> > speed up load times. Or is that not even how it works...
> >> >
> >> > What are my alternatives to getting these 115,000 items to populate the
> >> > ComboBox in a matter of seconds as it does in the Access 97 program?
> >> > Does
> >> > anyone even know why it's so quick to databind in Access 97 but so slow
> >> > in
> >> > .NET?
> >> >
> >> > Any help is appreciated. Thanks in advance.
> >> >
> >> > RR
> >> >
> >>
>