Modifying Databound ComboBox at run time - c#

High level view of the application is:
Form1 displays client information in a DataGridView (pulled from a DB).
I save the client's information from the DataGridView in properties located in a Client class.
From Form1, the user can click a button that instantiates Form2 and allows the client's information to be modified.
Form2's constructor has a Client object as a parameter. This object is what holds all the client's information from Form1. Using this object i can re-populate the fields that i want the user to be able to edit on Form2.
There is a table in the DataSource that holds all the case types (ie. CaseType1, CaseType2, CaseType3).
I then use a ComboBox and populate it with all the case types from the DataSource when the form is instantiated. My ComboBox settings are as follows:
DataSource: set to my CaseTypeBindingSource which has the columns and data i need.
DisplayMember: The result of the query being used.
ValueMember: The result of the query being used. (same as DisplayMember)
SelectedValue: I have tried it with "none" and with the same value as DisplayMember and ValueMember.
Here's the problem:
At run-time, i want to be able to assign the client's case type that was brought over from Form1 as the item that is currently selected in the ComboBox (SelectedItem).
I could just assign the case type to a textbox and be done with it. But the idea is that i would like the form to show the user what the client's case type is, and allow him/her to change it by using the ComboBox.
The ComboBox name is CaseTypeComboBox. The object holding the case type information and its property is client.CaseType.
I've tried the following inside the constructor:
CaseTypeComboBox.SelectedItem = client.CaseType;
I've also tried creating a class variable in Form2 called origCaseType, assigning the client.CaseType value to the origCaseType class variable in the constructor. Then doing the following when the Form2_Load(...) event fires:
CaseTypeComboBox.SelectedValue = origCaseType;
Does anyone have any thoughts on this? Any similar experience?
Please let me know if anything needs clarification, any help is appreciated.
Thanks in advance!
TS
"Form2" Constructor:
public ModifyCase(Client client)
{
InitializeComponent();
CaseNumberTextBox.Text = client.CaseNumber;
LoadStatusComboBox(client.Status);
LoadIsClosedRadioButton(client.IsClosed);
LoadIsInStorageRadioButton(client.IsInStorage);
LastModifiedTextBox.Text = client.LastModified.ToString();
NotesTextBox.Text = client.Notes;
origCaseType = client.CaseType;
}
"Form2" Load event:
private void ModifyCase_Load(object sender, EventArgs e)
{
//Fills the ComboBox box with case types
this.case_typesTableAdapter1.Fill(this.testDataSet1.case_types);
//Attempts to set value for client's case type from Form1
CaseTypeComboBox.SelectedValue = origCaseType;
//Just to help me see what these variables are holding to figure
//out the problem...
MessageBox.Show(origCaseType);
MessageBox.Show(CaseTypeComboBox.SelectedIndex.ToString());
}

I made two small adjustments that did the trick! The problem was i had set SelectedItem and SelectedValue to the DataSource column i'm using. Except, all the was needed was to set those two values to "none", and set the ValueMember and DisplayMember to the DataSource column. Thanks for your time wdavo!

Related

DatagridView Highlight Event - WINFORM C#

I have a combobox which is connected to the database so I populate the value of my combobox based on what's in my database. my combobox is another FORM from the datagrid. So here's I want to achieve.
form1 = datagrid (based on the database)
form2 = combobox (based on the database)
I want that If I highlight a certain row (My selection mode = fullrowselect) and press a button the comboBox will automatically point to that row.
for ex.
datagrid
name: Joe (highlighted)
*user clicks the button whch in my case is edit
*load edit form
comboBox.SelectedIndex is = highlighted row (which the user clicks)
I can show you my code if it helps. thanks :))
THANKS! :))
You can try to set in the following ways, you can pass the value Joe to the other form via a parameter in the constructor. This could be then used to select you required value in the ComboBox
comboBox2.SelectedIndex = comboBox2.Items.IndexOf("Joe");
comboBox2.SelectedText = "Three"; // or SelectedValue depending on how you are binding
EDIT
Avoid accessing the grid directly from the other form, expose the value required as a property or better pass it to the new form as parameter.
Joe could be the value of the cell like dataGridView2.CurrentRow[0].FormattedValue and pass this to the new form constructor like new Form2(object datagridvalue). Then use the value in the form later on.

