views:

398

answers:

5

Hi guys.

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.

Thanks in advance for your time.

A: 

For your second question the two ways I normally do it is either handle the Validating event for the combo box before I leave the form (and disable the continue button or throw up a error message) or set ComboBox.DropDownStyle to ComboBoxStyle.DropDownList

Scott Chamberlain
Thanks Scott. I do not think that neither of those solutions are good for me. As far as I know changing the style for the DropDownList will disable typing and I don't want that. I just want to restrict typing to the things which are on the list. But I think that I can write my own validation in the KeyPress event and it will be fine.
Wodzu
A: 

well, you could change your SQL to get the name and surname as one column, and then bind once to that column also i would put the combo and the label in an Ajax update panel.

regarding the validation: to validate on the client you need to have all possible values at the client in advance, or postback for each char and hit the SQL with a: SELECT code FROM TABLE WHERE code LIKE @x + '%' .(which probably isnt a good idea - kills server).

is an autocomplete-box an acceptable option? have a look at Jquery`s autocompletebox, for an example.

menaheme
A: 

Try the following:

Create a Binding object manually so you have a reference to it. Use the Binding object to bind to your ComboBox's SelectedItem property. I think this should get you the selected DataRow. Add that Binding object to the label's DataBindings collection.

Handle the Binding.Format event (or it may be Parse - can't quite remember which event applies when binding in which direction) and use the DataRow object you receive in the event args object to form an appropriate name string containing both forename and surname. You'll need to set some other property of the EventArgs to that string.

You also may have to play with the FormattingEnabled property of the Binding class - it's been a while since I last did this, I just remember it was a bit tricky.

See the documentation for the Parse and Format events: http://msdn.microsoft.com/en-us/library/system.windows.forms.binding_events.aspx

Alex Humphrey
A: 

usually the event of dropdown selected index change occurs when you change you selection and move your focus outside of the dropdown like clicking outside or pressing tab key...

if you need to trigger it immediately you should seek for some other event or invoke event thru javascript onchange event etc....

yasirmturk
+2  A: 

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.

SPFiredrake
Thanks for your interest, I will look at this issue once again.
Wodzu
You had right. You made me to think about this once again and I've added a bindingSource (which exposes CurrencyManager) events. Through them I've been able to accomplish the task.
Wodzu