C# - Update databound combo box automatically - c#

So I've found a lot of questions similar to this tho nothing really solve my problem..
I have a combobx that is bounded by a datasource
cmbProduct.DataSource = this.masterDataSet.Product.Where(x => x.Location == getLocation).AsDataView();
cmbProduct.DisplayMember = "Product";
cmbProduct.ValueMember = "Product";
But whenever i update the source, the combobox items does not update automatically. I still need to close then reopen the form.
is there a method to refresh/reload/or update the combobox?

Solution 1
You can use an implementation of IBindingList as DataSource to view changes of data source in the bound list control (complex two-way data binding). The most suitable implementation is System.ComponentModel.BindingList<T>.
Then when you add items to the binding list, or remove item from it you will see changes immediately in the control.
Solution 2
But as a more simple solution with less changes for you, you can reset the databinding of your cmbProduct this way when you need; for example after a change, call RefreshBindings();:
public void RefreshBindings()
{
var list = put your updated list here;
this.cmbProduct.DataSource = null;
this.cmbProduct.DataSource = list;
this.cmbProduct.DisplayMember = "set the display member here";
this.cmbProduct.ValueMember = "set the value member here";
}

You could implement an event that fires whenever the DataSet changes. The event could reset the Datasource and rebind it.
Somewhere in your code:
yourDataController.DataChanged += OnDataChanged;
and the implementation
public void OnDataChanged(object sender, EventArgs e)
{
cmbProduct.Items.Clear();
cmbProduct.DataSource = this.masterDataSet.Product.Where(x => x.Location == getLocation).AsDataView();
cmbProduct.DisplayMember = "Product";
cmbProduct.ValueMember = "Product";
}
Edit: Of course you need to manually implement the event and cause it to fire every time your data changes.

Related

C# WinForms ListBox changes selection to first item

I got a problem with a ListBox in a WinForm application. I have two ListBoxes inside of a tab control and depending on the selection in the first one (lb1), the DataSource of the second one (lb2) changes. This is done in the SelectedValueChanged Event.
private void listBox_ControlUnits_SelectedValueChanged(object sender, EventArgs e)
{
ControlUnit unit = (sender as ListBox).SelectedItem as ControlUnit;
textBox_ProjectNameTab.Text = unit.ProjectName;
listBox_ControlCircuits.DataSource = null;
listBox_ControlCircuits.DataSource = unit.ControlCircuits;
}
lb1 is filled with a DataSource, too.
Now if I select a value in lb1 the selection automatically jumps back to the first item and I can not figure out why. is this some kind of UI update problem?
Even without the SelectedValueChanged event and the connection to the second listbox the issue occures.
Short gif of the problem, sorry for the blurriness
If I select one item more than once it works somehow (as seen in the gif).
Edit:
I found the problem but I do not quite understand what happens.
I have another listBox on another tab of my tab control. This listBox has the same DataSource as lb1. This seems to cause this behavior.
I finally found the problem:
I did not know that if I use the same DataSource for two ListBoxes they share the BindingContext per default.
I created a new BindingContext for the second ListBox and now the selection does no longer change.
listBox_allGroups.DataSource = null;
listBox_allGroups.DataSource = x.y;
listBox_allGroups.DisplayMember = "Name";
listBox_ControlUnits.DataSource = null;
listBox_ControlUnits.DataSource = x.y;
listBox_ControlUnits.DisplayMember = "Name";
listBox_ControlUnits.BindingContext = new BindingContext();
You can use a variable to hold the selected item
object _selecteditem=null;
and check it in ListBox click event.
prive void ListBox1_Click(object sender,EventArgs e)
{
if(ListBox1.SelectItem == _selecteditem) return;
// do ...
}

When I delete an item from the database and updatethe datagrid, I still see this object

