Independent drop down lists with shared data source - c#

I have binding list:
BindingList<string> sampleList = new BindingList<string>();
sampleList.Add("aaa");
sampleList.Add("bbb");
used as data source for two combo boxes:
comboBox1.DataSource = sampleList;
comboBox2.DataSource = sampleList;
When I change selection in one of these combo boxes, second is affected as well. How can I keep them independent?
EDIT:
Due to some 'hot' comments I have to do some clarifications:
it is windows forms code
it is possible
there is no another logic / code behind it
I'm using .NET 2.0
full code source:
public partial class Form1 : Form
{
BindingList<string> sampleList;
public Form1()
{
InitializeComponent();
sampleList = new BindingList<string>();
sampleList.Add("aaa");
sampleList.Add("bbb");
comboBox1.DataSource = sampleList;
comboBox2.DataSource = sampleList;
}
}

try this
comboBox1.DataSource = sampleList.ToList();
comboBox2.DataSource = sampleList.ToList();

Use 2 separate Data sources. In this particular case 2 different BindingList instances per combo, may be the best solution, especially if we are talking about reasonably small lists.
If I'm not mistake every Form has it's default associated BindingSource and it, actually, manages the "linking" controls refered to the same collection. But I'm not very sure in this, honestly.
Regards.

Related

How to populate combobox in base form

I have a base form in where I populate a combox with the following code:
private void FillComboBoxItemsFromResources(ComboBox comboBox, bool firstIsEmpty, bool selectFirst)
{
var res = new Resources(System.Threading.Thread.CurrentThread.CurrentCulture);
var cbEntries = res.GetComboBoxItems(comboBox.Name);
if (firstIsEmpty)
{
cbEntries[0] = new KeyValuePair<int, string>(0, string.Empty);
}
comboBox.DataSource = cbEntries.ToArray();
comboBox.DisplayMember = "Value";
comboBox.ValueMember = "Key";
if (selectFirst && cbEntries.Count > 0)
{
comboBox.SelectedIndex = 0;
}
}
When I run the code and show the base form. -> All works.
When I inherit another form from my base form the code compiles but crashes with "Items-Collection cannot be modified when the data source is set" in Windows form designer generated code.
How to work around this ?
Any help is highly aprreciated.
TIA acki
Well after hours of trying around with rebuilding base form, rebuilding inherited form I just deleted the combobox in base form. Readded combobox into base form, uncommented code to fill combobox and now it works all as expected...

Updating data from other form

I have tow form,ListFrom, and DetailForm
In the ListForm I have a devexpress grid and some button(add, delete, edit)
In the DetailForm I have some textboxes and some button(save,delete,next,previous)
well I have to senario
1 - I open the ListForm and I click on a product to modify it a got the DetailForm opened, I make some modification and I save,then i should have my grid in the ListForm refreshed with the new value.for this I have this code
In the ListFrom
FrmProduit frm = new FrmProduit(monProduit.Id) { MdiParent = this.MdiParent};
frm.updateDataInGridView += new System.Action(refereshGridView);
frm.Show();
in the detailform
if (updateDataInGridView != null)
updateDataInGridView();
well in this scenario everything is OK
second scenario
If I open the detailFrom,and after that I open the listForm, I make some change in the detailFrom and I click save updateDataInGridView in this case is null and then the grid is not refreshed
anyone have suggestion?
I would create a shared BindingSource that both forms would use to show data. If any item is changed in BindingSource it takes care to notify all controls bind to it and so it would refresh grid automatically.
Second approach is to make refereshGridView method public and in DetailForm on save click do this:
var lists = Application.OpenForms.OfType<Form>().Where(x => x.GetType() == typeof(ListFrom));
foreach (var listform in lists)
{
listform.refereshGridView();
}
I did not use FirstOrDefault as maybe there is more than one listform opened.
EDIT about Binding Source
Here is quite good tutorial so please take a look.
Below is a fast-written far from best example of stretch I did:
internal static class DataSources
{
private static BindingSource bs;
public static BindingSource CerateDataSource(List<object> yourObjects)
{
bs = new BindingSource();
bs.DataSource = yourObjects;
}
public static BindingSource GetDataSource()
{
return bs;
}
public static void Reset()
{
bs.ResetBindings(false);
}
}
and then in your listview
dataGridView1.DataSource = DataSources.GetData();
and in detailsview where you are editing one of the objects from BindingSource on save you would have to call: DataSources.Reset();. This is just a markup, but hopefully you get the idea :).
You must always be sure you are referring to the current instance of detailform, thus declare on your listForm
detailform obj = (detailform)Application.OpenForms["detailform"];
And every time you call detailform from listForm do it by obj e.g:
obj.Show()

