Populating Combobox with a large amount of items - c#

I'm hopeful that I can get some help/guidance from you fine folks on stackoverflow. I'm in the midst of moving everything from a vba macro project over to a C# word interop add in. For the most part, the conversion has been fine, if a little verbose as I sus out how to achieve in c# what was a 1 or 2 liner in vba. Part of my vba project has some forms, of which some have comboboxes that are loaded with data from a database server (14k+ count at this moment). This data is populated via the following code:
Private Sub IndividualsLoad(Optional vCompany As Variant)
'load the individuals
'if the company is provided, list the individuals in that company
Dim sSql As String
Dim sCompany As String
Dim rsData As Recordset
Dim sOldValue As String
If (IsMissing(vCompany)) Then
sSql = "SELECT DISTINCT LastName,FirstName " & _
"FROM tblIndividuals " & _
"ORDER BY LastName, FirstName;"
Else
sCompany = SQLSanatize(CStr(vCompany))
sSql = "SELECT DISTINCT LastName, FirstName " & _
"FROM tblIndividuals " & _
"WHERE CompanyName = '" + sCompany + "' " & _
"ORDER BY LastName, FirstName"
End If
Set rsData = New ADODB.Recordset
rsData.Open sSql, gdb, adOpenForwardOnly
sOldValue = cboIndividuals
cboIndividuals.Clear
Do Until rsData.EOF
cboIndividuals.AddItem rsData("LastName") & ", " & rsData("FirstName")
rsData.MoveNext
Loop
rsData.Close
Set rsData = Nothing
cboIndividuals = sOldValue
End Sub
that function is called on form load , and the form loads extremely quickly (Less than 2 seconds) when invoked from Word.
Doing that same thing in C# winforms has been much slower (as quick as about 10 seconds [Mississippily], as long as a minute and a half, whether I keep the populate individuals function in Form_Load or Form_Shown events).
I have tried various ways to speed it up combing through answers to other people's stack over flow questions. I've tried filling a data set from a dataadapter directly from the SQL server and making that the data source to the combobox:
public void PopulateIndividualList()
{
/*start test to see if we can speed up form loading a bit*/
DataSet ds = new DataSet();
using (SqlConnection conn = new SqlConnection(Globals.ThisAddIn.GenDBConnectionString()))
{
string qry = GenFileNumbersLoadQry();
SqlDataAdapter da = new SqlDataAdapter(qry, conn);
conn.Open();
da.Fill(ds, "Individuals");
}
cboIndividuals.DataSource = ds;
}
I've tried async populating of the bindinglist:
public static async Task<List<Individual>> IndividualList(string sSQL)
{
var list = new List<Individual>();
using (var cn = new SqlConnection { ConnectionString = Globals.ThisAddIn.GenDBConnectionString() })
{
using (var cmd = new SqlCommand { Connection = cn })
{
cmd.CommandText = sSQL;
await cn.OpenAsync();
var reader = cmd.ExecuteReader();
while (reader.Read())
{
list.Add(new Individual() { FirstName = reader.GetString(1), LastName = reader.GetString(0) });
}
}
}
return list;
}
BindingList<Individual> individuals = new BindingList<Individual>(await ServerOps.IndividualList(qry));
cboIndividuals.DataSource = individuals;
I've tried populating the bindinglist prior to creating the form, and just passing it in the constructor to the form and assigning the list as the data source in either the Form_Load or the Form_Shown event.
I've tried adding the items as the vba code above does in a while reader.Read() loop:
private void IndividualsLoad()
{
string qry = GenIndividualsLoadQry();
string connString = Globals.ThisAddIn.GenDBConnectionString();
using (SqlConnection connection = new SqlConnection(connString))
{
using (SqlCommand command = connection.CreateCommand())
{
command.CommandText = qry;
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
cboIndividuals.Items.Add(reader["FullName"]);
}
}
}
}
}
I've tried adding the items via Combobox.AddRange():
cboIndividuals.Items.AddRange(individuals.ToArray());
I've tried using SendMessage from user32.dll (which acccording to one of the answers on this question would account for a 60% speed up vs. additem():
private const long CB_ERR = -1;
private const uint CB_ADDSTRING = 0x143;
private const uint CB_RESETCONTENT = 0x14B;
private const long CB_SETITEMDATA = 0x151;
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern IntPtr SendMessage(
IntPtr hWnd,
uint Msg,
UIntPtr wParam,
string lParam
);
private void AddItem(ComboBox cmb, string txt)
{
SendMessage(cmb.Handle, CB_ADDSTRING, UIntPtr.Zero, txt);
}
I've long since lost my hair, but I'm just boggled that the vba form is so much quicker for the same thing.
Based on my research, it seems that populating the items into the combobox is definitely the overhead (as opposed to pulling the items down from the SQL server), which makes me assume that the myriad advanced features of the c# combobox vs the vba combobox are the cause for the difference. I toyed with just setting a custom autocomplete source:
cboIndividuals.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
cboIndividuals.AutoCompleteSource = AutoCompleteSource.CustomSource;
AutoCompleteStringCollection combData = new AutoCompleteStringCollection();
getData(combData);
cboIndividuals.AutoCompleteCustomSource = combData;
and if I do that, at least an end user can type out the person they're searching for and the form autosuggests/completes the entry, and most-importantly opens as speedily as the vba version, but I know that change is going to be super hard to swallow for my end users, and if I'm going to keep the dropdown functionality at the expense of loading speed, I'm going to have a tough sale to management of "let's use this slower version".
Does anyone have any ideas out there that I haven't tried to populate a combobox with a large amount of items, or maybe a simple vba-ish combobox without the overhead of the c# one. Or, just another idea completely, as I'm too close to this at this point. Any help/ideas/suggestions here would be greatly appreciated. Thank you in advance for your time.

