I am binding ListCollectionView to BindingSource which in turn is binded to DataGridView (winforms). But Whenever new object is added to ListCollectionView BindingSource is not getting updated automatically. I need to make it NULL and re-bind again.
//Binding to Datagrid
bindingProvider.DataSource = this.GetController.ProvidersView;
this.dgvProviders.DataSource = bindingProvider;
After that in Add Button Click.
//Adds new object in ProvidersView Collection.
this.GetController.AddEditProvider();
this.bindingProvider.DataSource = null;
this.bindingProvider.DataSource = this.GetController.ProvidersView;
Can someone please let me know the easy way of refreshing the Bindingsource.
Below is the sample code
BindingList<DemoCustomer> lstCust = new BindingList<DemoCustomer>();
BindingListCollectionView view;
private void Form1_Load(object sender, EventArgs e)
{
lstCust.Add(DemoCustomer.CreateNewCustomer());
lstCust.Add(DemoCustomer.CreateNewCustomer());
lstCust.Add(DemoCustomer.CreateNewCustomer());
lstCust.Add(DemoCustomer.CreateNewCustomer());
view = new BindingListCollectionView(lstCust);
bindingSource1.DataSource = view;
dataGridView1.DataSource = bindingSource1;
}
private void button1_Click(object sender, EventArgs e)
{
this.lstCust.Add(DemoCustomer.CreateNewCustomer());
bindingSource1.EndEdit();
this.bindingSource1.ResetBindings(false);
//(bindingSource1.DataSource as BindingListCollectionView).NeedsRefresh
dataGridView1.Refresh();
}
public class DemoCustomer : INotifyPropertyChanged
{
// These fields hold the values for the public properties.
private Guid idValue = Guid.NewGuid();
private string customerNameValue = String.Empty;
private string phoneNumberValue = String.Empty;
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged(string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
// The constructor is private to enforce the factory pattern.
private DemoCustomer()
{
customerNameValue = "Customer";
phoneNumberValue = "(312)555-0100";
}
// This is the public factory method.
public static DemoCustomer CreateNewCustomer()
{
return new DemoCustomer();
}
// This property represents an ID, suitable
// for use as a primary key in a database.
public Guid ID
{
get
{
return this.idValue;
}
}
public string CustomerName
{
get
{
return this.customerNameValue;
}
set
{
if (value != this.customerNameValue)
{
this.customerNameValue = value;
NotifyPropertyChanged("CustomerName");
}
}
}
public string PhoneNumber
{
get
{
return this.phoneNumberValue;
}
set
{
if (value != this.phoneNumberValue)
{
this.phoneNumberValue = value;
NotifyPropertyChanged("PhoneNumber");
}
}
}
}
Please let me know whats the issue with my code. Whenever I add any new item its not reflected in BindingSource bcoz of tht its not reflecting in DataGridView
www.techknackblogs.com
Make sure your underlying collection (that you used to create the CollectionView) implements INotifyCollectionChanged.
For example, instead of using a List<T>, use ObservableCollection<T> or BindingList<T>.
This allows changes to the collection (adding an element) to propagate to the CollectionView.
Related
So I'm doing some example with data binding in C# winforms. And the textbox's text does not change when I update values of the object contains that property, but when I used another textbox to directly change the value of the property then it works well. I found out that when I used another textbox to set value, the "PropertyChanged" has value and when I used object, it's just "null".
Here are my class:
public WeatherClient() { }
~WeatherClient() { }
public void Dispose() { }
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
//other properties
private float _generationtime_ms;
public float generationtime_ms
{
get => _generationtime_ms;
set
{
_generationtime_ms = value;
OnPropertyChanged(nameof(this.generationtime_ms));
}
}
public WeatherClient getWeather(string url)
{
using (WebClient web = new WebClient())
{
url = string.Format(url);
var json = web.DownloadString(url);
WeatherClient WeatherInfoRespond = JsonConvert.DeserializeObject<WeatherClient>(json);
return WeatherInfoRespond;
}
}
Form.cs:
WeatherClient WeatherClient = new WeatherClient();
public Form1()
{
InitializeComponent();
WeatherClient = WeatherClient.getWeather(); //this for avoiding null value of class type variable
BindingInit();
}
Binding WeatherClient.generationtime_ms with textbox3
void BindingInit()
{
Binding Binding2 = new Binding("Text", WeatherClient, nameof(WeatherClient.generationtime_ms), true, DataSourceUpdateMode.OnPropertyChanged);
textBox3.DataBindings.Add(Binding2);
}
When I update via instance, it does not binding:
private void button1_Click(object sender, EventArgs e)
{
WeatherClient = WeatherClient.getWeather();
}
And when I used another textbox to update value, it works:
private void button1_Click(object sender, EventArgs e)
{
WeatherClient.generationtime_ms = Convert.ToInt32(textBox1.Text);
}
Any suggestion would help.
Thank you guys.
I am trying to understand the way INotifyPropertyChanges works.
I notice in the docs example that the Property Changed event is declared.
public event PropertyChangedEventHandler PropertyChanged;
I can see where the event gets invoked from within the NotifyPropertyChanged method.
But I can't see where it gets subscribed to.
The example after pasting it into my project is as follows
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.CompilerServices;
using System.Windows.Forms;
// Either change the following namespace to the name of your project,
// or name your project with the following name when you create it.
namespace TestNotifyPropertyChangedCS
{
// This form demonstrates using a BindingSource to bind
// a list to a DataGridView control. The list does not
// raise change notifications. However the DemoCustomer type
// in the list does.
public partial class Form1 : Form
{
// This button causes the value of a list element to be changed.
private Button changeItemBtn = new Button();
// This DataGridView control displays the contents of the list.
private DataGridView customersDataGridView = new DataGridView();
// This BindingSource binds the list to the DataGridView control.
private BindingSource customersBindingSource = new BindingSource();
public Form1()
{
InitializeComponent();
// Set up the "Change Item" button.
this.changeItemBtn.Text = "Change Item";
this.changeItemBtn.Dock = DockStyle.Bottom;
this.changeItemBtn.Click +=
new EventHandler(changeItemBtn_Click);
this.Controls.Add(this.changeItemBtn);
// Set up the DataGridView.
customersDataGridView.Dock = DockStyle.Top;
this.Controls.Add(customersDataGridView);
this.Size = new Size(400, 200);
}
// Change the value of the CompanyName property for the first
// item in the list when the "Change Item" button is clicked.
void changeItemBtn_Click(object sender, EventArgs e)
{
// Get a reference to the list from the BindingSource.
BindingList<DemoCustomer> customerList =
this.customersBindingSource.DataSource as BindingList<DemoCustomer>;
// Change the value of the CompanyName property for the
// first item in the list.
customerList[0].CustomerName = "Tailspin Toys";
customerList[0].PhoneNumber = "(708)555-0150";
}
private void InitializeComponent()
{
this.SuspendLayout();
//
// Form1
//
this.ClientSize = new System.Drawing.Size(284, 261);
this.Name = "Form1";
this.Load += new System.EventHandler(this.Form1_Load_1);
this.ResumeLayout(false);
}
private void Form1_Load_1(object sender, EventArgs e)
{
// Create and populate the list of DemoCustomer objects
// which will supply data to the DataGridView.
BindingList<DemoCustomer> customerList = new BindingList<DemoCustomer>();
customerList.Add(DemoCustomer.CreateNewCustomer());
customerList.Add(DemoCustomer.CreateNewCustomer());
customerList.Add(DemoCustomer.CreateNewCustomer());
// Bind the list to the BindingSource.
this.customersBindingSource.DataSource = customerList;
// Attach the BindingSource to the DataGridView.
this.customersDataGridView.DataSource =
this.customersBindingSource;
}
}
// This is a simple customer class that
// implements the IPropertyChange interface.
public class DemoCustomer : INotifyPropertyChanged
{
// These fields hold the values for the public properties.
private Guid idValue = Guid.NewGuid();
private string customerNameValue = String.Empty;
private string phoneNumberValue = String.Empty;
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
// The constructor is private to enforce the factory pattern.
private DemoCustomer()
{
customerNameValue = "Customer";
phoneNumberValue = "(312)555-0100";
}
// This is the public factory method.
public static DemoCustomer CreateNewCustomer()
{
return new DemoCustomer();
}
// This property represents an ID, suitable
// for use as a primary key in a database.
public Guid ID
{
get
{
return this.idValue;
}
}
public string CustomerName
{
get
{
return this.customerNameValue;
}
set
{
if (value != this.customerNameValue)
{
this.customerNameValue = value;
NotifyPropertyChanged();
}
}
}
public string PhoneNumber
{
get
{
return this.phoneNumberValue;
}
set
{
if (value != this.phoneNumberValue)
{
this.phoneNumberValue = value;
NotifyPropertyChanged();
}
}
}
}
}
I have an application with a front end, and instead of deleting objects right away we have a flag on every object that says whether it is supposed to be deleted, so it can be handled later. So the problem is when I am using the object in front end with a DataGrid in WinForms. When I set the Deleted flag I would like the object to not be displayed in the DataGrid, with the BindingList<> as the DataSource of the DataGrid. Is there a way to force a filter every time the DataGrid is repainted? would this be a function of the DataGrid? Or a function of the BindingList<>? For those who are more visual here is a code example. (WARNING this is a code example for conceptual purposes)
test.cs
public class Person : INotifyProperyChanged
{
public string Name { get; set; }
public int Id { get; set; }
private bool _isForDelete;
public bool IsForDelete
{
get { return _isForDelete; }
set
{
_isForDelete = value;
OnPropertyChanged("IsForDelete")
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
MyForm.cs
private BindingList<Person> _persons;
private void MyForm_Load(object sender, EventArgs e)
{
_persons = GetPersonsFromServer();
//Obviously this doesn't work, but I can dream. This is the basic idea.
_myDataGrid.DataSource = _persons.Where(x=>!x.IsForDelete);
}
private void DeleteBtn_Click(object sender, EventArgs e)
{
foreach(var row in _myDataGrid.SelectedRows)
{
var person = (Person)row.DataBoundItem;
person.IsForDelete = true;
}
}
Any suggestions?
One solution to your problem would be to loop through each row of the datagrid, get the object bound to it, check the property, and then if it is set to true suspend the binding, set the row to invisible and then resume the binding. Something like:
CurrencyManager cur = (CurrencyManager)datagrid.BindingContext[datagrid.Datasource];
cur.SuspendBinding();
datagridviewrow.Visible = false;
cur.ResumeBinding();
I though I would post this as after spending several hours trying to work it out I am getting nowhere. Firstly, I am fully aware that databinding in WinForms is not the best. That said it does work in most scenarios.
In my scenario, I have a binding source which is the master for my form. The object that is used for this binding source has a few simple properties and two binding lists as properties as well. Both this class, and the class type for the binding lists implement INotifyPropertyChanged. On my form, I have two DataGridViews for displaying the contents of the binding list properties.
This is also done through databinding at design time. I have two binding sources for each which use the main binding source as there data source and then the respective bindinglist properties as the data member.
So far, I would consider this to be fairly standard.
To update what is in these lists I have buttons to show a form that creates a new item, which I then add to the lists using BindingList.Add().
Now in code, if you debug, the items are in the lists, however, the grids are not updating.
But if I add a listbox to the form which uses just one of the list binding sources then both of the grids start refreshing as expected.
I apologise if any of this is unclear, I have tried to explain as best as I can with a confusing situation.
Any thoughts would be helpful as I really don't want to have to use a hidden list box.
This code works fine for me
BindingList<Foo> source; // = ...
private void Form1_Load(object sender, EventArgs e)
{
this.dataGridView1.DataSource = new BindingSource { DataSource = source };
this.dataGridView2.DataSource = new BindingSource { DataSource = source, DataMember = "Children" };
}
private void button1_Click(object sender, EventArgs e)
{
source.Add(new Foo { X = Guid.NewGuid().ToString() });
}
private void button2_Click(object sender, EventArgs e)
{
source[0].Children.Add(new FooChild { Y = Guid.NewGuid().ToString() });
}
with the model
public class Foo : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
string x;
public string X
{
get { return x; }
set
{
x = value;
this.NotifyPropertyChanged();
}
}
BindingList<FooChild> children;
public BindingList<FooChild> Children
{
get { return children; }
set
{
children = value;
this.NotifyPropertyChanged();
}
}
}
public class FooChild : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
string y;
public string Y
{
get { return y; }
set
{
y = value;
this.NotifyPropertyChanged();
}
}
}
Both grids get refreshed.
I hope this helps you
Edit
I changed the Form1_Load impl
I'm trying to bind some XAML code to a property in my ViewModel.
<Grid Visibility="{Binding HasMovies, Converter={StaticResources VisibilityConverter}}">
...
</Grid>
My ViewModel is setup like this:
private bool _hasMovies;
public bool HasMovies
{
get { return _hasMovies; }
set { _hasMovies = value; RaisePropertyChanged("HasMovies"); }
}
In the constructor of the ViewModel, I set the HasMovies link:
MovieListViewModel()
{
HasMovies = CP.Connection.HasMovies;
}
in CP:
public bool HasMovies
{
get { return MovieList != null && MovieList.Count > 0; }
}
private ObservableCollection<Movie> _movies;
public ObservableCollection<Movie> MovieList
{
get { return _movies; }
set
{
_movies = value;
RaisePropertyChanged("MovieList");
RaisePropertyChanged("HasMovies");
_movies.CollectionChanged += MovieListChanged;
}
}
private void MovieListChanged(object sender, NotifyCollectionChangedEventArgs e)
{
RaisePropertyChanged("HasMovies");
}
What am I doing wrong? How should I change this binding so that it reflects the current state of CP.Connection.HasMovies?
Either directly expose the object in the ViewModel and bind directly through that (so that the value is not just copied once which is what happens now) or subscribe to the PropertyChanged event and set HasMovies to the new value every time it changes in your source object.
e.g.
CP.Connection.PropertyChanged += (s,e) =>
{
if (e.PropertyName = "HasMovies") this.HasMovies = CP.Connection.HasMovies;
};
First of all, the setter for a collection type, such as your MovieList property, is not called when you change the content of the collection (ie. Add/Remove items).
This means all your setter code for the MovieList property is pointless.
Secondly, it's very silly code. A much better solution, is to use NotifyPropertyWeaver. Then your code would look like this, in the viewmodel:
[DependsOn("MovieList")]
public bool HasMovies
{
get { return MovieList != null && MovieList.Count > 0; }
}
public ObservableCollection<Movie> MovieList
{
get;
private set;
}
Alternatively you would have to add a listener for the CollectionChanged event when you initialize the MovieList property the first time (no reason to have a backing property, really really no reason!), and then call RaisePropertyChanged("HasMovies") in the event handler.
Example:
public class CP : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public CP()
{
MovieList = new ObservableCollection<Movie>();
MovieList.CollectionChanged += MovieListChanged;
}
public bool HasMovies
{
get { return MovieList != null && MovieList.Count > 0; }
}
public ObservableCollection<Movie> MovieList
{
get;
private set;
}
private void MovieListChanged(object sender, NotifyCollectionChangedEventArgs e)
{
RaisePropertyChanged("HasMovies");
}
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}