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();
Related
Is there any way to get when a variable has been changed? And if so, how can I achieve this?
The "official" way to do it, is INotifyPropertyChanged. E.g. it is used by UI's (Windows Forms, WPF) to automatically refresh controls, when the data object they are bound to updates.
public class MyClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
...
}
Then you can implement properties like this
private string _name;
public string Name
{
get { return _name; }
set {
if (value != _name) {
_name = value;
OnPropertyChanged(nameof(Name));
}
}
}
You can use it like this:
var myObj = new MyClass();
myObj.PropertyChanged += MyObj_PropertyChanged;
myObj.Name = "new name";
// Clean up (e.g. in a `Dispose()` method)
myObj.PropertyChanged -= MyObj_PropertyChanged;
Assuming this event handler:
// Will be called whenever a property of `MyClass` is updated.
private void MyObj_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
// Example
if (e.PropertyName == nameof(MyClass.Name)) {
var myObj = (MyClass)sender;
//TODO: do something.
}
}
Just check in the Update void. I would argue that this is the easiest way to achieve what you want in Unity.
var varToCheckIfChanged; //This is the variable you want to know if it is still the same
var tempVar; //This variable stores the original value, and every time the varToCheckIfChanged changed, you update it.
//Gets called on Start of the scene
void Start
{
tempVar = varToCheckIfChanged;
}
//Gets called every frame
void Update
{
if(varToCheckIfChanged != tempVar)
{
Debug.Log("Variable changed!"); //Debug when the variable is updated
var tempVar = varToCheckIfChanged;
}
}
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.
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 have a converter that accepts an ObservableCollection as a parameter, and I'd like to re-evaluate it whenever a specific property on any item in the collection changes
For example: lets say I have bound a label to a collection of Person objects with a converter. The job of the converter is to count the number of Persons in the list that are female, and return "valid" for 1 female or "accepted" for 2. I'd like the converter to get called again anytime the Gender property on any Person object gets changed.
How can I accomplish this?
That's a classic problem you end up having if you play around WPF long enough.
I've tried various solutions, but the one that works best is to use a BindingList like so:
public class WorldViewModel : INotifyPropertyChanged
{
private BindingList<Person> m_People;
public BindingList<Person> People
{
get { return m_People; }
set
{
if(value != m_People)
{
m_People = value;
if(m_People != null)
{
m_People.ListChanged += delegate(object sender, ListChangedEventArgs args)
{
OnPeopleListChanged(this);
};
}
RaisePropertyChanged("People");
}
}
}
private static void OnPeopleListChanged(WorldViewModel vm)
{
vm.RaisePropertyChanged("People");
}
public event PropertyChangedEventHandler PropertyChanged;
void RaisePropertyChanged(String prop)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(prop));
}
}
}
Then just bind to the People collection like you would do with an ObservableCollection, except bindings will be re-evaluated when any property in its items change.
Also, please note that OnPeopleListChanged is static, so no memory leaks.
And Person should implement INotifyPropertyChanged.
A CollectionChanged event is only thrown when an item is added or removed from the collection (not when an item in the collection is changed). So the converter is not called when an item is changed.
One option:
In the Gender Property Set include logic to evaluate the collection and set a string Property that you bind the label to.
Wrote generic version of the answer from Baboon
public class ObservalbeList<T>: INotifyPropertyChanged
{
private BindingList<T> ts = new BindingList<T>();
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));
}
}
public BindingList<T> Ts
{
get { return ts; }
set
{
if (value != ts)
{
Ts = value;
if (Ts != null)
{
ts.ListChanged += delegate(object sender, ListChangedEventArgs args)
{
OnListChanged(this);
};
}
NotifyPropertyChanged("Ts");
}
}
}
private static void OnListChanged(ObservalbeList<T> vm)
{
vm.NotifyPropertyChanged("Ts");
}
public ObservalbeList()
{
ts.ListChanged += delegate(object sender, ListChangedEventArgs args)
{
OnListChanged(this);
};
}
}
I want to bind a custom property of a windows form to a second property, so when I update the former the latter gets the same value.
This is the simplest example of what I'm trying to do:
public partial class Form2 : Form
{
public string MyTargetProperty { get; set; }
public string OtherProperty { get; set; }
public Form2()
{
InitializeComponent();
this.DataBindings.Add("MyTargetProperty", this, "OtherProperty");
}
private void button1_Click(object sender, EventArgs e)
{
MyTargetProperty = "test";
Console.WriteLine("OtherProperty " + OtherProperty);
}
}
When I click button1 I should be able to see that 'OtherProperty' has the same value as 'MyTargetProperty'. Am I doing something wrong? Do I miss something?
Your form needs to implement INotifyPropertyChanged for the MyTargetProperty.
Example:
class FooForm : Form, INotifyPropertyChanged
{
private int myTargetProperty;
public int MyTargetProperty
{
get { return this.myTargetProperty; }
set
{
this.myTargetProperty = value;
this.OnPropertyChanged(
new PropertyChangedEventArgs("MyTargetProperty"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
var evt = this.PropertyChanged;
if (evt != null)
evt(this, e);
}
}
Then you need to add the binding like this:
this.DataBindings.Add(
"OtherProperty",
this,
"MyTargetProperty",
false,
DataSourceUpdateMode.Never);
This will perform a one way binding. The MyTargetProperty will never be updated when the OtherProperty changes. If you need a two way binding you change the DataSourceUpdateMode and also implement a PropertyChanged for OtherProperty.