If you need to let the users browse all the 14k items, then it's not a good solution, but what I ment is something like this. (not yet properly elaborated, but might be a good start)
using System;
using System.Windows.Forms;
namespace ComboBox
{
public partial class Form1 : Form
{
private Timer _timer;
public Form1()
{
InitializeComponent();
InitTimer();
}
private void InitTimer()
{
_timer = new();
_timer.Interval = 700;
_timer.Tick += OnTypeTimerTicked;
}
private void OnTypeTimerTicked(object sender, EventArgs e)
{
_timer.Stop();
//BeginUpdate-EndUpdate helps to gain some speed
comboBox1.BeginUpdate();
//here you can reach out to your repo and probably use AddRange instead of Add
//here you have to filter the results according to your search logic
//and populate the combobox
comboBox1.Items.Add("Item 1");
comboBox1.Items.Add("Item 2");
comboBox1.Items.Add("Item 3");
comboBox1.Items.Add("Item 4");
comboBox1.Items.Add("Item 5");
comboBox1.Items.Add("Item 6");
comboBox1.Items.Add("Item 7");
comboBox1.Items.Add("Item 8");
comboBox1.Items.Add("Item 9");
comboBox1.Items.Add("Item 10");
comboBox1.EndUpdate();
}
private void comboBox1_TextChanged(object sender, EventArgs e)
{
_timer.Start();
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
try
{
//your code here
}
finally
{
//maybe it's not the right place for your needs but it must be done somewhere to prevent memory leaks
_timer.Tick -= OnTypeTimerTicked;
}
}
}
}
one more detail about the combobox: it has an AutocompleteMode property. You have to set it to other than None - either in the Designer or in code.

Related

Viso VSTO - ShapeAdded event not firing (sometimes)