Linking Textboxes to a dataset using a BindingSource

This is the section of the form I am working on:
The following code links the BindingNavigator to the dataset using a bindingSource. Can I use this binding source to hook up the two text boxes to the data?
Do I simply need to use a property of the textboxes or is this more involved?
i.e when the form loads the first record's fields "Work Phrase" and "Description" will be displayed and when I scroll using the navigator the values in these boxes will change accordingly.
public partial class uxRevisionHelperForm : Form
{
public SqlCeConnection conn = new SqlCeConnection(ConfigurationManager.ConnectionStrings["WindFormAppRevisionHelper.Properties.Settings.DefinitionsDBConnectionString"].ConnectionString);
BindingSource definitionsBindingSource = new BindingSource();
public uxRevisionHelperForm()
{
InitializeComponent();
uxDescriptionTextBox.AutoSize = true;
this.hookUpBindingNavigator();
}
public void hookUpBindingNavigator()
{
SqlCeDataAdapter da = new SqlCeDataAdapter(new SqlCeCommand("Select * From tb_Definitions",conn));
DataSet ds = new DataSet("Helper");
ds.Tables.Add("DefinitionsTable");
da.Fill(ds.Tables["DefinitionsTable"]);
// Assign the BindingSource.
this.uxBindingNavigator.BindingSource = this.definitionsBindingSource;
this.definitionsBindingSource.DataSource = ds.Tables["DefinitionsTable"];
}
Try using the DataBinding collection of the textboxes.
Something like this:
uxDescriptionTextBox.DataBindings.Add("Text",
definitionsBindingSource,
fieldInTable);
Have added the full source code (highlighting exactly your requirement) here - http://sdrv.ms/NyXHdu. Download > Open the solution in VS2010 > Hit F5
[Update]
Double click on Form.cs designer and observe the productListBindingSource. It bound to a custom object - The ProductList class
Then see the properties of the TextBoxes & ComboBox and observe the DataBindings > Text property. They are bound to the productListBindingSource's individual item. See Image below.
Courtsey - http://www.apress.com/9781590594391/ [Chapter 8]

Synchronize list with listbox?

I have a listbox on my WinForms where users can move the items up and down and that listbox is as well the same as a list I have and I was wondering what would be the most efficient way to maintain both synchronized.
for example to move an item down I have:
int i = this.recoveryList.SelectedIndex;
object o = this.recoveryList.SelectedItem;
if (i < recoveryList.Items.Count - 1)
{
this.recoveryList.Items.RemoveAt(i);
this.recoveryList.Items.Insert(i + 1, o);
this.recoveryList.SelectedIndex = i + 1;
}
And I have:
public List<RouteList> Recovery = new List<RouteList>();
Which I would like to maintain updated against the listbox.
Should I simple clear Recovery and update with the current listbox data or is there a better way to update both when move up and down ?
I am mainly asking because the types from the listbox to the list are different.
.Net provides built-in support for this type of behavior. In order to use it, you need to change the type of your Recovery list to:
public BindingList<RouteList> Recovery = new BindingList<RouteList>();
And then you use that BindingList as the DataSource in your controls:
listBox1.DataSource = Recovery;
Here's a simple example using a BindingList of String. I have two listBox's on the form, and they both stay in sync as the selected element gets swapped with the first element in the list:
public partial class Form1 : Form
{
private readonly BindingList<string> list = new BindingList<string> { "apple", "pear", "grape", "taco", "screwdriver" };
public Form1()
{
InitializeComponent();
listBox1.DataSource = list;
listBox2.DataSource = list;
}
private void listBox1_KeyUp(object sender, KeyEventArgs e)
{
var tmp = list[0];
list[0] = list[listBox1.SelectedIndex];
list[listBox1.SelectedIndex] = tmp;
}
}
The proper way is to change the underlying object and then have the UI Control react to that change.
For the ListBox to react to changes in your object collection (your List) you'd need to use an ObservableCollection instead. It's like the INotifyPropertyChanged for collections.
Then you make your up/down actions change the collection, NOT the UI.
EDIT
I am not saying to add an observer on TOP of the collection. I'm saying to change the type of your collection. Don't use List, use ObservableCollection. It works (largely) the same way but notifies the bound UI Controls of changes to it's items.
As for an example, please Google for it. That's what i'd have to do to provide one anyway..

listbox Refresh() in c#

int[] arr = int[100];
listBox1.DataSource = arr;
void ComboBox1SelectedIndexChanged(object sender, EventArgs e)
{
.....//some processes
listBox1.DataSource = null;
listBox1.DataSource = arr;
}
is not working,
also,
listBox1.Refresh(); is not working,
also,
listBox1.Update(); is not working,
i know i can use BindingList<T> but i have to work with only array.
can you help me how can i refresh listbox?
my first answer on stack exchange here.
C# .Net 4.0:
listBox1.DataSource = null;
listBox1.DataSource = names;
I noticed that setting the datasource for the first time, it refreshes.
When it's set, and you try set it to the same one again, it doesn't update.
So I made it null, set it to the same one, and it displayed correctly for me with this issue.
ListBox only updates its shown content when the object that is binded on dataSource notifys it own changes. the BindingSource object has an event called DataSourceChanged. when the Source is changed to a different object the Listbox will update itself.
Same thing when you bind a List. Nothing will happen if you change the List, because the List doesn't notify that it has been changed. There is a Simple solution for this Problem: use BindingList
http://msdn.microsoft.com/de-de/library/ms132679%28v=vs.110%29.aspx
the BindingList has the ListChanged Event is called every time when the List is changed (obviously). So the DataBindings of Windows.Form objects use events like ListChanged to update themselves. A simple List doesn't support this event.
SO if you want to work with a lot of Data Bindings you should know about:
http://msdn.microsoft.com/de-de/library/system.componentmodel.inotifypropertychanged%28v=vs.110%29.aspx
Managed to do just with
FirstListBox.DataContext = null;
FirstListBox.DataContext = App.ViewModel;
Simply loses link and get all the data back to it.
I inherited ListBox and added a public method calling RefreshItems() which does what we want. Already implemented and all. I dont know why they didnt put in a public method.
The problem might come from the ListBox SelectionMode.
For a reason that I don't know, the databinding does not work when SelectionMode is SelectionMode.None.
A workaround could be:
listBox.SelectionMode = SelectionMode.MultiExtended;
listBox.DataSource = myDatasource;
listBox.SelectionMode = SelectionMode.None;
Hope it helps.
well, without binding I only managed with:
this.Hide();
this.Show();
it redraws everything...
try the following
listBox1.DataBind()
Use BeginUpdate and EndUpdate, that should solve it.
No need to set the data source twice
listBox1.BeginUpdate();
listBox1.DataSource = myList;
listBox1.EndUpdate();
Windows forms to see changes especially on Listbox and other controls before load is finished is tricky.
To see data as its loaded use invalidate(); then Update();
Quick Answer
BindingSource bs = new BindingSource();
bs.DataSource = arr;
listbox1.DataSource = bs;
arr[0] = 100; //Do some value change
bs.ResetBindings(false); //Refresh the listbox
Explanation
As stated by Microsoft:
The BindingSource component serves two purposes. First, it provides a layer of indirection when binding the controls on a form to data. This is accomplished by binding the BindingSource component to your data source, and then binding the controls on your form to the BindingSource component. All further interaction with the data, including navigating, sorting, filtering, and updating, is accomplished with calls to the BindingSource component.
So, if you have multiple Controls binded with same data which changes frequently, it is suggested to use BindingSource rather than setting DataSource property of the Controls directly to an Array or List etc.
Moreover, with decompiling ListBox class which is inherited from ListControl in .net 4.6.2, the setter of property DataSource is:
if ((value != null) && (!(value is IList) && !(value is IListSource)))
{
throw new ArgumentException(System.Windows.Forms.SR.GetString("BadDataSourceForComplexBinding"));
}
if (this.dataSource != value)
{
try
{
this.SetDataConnection(value, this.displayMember, false);
}
catch
{
this.DisplayMember = "";
}
if (value == null)
{
this.DisplayMember = "";
}
}
We can see if the value is null the DisplayMember property will automaticly be set to empty. And when you reset a value, several initialization will be performed in SetDataConnection method which takes extra cost.
In contrast, calling BingdingSource.ResetBindings only fires an event:
public void ResetBindings(bool metadataChanged)
{
if (metadataChanged)
{
this.OnListChanged(new ListChangedEventArgs(ListChangedType.PropertyDescriptorChanged, null));
}
this.OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
}
Although I didn't find the handler in ListBox but I guess there should be less side effects. So I think the highest voted answer is not the best solution.

Categories

Resources