How to bind a List of a DataObject to a Grid with BindingSources?

In an assembly I created a class like the following:
[DataObject(true)]
public class A
{
public int Foo{get;set;}
[DataObjectMethod[DataObjectMethodType.Select)]
public static List<A> GetAllA(string ConnectionString)
{
// return filled List<A>
}
}
Now I want to display this List with a Gridcontrol under Winforms. I though of a DataGrid.
Though I'm coming from ASP.net I'd first think of
this.dataGridView1.DataSource = A.GetAllA(ConnectionString)
Works, but I'd prefer a better databinding with BindingSources. (Because I've always heard that thats the way to go)
I managed to drop a BindingSource onto the form and set the DataSource property to class A.
But where can I set the SelectMethod and its parameters? If I set DataSource property of the dataGridView to the BindingSource, it will only display an empty line.
Is this the right way to go? Will it only require some additional clicks in the wizard, or do I need to read tons of documentation to get this working?
Edit: Is there even a way to achieve automatically binding to my select method? Or does the BindingSource only supports mapping the columns, but not actually binding the data, meaning I'm required to set the DataSource property nevertheless?
You need to create a DataSource. Click "Data" menu and select "Add New DataSource..."
Connecting to Data in Visual Studio Overview
http://msdn.microsoft.com/en-us/library/wxt2cwcc(VS.80).aspx
To connect your application to data in
a database, Web service, or object,
run the Data Source Configuration
Wizard
by selecting Add New Data Source from
the Data Sources
Window.
Public Class A
Private _field As String
Public Property Field() As String
Get
Return _field
End Get
Set(ByVal value As String)
_field = value
End Set
End Property
End Class
Public Class AListing
Inherits List(Of A)
End Class
Use AListing as the object when adding a data source. Good for grid views or detail forms that provide navigation. It is up to you to populate it.
Use A as the object when adding a data source. Good for a dialog when you only need to bind to one instance. It is up to you to populate it.
A DataSource just helps the designer configure data binding. You still have to fill the objects. If you do not care about designer support, calling as you do is fine. Using a BindingSource just allows you to use an object like a "data table". Using your example, if I use a BindingSource, I could handle the CurrentChanged event for any additional processing.
this.dataGridView1.DataSource = A.GetAllA(ConnectionString);
//-or-
this.bindingSource1.DataSource = A.GetAllA(ConnectionString);
Have class A retrieve the connection string from the configuration file rather than as a parameter on the GetAllA method. Once your method has no parameters it should be possible to select it in the wizard.

How to refresh data driven combo box on a winform