In my Add-in for Visio, I have set a handler for 'ShapeAdded'.
This fired for the first 2 or 3 shapes that are added, but then just stop firing altogether.
Here's the basic outline of how my add-in functions:
User adds shape to page
On ShapeAdded, a form is displayed.
User enters text in form, presses search button
Call to stored Procedure (parameter = user text)
Form's datagridview is populated with results.
User double-clicks result row required.
Form closes, selected value becomes shape text.
If I comment out my code after item (3) - then my event handler continues firing without issue. I can add new shapes all day long.
BUT - once I let code call the stored procedure (step 4), then that is where problems arise.
Very specifically : da.Fill(dt)
I may manage 1 to 6 shape adds, but sooner or later, the event just stops firing.
(*update 8th Jan: Recordset size seems to affect the issue. When 1100 rows are returned each time, I manage to add around 6 shapes to my page. When 3 rows returned each time, I get to add up to 18 shapes before the events stop firing.
That tells me there is something not 'clean' about the way I am handling my data calls - but I cannot see what it is !!)
I am totally baffled as to why calling the stored procedure would interfere with my event handler !?!?
Especially without any error messages.
Has anyone any ideas whatsoever as to what I might be doing wrong ?
Or even, ideas around how to debug this in a better manner ?
public partial class ThisAddIn
{
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
Globals.ThisAddIn.Application.MarkerEvent += new Visio.EApplication_MarkerEventEventHandler(Application_MarkerEvent);
}
private void Application_MarkerEvent(Visio.Application visapp, int SequenceNum, string ContextString)
{
if (ContextString.Contains("soln=myApplication") && ContextString.Contains("/cmd=DocCreated"))
{
SetDocEvents();
}
}
public void SetDocEvents()
{
Microsoft.Office.Interop.Visio.Document doc = Globals.ThisAddIn.Application.ActiveDocument;
// set event handler
try
{
doc.ShapeAdded += new Microsoft.Office.Interop.Visio.EDocument_ShapeAddedEventHandler(onShapeAdded);
}
catch (Exception err)
{
System.Diagnostics.Debug.WriteLine(err.Message);
throw;
}
}
private void onShapeAdded(Visio.Shape Shape)
{
Form_Entity fe = new Form_Entity(Shape.Text);
fe.ShowDialog();
fe.Dispose();
}
// ... other stuff
}
Form Code:
public partial class Form_Entity : Form
{
public Int32 eid { get { return m_id; } }
public string ename { get { return m_name; } }
public string eabbr { get { return m_abbr; } }
private Int32 m_id;
private string m_name;
private string m_abbr;
public Form_Entity()
{
InitializeComponent();
}
public Form_Entity(String search)
{
InitializeComponent();
txt_search.Text = search;
}
private void Cmd_Search_Click(object sender, EventArgs e)
{
String sample = txt_search.Text;
DataTable dt = new DataTable();
SqlConnection myConn = new SqlConnection("Server=xxxxxxx;Database=xxxxxxxx;Trusted_Connection=True;");
myConn.Open();
SqlCommand myCmd = new SqlCommand("[dbo].[mySearch]", myConn);
myCmd.Parameters.Add(new SqlParameter("#searchText", sample));
myCmd.CommandType = CommandType.StoredProcedure;
SqlDataAdapter da = new SqlDataAdapter(myCmd);
da.Fill(dt);
dataGridView1.DataSource = dt;
myCmd.Dispose();
myConn.Close();
}
}
** Files for this project
Visual Studio Solution
T-SQL to create sample table/data.procedure
Viso Template
ReadMe.txt
http://www.netshed.co.uk/temp/Vis_Sample.zip
I think the problem here is that you're not keeping the doc object in scope and Visio will stop reporting events for which there is no reference.
You can add a field (or property) as follows and then the reference and associated events should be maintained:
public partial class ThisAddIn
{
private Visio.Document _targetDoc = null;
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
Application.MarkerEvent += Application_MarkerEvent;
}
public void SetDocEvents()
{
_targetDoc = Globals.ThisAddIn.Application.ActiveDocument;
// set event handler
try
{
_targetDoc.ShapeAdded += onShapeAdded;
}
catch (Exception err)
{
System.Diagnostics.Debug.WriteLine(err.Message);
throw;
}
}

RDLC gathering and presenting data