I have two windows wpf.
On the first window I see data from DataGrid, on the second window I delete object from database.
First window:
private void mnRemoveProduct_Click(object sender, RoutedEventArgs e)
{
RemoveProduct window = new RemoveProduct();
window.ShowDialog();
ShowWatchTable();
} // mnRemoveProduct_Click
Second window:
private void btnDelete_Click(object sender, RoutedEventArgs e)
{
if (cmbNameOfWatch.SelectedIndex > 0)
{
var value =
(from watch in obj.Watch.Where(w => w.mark == cmbNameOfWatch.SelectedValue.ToString())
select new { watch.id }).ToArray().First();
Watch watchObj = obj.Watch.Find(value.id);
obj.Watch.Remove(watchObj);
obj.SaveChanges();
this.Close();
}
} // btnDelete_Click
and function ShowTable
public void ShowWatchTable()
{
obj.Watch.ToList();
dataGrid.ItemsSource =
from watch in obj.Watch.Local
select new
{
ID = watch.id,
Brand = watch.mark,
Typ = watch.TypeWatch.typeName,
Price = watch.price + " $".ToString(),
Quantity = watch.number,
Fabricator = watch.Fabricator.fName,
Country = watch.Fabricator.Country.countryName
};
}
When I delete value from database, I still see this value in DataGrid. Only when I close and open this form then I no longer see this object. When I add an element, the same logic is used. Datagrid is updated with the ShowTable function and I see the updated database. But when deleting it it does not work. Please help
From what I can see, you are deleting from obj.Watch using
obj.Watch.Remove(watchObj);
obj.SaveChanges();
but when refreshing your list, you are binding to obj.Watch.Local which I assume is a local cache of your datasource. I don't see where you are syncing this so you might want to sync that before binding or perform your operations on the local sync and ensure that it sends it's changes to the remote data source.
Well, my guess your data source is a query result, but i'm not pretty sure that it's observable(f.e. when I need to bind to collections, I use IObservableCollection to notify that collection changed).
Probably you can try to call dbGrid.Items.Refresh(); method when your primary views gets focus back
You just need to manually refresh your DataGrid.
Add this after your datasource is updated:
dataGrid.Items.Refresh();
You should make sure the collection backing the source of your data items implements INotifyCollectionChanged.
MSDN link to docs, also copied relevant section here.
Binding to Data
To bind the DataGrid to data, set the ItemsSource property to an IEnumerable implementation. Each row in the data grid is bound to an object in the data source, and each column in the data grid is bound to a property of the data object. In order for the DataGrid user interface to update automatically when items are added to or removed from the source data, the DataGrid must be bound to a collection that implements the INotifyCollectionChanged interface, such as an ObservableCollection. To automatically reflect property changes, the objects in the source collection must implement the INotifyPropertyChanged interface. For more information, see Data Binding (WPF).

Alternative to DataGridView DataBindingComplete Event

As stated here the DataBindingComplete event for a DataGridView is fired whenever the contents of the data source change, or a property such as DataSource changes. This results in the method being called multiple times.
I am currently using the DataBindingComplete event to do some visual formatting to my form. For example, I make the text in the first column (column 0) appear as Row Headers and then hide that column (see code below).
private void grdComponents_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
foreach (DataGridViewRow row in grdComponents.Rows)
{
row.HeaderCell.Value = row.Cells[0].Value.ToString();
}
grdComponents.Columns[0].Visible = false;
// do more stuff...
}
It is unnecessary to execute this code more than once, and I am looking to put it into a place where that can happen. Unfortunately it didn't work when I added the snippet to the end of my form's Load method (after I set the DataSource of my DataGridView), nor did it work in the DataSourceChanged event.
Yes, you can use DataSourceChanged event, but be aware, that it occurs only when data source is changed. Additionally, DataBindingComplete offers you information why it has happend - through e.ListChangedType:
Reset = 0,// Much of the list has changed. Any listening controls should refresh all their data from the list.
ItemAdded = 1,// An item added to the list
ItemDeleted = 2,// An item deleted from the list.
ItemMoved = 3,// An item moved within the list.
ItemChanged = 4,// An item changed in the list.
PropertyDescriptorAdded = 5,// A System.ComponentModel.PropertyDescriptor was added, which changed the schema.
PropertyDescriptorDeleted = 6,// A System.ComponentModel.PropertyDescriptor was deleted, which changed the schema.
PropertyDescriptorChanged = 7// A System.ComponentModel.PropertyDescriptor was changed, which changed the schema.
According to this answer:
https://social.msdn.microsoft.com/forums/windows/en-us/50c4f46d-c3b8-4da7-b08f-a751dca12afd/databindingcomplete-event-is-been-called-twice
the whole thing happens because you don't have DataMember property set in your dataGridView. And you can set it only if you want to set particular table from database which is set as your DataSource of dataGridView. Other way - throws an exception.
The simplest way will be just to execute this code once:
Add a flag like Boolean isDataGridFormatted in your form.
And check it like
private void grdComponents_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
if (this.isDataGridFormatted )
return;
foreach (DataGridViewRow row in grdComponents.Rows)
{
row.HeaderCell.Value = row.Cells[0].Value.ToString();
}
grdComponents.Columns[0].Visible = false;
// do more stuff...
this.isDataGridFormatted = false;
}
A bit better will be to prepare your DataGridView during the form construction. As I understand your columns won't change during the course of your program but you don't want to initialize everything manually. You could load some dummy one-item(one-row) data during the initialization:
private void Initialize_DataGridView()
{
// Add dummy data to generate the columns
this.dataGridView_Items.DataContext = new Item[]{ new Item {Id = 5, Value = 6}};
// Make your formatting
foreach (DataGridViewRow row in grdComponents.Rows)
{
row.HeaderCell.Value = row.Cells[0].Value.ToString();
}
grdComponents.Columns[0].Visible = false;
// Reset the dummy data
this.dataGridView_Items.DataContext = null; // Or new Item[]{};
}
...
public MyForm()
{
Initialize();
this.Initialize_DataGridView();
}
I am not sure that exactly such code will work with dataGridView but it is close enough.
Of course an event would have been a nearly ideal solution but there's hardly any that deals with successful autogeneration of columns http://msdn.microsoft.com/en-us/library/system.windows.forms.datagridview_events(v=vs.110).aspx except the AutoGenerateColumnChanged but that is not what we need.
While it is possible to use the ColumnAdded - it will probably execute only once foreach of the autogenerated column, the actual implementation could become an overkill and will be even less direct than already mentioned approaches.
If you will have some time and desire you could create your own DataGridView derived class, take Boolean isDataGridFormatted from your form and implement all the initialization(or event hooking) inside the custom DataGridView.