I have a winform with a combo box thats filled from a query in the database. If I add a field to the database, the new field won't show up in the form until I close it and reopen it.
I was able to put in a MessageBox.Show() and once that popped up, I closed it, and saw the new data in the combo box.
EDIT:
Let me clarify a bit. I have a dropdown combo box, and that is populated by a table adapter. I just did the databinding with the GUI so I'm not sure how it works.
What I want is, I want the new data that I enter to be refreshed when I come back to it. I have a seperate window to manage the data, and then I close it I want the combo box to be updated with what I just saved.
Is this possible? I tried to do it on form load but that doesn't work either, I think because the form is already loaded.
The Refresh method is not for that. What you want to achieve is refreshing the data binding. This would be sth like this:
cb.DataBindings[0].ReadValue();
Another way is using a data source that supports change notification. Such a data source fires ListChanged event with appropriate parameters to trigger update of controls that are bound to it.
you have to fill your table adapter from dataset again by
youTableAdapter.Fill(yourdataSet.tablename);
then you have to reassign datasource
this.combobox.DataSource = this.yourBindingSource;
in the end you may refresh your combobox
combobox.Refresh();
It depends on what your datagrid is bound to. Repopulating the datasource (like a DataTable or a customized BindingList) should automatically repopulate the grid, assuming the data source's ListChanged event is properly fired.
If I understand this correctly, one thing you could do is use a ListChanged event like what was mentioned but it seems as if that didn't work.
TableAdapters aren't really a table in the standard sense but a temporary storage area.
Look in your Form1_Load function (or whatever you named your form, just using the default) and look for a tableadapter.fill method. this.comboboxTableAdapter.Fill(yourdataset name). This is what actually fills your dataset.
Create a function that fills those datasets (if you have more than one) and then call that function on a ListChanged event, or even on the Activate event of the form. This way when you go into that child form and change the data, when you come back to main form the data will be there.
I hope this helps, and good luck with your project.
Here is what worked for me. When I refreshed the secondary dataset (first line), the combo box didn't originally pick up the new values, so I reassigned the dataTable as the DataSource (third line).
boundDataSet = proxy.ReadResources();
DataGridViewComboBoxColumn nameColumn = dataGrid.Columns["Name"] as DataGridViewComboBoxColumn;
nameColumn.DataSource = boundDataSet.Table;
cmbguest.DataSource = null;
loadguestDetails();
make the dataset null and rebind the combobox then u can refresh the data driven combobox in winforms
Child Form inner;
private void UpdateAccount_FormClosed(object sender, FormClosedEventArgs e)
{
ParentForm parentForm= (ParentForm )Application.OpenForms["ParentFormName"];
parentForm.GetAccounts();
}
Parent Form inner;
public void GetAccounts()
{
AccountData lastSelectedItem = (AccountData)cbAccounts.SelectedItem;
cbAccounts.Items.Clear();
List<AccountData> accountDatas = AccountXML.ReadAccountXML();
if (accountDatas != null)
foreach (var item in accountDatas)
{
cbAccounts.Items.Add(item);
}
if(lastSelectedItem != null)
{
cbAccounts.SelectedText = lastSelectedItem.AccountName;
}
}

A ComboBox Data Binding Question