Recently got back into programming after a few years break, and am in the process of building a POS(Point of Sale) software app for my own consumption. I though the app was pretty near close to Version 1 Beta release ..... until I ran into the Reporting side of things.
Having trolled through numerous Tutorials and Walkthroughs, I am pretty much none the wiser as to how to achieve what I need the report to do.
What I am trying to achieve is an EOD (End of Day) report and the issue that I am running into is how to present the data that comes from 4 different queries.
I have tried the Business object approach and this works for the first level that is set up at design time, but the remainder return nothing.
Is there a better approach (easier to get my head around) to achieve this?
Could someone recommend a tutorial/Walkthrough that models a report using data from different objects or tables?
Extract fronm Code
Reportviewer form with docked reportviewer
public partial class ReportViewer : Form
{
private List<VO.TotSalesByDept> deptSales = new List<VO.TotSalesByDept>();
private VO.TotalSales daySales = new VO.TotalSales();
private List<VO.TotSalesByTender> tendSales = new List<VO.TotSalesByTender>();
private List<VO.TotSalesByGroup> grpSales = new List<VO.TotSalesByGroup>();
private BUS.UserBUS _userBUS = new BUS.UserBUS();
public ReportViewer()
{
InitializeComponent();
//daySales = _userBUS.getSalesTot(DateTime.Now);
deptSales = _userBUS.getDeptSales(DateTime.Now);
//tendSales = _userBUS.getTendSales(DateTime.Now);
//grpSales = _userBUS.getGroupSales(DateTime.Now);
}
private void ReportViewer_Load(object sender, EventArgs e)
{
//reportViewer1.LocalReport.SubreportProcessing += new Microsoft.Reporting.WinForms.SubreportProcessingEventHandler(LocalReport_SubreportProcessing);
//this.TotalSalesBindingSource.DataSource = daySales ;
//this.TotSalesByGrp.DataSource = grpSales;
//this.TotSalesByTender.DataSource = tendSales;
this.TotSalesByDeptBindingSource.DataSource = deptSales;
this.reportViewer1.RefreshReport();
}
//private void LocalReport_SubreportProcessing(object sender, SubreportProcessingEventArgs e)
//{
// e.DataSources.Clear();
// e.DataSources.Add(new ReportDataSource());
//}
}
The business layer that the form calls
public List<VO.TotSalesByDept> getDeptSales(DateTime _now)
{
List<VO.TotSalesByDept> deptSales = new List<VO.TotSalesByDept>();
DataTable dataTable = new DataTable();
dataTable = _userDAO.getTSalesByDept(_now.Date);
foreach (DataRow dr in dataTable.Rows)
{
VO.TotSalesByDept deptSale = new VO.TotSalesByDept();
deptSale.Dept = dr["Department"].ToString();
deptSale.RepQty = Int32.Parse(dr["Total QTY"].ToString());
deptSale.RGSales = decimal.Round(decimal.Parse(dr["TotItemSales"].ToString()),2,MidpointRounding.AwayFromZero);
deptSales.Add(deptSale);
}
return deptSales;
}
not sure what else you would need to see?
Many thanks in advance
Steve
The following link enabled me to begin working on a solution
http://www.c-sharpcorner.com/UploadFile/robo60/StandaloneRDLCReports11142007183516PM/StandaloneRDLCReports.aspx
Thanks
Steve

Returning values from SQL to a WPF form - am I doing it correctly?

I am creating a form with several calculations from the same table (in this case). The code works fine, but I could with with some guidance to make sure I am doing things efficiently:
When the form loads, I simply want two textblocks to be populated with counts. I know that I will need to put some error checking in, but outside of that - is this a good way of doing it?
private void Window_Loaded(object sender, RoutedEventArgs e)
{
int intCount = ReturnNumber("SELECT COUNT(ActivityID) FROM tblActivity WHERE [Activity_Category] = 'Productivity'");
TxtBlockProductivityPerc.Text = intCount.ToString();
intCount = ReturnNumber("SELECT COUNT(ActivityID) FROM tblActivity WHERE [Activity_Category] = 'Revenue'");
TxtBlockRevenuePerc.Text = intCount.ToString();
}
public int ReturnNumber(string StrQuery)
{
string cs = ClsVariables.StrDb;
string cmdText = StrQuery;
using (SQLiteConnection con = new SQLiteConnection(cs))
using (SQLiteCommand cmd = new SQLiteCommand(cmdText, con))
{
con.Open();
int count = Convert.ToInt32(cmd.ExecuteScalar());
con.close();
return count;
}
}
Basically, if you are not developing an application in MVVM style, such an approach is not bad, but it's just my opinion. Here a couple of my comments:
In this situation, I think better to use event Window.ContentRendered, because Loaded event is triggered when loading Window as Control, and the ContentRendered event triggered when rendering the contents of the Window. But the big difference no between them link.
You have to be separately stored query strings, because every time we need to change them, you'll have to climb into your function, which is not convenient.
Add to the functions that work with SQL server prefix FromSQL, in your case will be something like this: ReturnNumberFromSQL().
You do not need a temporary variable, you can call the function and immediately get a result from it.
My pseudo example:
private void Window_ContentRendered(object sender, EventArgs e)
{
string Test1Sql = "Test1 SQL query"; // stored separately
string Test2Sql = "Test2 SQL query"; // stored separately
MyTextBlock1.Text = ReturnNumberFromSQL(Test1Sql).ToString();
MyTextBlock2.Text = ReturnNumberFromSQL(Test2Sql).ToString();
}
public int ReturnNumberFromSQL(string StrQuery)
{
return 777;
}
And think on the expense of having to store procedures that do not work with GUI separately in the appropriate class.

C # memory management and performance