edit item' is not allowed for this view within datagrid wpf C#

I am using WPF DataGrid and I'm unable to edit data in it. When I double click a cell I get the error listed in the discussion name:
'Edit item' is not allowed for this view.
How can I overcome this ?
Thanks
Edit:
There are two data in my students' name and what they want to say, both of string. Then, I'm binding with my entityframework.
Below is my selectionchanged function
private void ClassDataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var selectedStudent = (Class)e.AddedItems[0];
var sayingCollection = selectedStudent.;
sayingCollection.AssociationChanged += ColumnsCollection_Changed;
var sayingViewSource = ((CollectionViewSource)(this.FindResource("StudentsSayingViewSource")));
sayingViewSource.Source = sayingCollection.Where(c => c.Saying.Contains(":"));
}
Let You confirm us whether you bound a Collection Item such as either IList , ObservableCollection or IEnumerable in the ItemSource.
Because the IEnumerable object doesn't have an add\remove property. So You couldn't modify the collection.

C# - Can I Data Bind between a value and an expression?

I have a List and a Button. When the Lists Count == 0, I would like the button Visibility to = false.
How do I do this using Data Binding?
Thanks in advance,
Added
I have asked this so that I can try to avoid checking the Count on the list in code every time I add or remove an item to or from the list. But if there is no solution then I will continue to do it that way.
Create a DTO (Data Transfer Object) that exposes all your data that you intend to bind to UI elements. Create a property in the DTO (with an appropriate name):
public bool ButtonVisible
{
get { return myListCount != 0; }
}
Add a BindingSource to your form and set it's DataSource to your DTO type.
Click on the Button, goto Properties. Expand the DataBindings node, and click Advanced.
Scroll down the list in the left hand pane, and select Visible. Set it's binding to your property exposed vis the BindingSource..
The General Answer
Write an event handler and register it with your list-control's bindings object
A Specific Example
class MyForm : Form {
protected Button myButton;
BindingSource myBindingSource;
DataGridView dgv;
public MyForm(List someList) {
myBindingSource = new BindingSource();
dgv = new DataGridView();
this.myButton = new Button();
this.Controls.Add(myButton);
this.Controls.Add(dgv);
myBindingSource.DataSource = someList;
dgv.DataSource = myBindingSource;
dgv.DataSource.ListChanged += new ListChangedEventHandler (ListEmptyDisableButton);
}
protected void ListEmptyDisableButton (object sender, ListChangedEventArgs e) {
this.myButton.Visible = this.dgv.RowCount <= 0 ? false : true;
}
}
PS - I'd vote down the favorite answer. A Data Transfer Object (DTO) misses the whole point and functionality of .NET Binding architechture
As the question is currently worded, it doesn't sound like it has anything to do w/ DataBind.
If we have a list -- doesn't matter whether it's populated via code or bound to a data source -- we can set the button's visibility based on the count.
e.g.
List<string> somelist = new List<string>();
somelist.Add("string1");
somelist.Add("string2");
Button1.Visible = somelist.Count > 0 ? true : false;
I think you want to be using the CurrencyManager and the BindingContext of the control.
http://www.akadia.com/services/dotnet_databinding.html#CurrencyManager

Categories