I have an interesting data binding question related to combobox. Hope someone has some good suggestion.
I have a simple form, it contains a file picker and a comboxbox. Every time a file is picked, I read a list of strings from the file to a List object and I set comboBox.DataSource = listOfStrings.
In the form load event, I set comboBox.DataBindings.Add("SelectedItem", myObject, "PickedValue");
The purpose is clear: every time a string is selected from the combobox, I want to write the string to myObject.PickedValue.
That is the whole story.
Now I launch the form, rather than go pick a file, I check the combobox first. Of course, at this point, comboBox.DataSource is null, comboBox.SelectedItem is null, too. But the data binding on the comboBox is already setup (since the setting is in form load event). Now my focus cannot be moved from the combobox to anywhere else.
I think the reason is, when I try to check the combobox, it has null as SelectedItem. When I try to move the focus to somewhere else, the data binding of the combobox is triggered. Underlying, it tries to convert the selected item to string and update myObject.PickedValue with that converted string. Since you cannot convert a null to a string, the data binding validation fails, and the validation mechanism doesn't allow my focus to be moved elsewhere and I am sucked at this moment, cannot even move to pick a file.
My question is, what is the normal binding setup work-flow for my application scenario to prevent this trap? What is the correct order of setting up such a data binding so I can check my combobox before its data source is filled by something?
FYI, I tried to bind myObject.PickedValue to SelectedText property of the combobox (I noticed that SelectedText is a string and never be null, even when SelectedItem is null). But interestingly, even if I select something from the combobox, SelectedText is still empty string when data binding is triggered. What's wrong here?
Thanks for any help.
The failure is a little simpler than you describe: Your ComboBox will fail just because there is no selected item, because there's nothing to select from.
I would just disable the ComboBox if there's nothing to select from. It's pretty easy to do. Remember to hook up a PropertyChanged event in your data object; the binding source will find it automatically with reflection.
class MyData
{
public event PropertyChangedEventHandler PropertyChanged;
// ...
public HasListOfStrings { get { return ListOfStrings != null && 0 < ListOfStrings.Count; } }
private void LoadListOfStrings
{
// ... load the list of strings ...
if ( PropertyChanged) {
PropertyChanged(this, "ListOfStrings");
PropertyChanged(this, "HasListOfStrings");
}
}
}
In the designer, bind the 'Enabled' property of the 'ComboBox' to the HasListOfStrings property. You can do it in code with:
listOfStringsComboBox.Bindings.Add ("Enabled", bindingSource, "HasListOfStrings");
I also recommend you change the AutoValidate property of the container (or container's container) to EnableAllowFocusChange.
This doesn't seem right; it should be possible to set a string property to null. Possibly the focus problem lies elsewhere. Have you tried setting a breakpoint on your property setter to confirm your theory?
The SelectedText property of a combo box refers to text that has been selected in the text portion of the combobox. This only works if the dropdown style is set to combo. Basically it's the selected text of the text box portion of the combo control (the reason a combobox is called "combo" is because it is a combination of a textbox and a selection list). You would ordinarily expect this property to be empty unless the user was editing the text portion of the combo.
If you want a workaround for this problem that is consistent with a good user experience, try disabling the combo box on form load, then enabling it when a file is picked.

Sync a WinForm with DatagridView

I have a Form with a DataGridView which DataSource is a BindingSource to a table. This view will have a single row selection and a button to delete, edit the current selected row in a popup Form and a insert button that will use the same Form as well.
My question is how can I sync the pop Form with the current row?
I tryied to use the RowStateChanged event to get and store the current selected Row to be used in the Form but I coudnt. After the event I get the row that was selected before.
Other thing I dont understand yet in C# how to have a single recordSet and know wich is the current record even if its a new being inserted in a way that once in the Form all data being entered will show up at the same time in the DataGridView.
You don't have to sync the form with the current row. That's what a BindingSource is for.
When you do simple binding to a BindingSource, then every time its current item changes, the bound controls get updated, and every time the values in the bound controls change, the underlying values in the bound item get updated. When you do complex binding (i.e. the bound control displays the BindingSource's list, not just the current item), changing Position on the BindingSource will also change the current position in the bound control, and vice versa. So in this case, you want to bind the controls on your second form using simple binding, and the DataGridView on your first using complex binding.
The only unusual thing you need to do is make sure that both forms are using the same BindingSource. When you do that, clicking on a new row in the DataGridView updates the Position on the BindingSource, and the BindingSource pushes the values from the current bound item out to all of the simply-bound controls that are bound to it.
This is easily accomplished. Assuming that Form1 is the form with the DataGridView, and Form2 is the one with the simply-bound controls, do this:
In Form1:
private BindingSource Source = new BindingSource();
Form1_Load(object sender, EventArgs e)
{
// set Source's DataSource to your DataTable here.
mainDataGridView.DataSource = source;
// create DataGridView columns and their bindings here.
Form2 f2 = new Form2();
f2.TopMost = true;
f2.Source = Source;
f2.Show();
}
In Form2:
public BindingSource Source { get; set; }
public void Form2_Load(object sender, EventArgs e)
{
idTextBox.DataBindings.Add("Text", Source, "id");
descriptionTextBox.DataBindings.Add("Text", Source, "description")
}
You can easily keep it in sync, but not using BindingSource.
Windows Forms data binding is built on top of a few interfaces the most important are:
IList and IBindingList. The first one is just responsible for providing access to elements by their index in the list(to make it simple) the second one actually is more complicated.
IBindingList - (which is implemented by BindingSource) has methods to support:
change notification
adding new "empty" element
sorting
searching
The one that is important for you is of course change notification. Unfortunately BindingSource doesn't implement that bit of code. You may do 2 things here - either implement your version of BindingSource with change notification or try to mess with DGV and textboxes/comboboxes events to update the data.
Personally I've done the first one(I can share the code I have).
"Other thing I dont understand yet in C# how to have a single recordSet and know wich is the current record even if its a new being inserted in a way that once in the Form all data being entered will show up at the same time in the DataGridView."
Every form has a BindingContext object that keeps CurrencyManagers - one for each DataSource. That way every control bound to the same data source knows which record is current. Actually what BindingNavigator does is messing with the apropriate CurrencyManager and calling its methods. (I have no idea why it requires BindingSource instead of in the minimum IList or for full functionality IBindingList)

Categories