The criteria is simple. I am developing a winform application using C# 4.0 and have a win form having a grid view. On the load event, I retrieve data from database and then assign the datasource to the gridview. I have used BackGroundWorker object for database retrieval. This is nice. My GUI is responsive. The records are more than about 10 lac. So i fill my datatable using background worker and then assign the datatable to gridview. The problem is, my system gets slower. When i minimize mdi form of my application and try to do other things like opening internet browser and stuff, my pc gets slower. I have core i3 2ith 2GB RAM. A grid having 10 lac records makes my system slow. How to manage memory in this case? Here is the code:
BackgroundWorker bWorker;
DataTable dt;
public Form1()
{
InitializeComponent();
bWorker = new BackgroundWorker();
bWorker.DoWork += new DoWorkEventHandler(m_oWorker_DoWork);
bWorker.ProgressChanged += new ProgressChangedEventHandler(m_oWorker_ProgressChanged);
bWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(m_oWorker_RunWorkerCompleted);
bWorker.WorkerReportsProgress = true;
bWorker.WorkerSupportsCancellation = true;
}
void m_oWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
dataGridView1.DataSource = dt;
pictureBox1.Visible = false;
//progressBar1.Style = ProgressBarStyle.Blocks;
//label1.Text = "Data Loaded Successfully!";
this.Hide();
this.Show();
}
void m_oWorker_DoWork(object sender, DoWorkEventArgs e)
{
dt = getDataTable();
//bWorker.ReportProgress(100);
}
void m_oWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//Here you play with the main UI thread
//progressBar1.Value = e.ProgressPercentage;
}
private void Form1_Load(object sender, EventArgs e)
{
bWorker.RunWorkerAsync();
}
private DataTable getDataTable()
{
string conStr = ConString;
SqlConnection con = new SqlConnection(conStr);
SqlCommand cmd = new SqlCommand("Select *from testtable",con);
SqlDataAdapter da = new SqlDataAdapter(cmd);
dt = new DataTable();
try
{
da.Fill(dt);
return dt;
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
return null;
}
}
Please guide me how to manage memory so that system should not get slower. If i dispose datatable after assigning it to grid, would that help?
I am actually a beginner. And want a complete guidance about memory management. Application should be fast. Thanx
Most likely your problem is that your DataGridView creates a row for each row in the result set. You should read about what the virtual display mode is, the VirtualMode property and how to implement it.
You can't expect to load a million rows of data into a GUI control and expect everything to behave well, can you?
The problem is here:
SqlCommand cmd = new SqlCommand("Select *from testtable",con);
There's no way you should be SELECTing the entire table. You'll want to LIMIT your query to only those rows that the GUI can reasonably display. Shooting completely in the dark, I'd say somewhere on the order of a thousand rows at maximum.
My assumation is you try to load all those rocords one time in getDataTable. Why not load by demand? I.e., only load the first level load. If all those are first level, you can separate into multi pages whose size may be a screen width.
You can use the DataGrid control with setting all appropriate select, insert, update, delete statements, add paging rules and this will also to the trick. The grid will not load all the records in once, but instead, it will use the clever internal functionality to select from the db and display only the appropriate data.
Hope this helps also.

Getting increasingly slow to write into textbox using textBox_TextChanged event and if statement together.

