Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper binding data to combobox and handling its events

I have a table in SQL Server which looks like this:

ID Code Name     Surname
1   MS  Mike     Smith 
2   JD  John     Doe
3   UP  Unknown  Person

and so on...

Now I want to bind the data from this table into the ComboBox in a way that in the ComboBox I have displayed value from the Code column.

I am doing the binding in this way:

SqlDataAdapter sqlAdapter = new SqlDataAdapter("SELECT * FROM dbo.Users ORDER BY Code", MainConnection);
sqlAdapter.Fill(dsUsers, "Users");
cbxUsers.DataSource = dsUsers.Tables["Users"];
cmUsers = (CurrencyManager)cbxUsers.BindingContext[dsUsers.Tables["Users"]];
cbxUsers.DisplayMember = "Code";

And this code seems to work. I can scroll through the list of Codes. Also I can start to write code by hand and ComboBox will autocomplete the code for me.

However, I wanted to put a label at the top of the combobox to display Name and Surname of the currently selected user code.

My line of though was like that: "So, I need to find an event which will fire up after the change of code in combobox and in that event I will get the current DataRow..."

I was browsing through the events of combobox, tried many of them but without a success.

For example:

private void cbxUsers_SelectionChangeCommitted(object sender, EventArgs e)
{
 if (cmUsers != null)
 {
  DataRowView drvCurrentRowView = (DataRowView)cmUsers.Current;
  DataRow drCurrentRow = drvCurrentRowView.Row;
  lblNameSurname.Text = Convert.ToString(drCurrentRow["Name"]) + " " + Convert.ToString(drCurrentRow["Surname"]);
 }
}

This give me a strange results. Firstly when I scroll via mouse scroll it doesn't return me the row wich I am expecting to obtain. For example on JD it shows me "Mike Smith", on MS it shows me "John Doe" and on UP it shows me "Mike Smith" again! The other problem is that when I start to type in ComboBox and press enter it doesn't trigger the event.

However, everything works as expected when I bind data to lblNameSurname.Text in this way:

lblNameSurname.DataBindings.Add("Text", dsusers.Tables["Users"], "Name");

The problem here is that I can bind only one column and I want to have two. I don't want to use two labels for it (one to display name and other to display surname).

So, what is the solution to my problem?

Also, I have one question related to the data selection in ComboBox. Now, when I type something in the combobox it allows me to type letters that are not existing in the list. For example, I start to type "J" and instead of finishing with "D" so I would have "JD", I type "Jsomerandomtexthere". Combobox will allow that but such item does not exists on the list. In other words, I want combobox to prevent user from typing code which is not on the list of codes.

like image 491
Wodzu Avatar asked Nov 06 '22 13:11

Wodzu


1 Answers

Just did a test on this myself, and I think what you might be looking for is bound to the wrong event (maybe even the wrong object).

I just tried adding the event to the PositionChanged handler of the CurrencyManager object (cmUsers), and it worked exactly as intended. The only issue I had with this was the first time it's loaded, it doesn't hit the PositionChanged so the first item never gets bound to the label (I'm sure there's an easier fix). http://msdn.microsoft.com/en-us/library/system.windows.forms.bindingmanagerbase.positionchanged.aspx

Ran a little test to see which event got fired first, and it seems the SelectionChangeCommitted event fired before the PositionChanged, meaning the CurrencyManager hasn't updated it's internal position yet, so the line:

DataRowView drvCurrentRowView = (DataRowView)cmUsers.Current;

Pulls the wrong DataRow.

Forgot about your second issue. Most controls do not handle keypresses, so what you should do is bind the OnKeyDown or OnKeyPress events to run whatever function you want (finding the index associated with the typed object [ie. FindString()], and set the ComboBox.SelectedIndex property to the found index, otherwise flash a warning). For example:

if(e.KeyCode == Keys.Enter)
{
    int indx;
    cmbBox.SelectAll();
    if((indx = FindString(cmbBox.SelectedText)) != -1)
        cmbBox.SelectedIndex = indx;
    else
        // Some warning
}

Edit After reading your comments, I figured an easier way to do the validation. In the "KeyPress" event of the ComboBox, use the following code:

if(e.KeyChar != (char)Keys.Enter)
{
    if(cmbBox.FindString(cmbBox.Text + e.KeyChar) == -1)
    {
        e.Handled= true;
    }
}

No fuss, no muss. Since we're limiting to only items in the list here, we don't need to handle when they press Enter as it should automatically change the selected item (however, it wouldn't hurt to run some validation to make sure it's the proper cAsE, or just changed the SelectedIndex property manually on Enter keypress). Still, hope this helped someone out.

like image 97
SPFiredrake Avatar answered Nov 15 '22 12:11

SPFiredrake