I am making a dictionary using C# and Windows forms. In my dictionary I have a textBox where the user can search for a word to get the meaning. I also have some options in a comboBox where the user can choose a language to see the meaning for that language. Because I am making the dictionary for different languages.
My code looks like:
private void textBox1_TextChanged(object sender, EventArgs e)
{
string word = textBox1.Text;
SqlCeConnection con = new SqlCeConnection(#"Data Source=" + Directory.GetCurrentDirectory() + #"\Database\condrokothadb.sdf;Password=000;");
//in combobox there are 2 option(language)
//if select one language(option) from combobox
if(mood=="bangla")
{
SqlCeDataAdapter b = new SqlCeDataAdapter("SELECT english,bangla FROM dic WHERE (bangla like '" + word + "%')", con);
DataTable tt = new DataTable();
b.Fill(tt);
dataGridView1.DataSource = tt;
}
else //by default english language is selected
{
using (con)
{
con.Open();
using (SqlCeDataAdapter b = new SqlCeDataAdapter("SELECT english,bangla FROM dic WHERE (english like '" + word + "%')", con))
{
DataTable tt = new DataTable();
b.Fill(tt);
dataGridView1.DataSource = tt;
}
}
}
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (((ComboBox)sender).SelectedItem.ToString() == "Bangla")
{
mood = "bangla";
}
else if (((ComboBox)sender).SelectedItem.ToString() == "English")
{
mood = "english";
}
}
My problem is that when a user want to write something into the textbox it is getting so much slower to write. How can I overcome that?
This is an interesting question and here is how I would solve it.
I added a timer that starts to count as you type the first character into your textBox, and for every character you add the timer resets. The application wont execute the part where you search through the database untill the timer reaches a set number of ticks.
Make sure you add a timer and a backgroundWorker into the form. Create the events through the properties window and add this code:
int timerTicks;
int waitUntill = 10; //10 = 1 second. Change this to decide how long the application will wait.
string mood;
string word;
string langConnection;
DataTable tt;
SqlCeConnection con;
SqlCeDataAdapter b;
private void textBox1_TextChanged(object sender, EventArgs e)
{
if (!timer1.Enabled)
timer1.Start();
//Reset the timer when a character is entered in textBox1.
timerTicks = 0;
}
private void timer1_Tick(object sender, EventArgs e)
{
timerTicks++;
if (timerTicks > waitUntill && !backgroundWorker1.IsBusy && comboBox1.SelectedItem != null)
{
//Stop the timer and begin the search in a background thread.
timer1.Stop();
word = textBox1.Text;
mood = comboBox1.SelectedItem.ToString();
backgroundWorker1.RunWorkerAsync();
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
tt = new DataTable();
con = new SqlCeConnection(#"Data Source=" + Directory.GetCurrentDirectory() + #"\Database\condrokothadb.sdf;Password=000;");
langConnection = String.Format("SELECT english,bangla FROM dic WHERE ({0} like '{1}%')", mood, word);
using (con)
{
con.Open();
b = new SqlCeDataAdapter(langConnection, con);
b.Fill(tt);
}
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
dataGridView1.DataSource = tt;
}
Note that you don't need the comboBox1_SelectedIndexChanged event for this to work.
EDIT:
To make the actual search run faster you would have to open the connection at startup and keep it open throughout the entirety of the execution, like other answers suggest as well. You should be able to figure that out for yourself though.
Instead of getting data from server in every text changed you can get all data in grid at once and then filter them with dataview.
// to get data in grid
CustomList<wordlistDAO> WordList = null;
WordList = WordListBLL.GetAllWord();
GridWord.DataSource = WordList ;
// create word datatable for filtering
DataTable dtWord = null;
dtWord = new DataTable();
foreach (DataGridViewColumn colu in GridWord.Columns)
dtWord .Columns.Add(new DataColumn(colu.HeaderText));
foreach (DataGridViewRow row in GridWord.Rows)
{
DataRow dr = dtWord.NewRow();
foreach (DataGridViewCell cell in row.Cells)
dr[row.Cells.IndexOf(cell)] = cell.Value;
dtWord .Rows.Add(dr);
}
//create data view
DataView wordlistview = new DataView();
wordlistview = new DataView(dtWord);
// filter dataview and show in grid
if (cboLanguage.Text == "Bangla")
{
wordlistview.RowFilter = "bangla LIKE '" + txtSearchValue.Text.Trim().ToUpper() + "%'";
}
else
{
wordlistview.RowFilter = "english LIKE '" + txtSearchValue.Text.Trim().ToUpper() + "%'";
}
GridWord.DataSource = wordlistview;
The main problem is that, every time you press a key in the textbox, you are creating a database connection and querying the database. This is very inefficient! Also, because your Bangla code doesn't dispose of the connection, you may be keeping a lot of objects referenced that don't need to be, so you may find performance is degrading over time.
One basic suggestion would be to use a single connection instead of opening new connections for each keypress. This will reduce somewhat the time taken for the query. Realistically though, I suspect that you want to load the full content of the data at once, and run your query in-memory. This will give you much better speed.
Running your query on a background thread will help maintain the responsiveness of your UI, but will potentially end up with lots of queries running at once trying to catch up with the user's typing.
A better solution is to consider running an "idle-timer," and only starting the query when the user has stopped typing for a short amount of time. I'd recommend still using a background thread for this. You won't query the database for every keypress, and you won't affect the responsiveness of the UI.
Delay could be due to large amount of data in the database and you're calling database for every text changed event.
What I would suggest is to get all the data into DataView and keep filtering and binding the grid with results from view. That way you can minimise the number of times database is called.
Doing a search in the database on any keystroke is a bad practice. As you already experienced, it makes the UI very slow. A better option would be to do the search in a background thread, and also not for every keystroke. You can wait some time (0.5 seconds for example) before doing the search. If the user pressed another key in the meantime, expand the wait again to another 0.5 seconds.

Categories

Resources