I thought this was a simple problem, but I can't find any information on the web. I'm binding a ListBox to a List using BindingSource like so:
List<Customer> customers = MyMethodReturningList();
BindingSource customersBindingSource = new BindingSource();
customersBindingSource.DataSource = customers;
customersListBox.DataSource = customersBindingSource;
Now, when I add or delete from customers list, my ListBox gets updated (even without using ResetBindings on BindingSource), but if I change any of the customer objects in the list, it does not. Calling ResetBindings has no effect. I even implemented my own BindingList, but the behaviour hasn't changed.
The Customer class uses properties for accessing and modification of data. Its ToString() content is displayed in the list.
I'm using C# in .Net 2.0.
Any ideas?
Thanks
If you use a BindingList you don't even need the BindingSource:
BindingList<Customer> customers = new BindingList<Customer>(MyMethodReturningList());
customersListBox.DataSource = customers;
OK, here's a dirty fix: wenever you need to refresh the box contents set datasource = null, then rebind it.
the reason it doesn't update is because the objects in the list haven't changed and it only checks the refrences of the object rather than their contents.
There is also a bug in the list box which can cause this problem. If you set the SelectionMode to None this problem appears.
As a work around I set the selection mode to One and then back to None when updating the datasource.
I got around this problem by converting data to array when updating source. Please see UpdateData method. This way you can update your combo box without losing ComboBox Settings.
class Person {
public int Id {get; set; }
public string FirstName{ get; set; }
public string SurName {get; set; }
}
public Form1()
{
InitializeComponent();
comboBox1.DisplayMember = "FirstName";
comboBox1.ValueMember = "Id";
comboBox1.DataSource = m_PersonList;
}
public void UpdateData() {
m_PersonList[0].FirstName = "Firstname1";
comboBox1.DataSource = m_PersonList.ToArray<Person>();
}
I understand that this question was asked almost 6 years ago but other than work-arounds I do not see a correct answer here.
When you change property of an item in a collection the event gets raised for the element (object) but not the collection. So the collection does not see a change and will not refresh bound controls. Elements inside all binding collections and most generic collections like List<> receive 2 events, PropertyChanging and PropertyChanged. When a property of the element inside collection is changed, the event gets triggered. All you need to do is add an event handler that would trigger either re-binding or raise an event on the Collection.
Related
I'm new to WP7 development, so binding is still a little foreign to me. I have a StackPanel that I've set the DataContext with TwoWay binding for editing a given record. But, within the "form" I have a ListPicker that I want to populate with possible values from a lookup table.
Currently I've created a DataTemplate and set the ItemsSource property of the ListPicker. Since the sole TextBlock in the DataTemplate is binding to the ItemsSource data context, how do I then bind the ListPicker's SelectedItem to the StackPanel's data context?
Your object that you assign to StackPanel's DataContext must expose appropriate properties, for example:
class MyData
{
public Data { get; set; }
public Selected { get; set; }
}
Then you need to bind ListPicker's ItemsSource: ItemsSource={Binding Data} and ListPicker's SelectedItem: SelectedItem={Binding Selected}.
If you want ListPicker to react when you change MyData's Selected property you will need MyData class to implement interface IObservable. The same goes for Data property, this collection must inform when it's state changes, so make it of type: ObservableCollection<ElemType>.
Check this: Using the parent's DataContext (WPF - Dynamic Menu Command Binding)
Check all answers and see if one of them helps you.
There are a few posts about this, but after hours of searching I still can't find what I need.
The answer in the following post almost gets me what I want:
Combobox for Foreign Key in DataGridView
Question 1:
Going off that example where a Product has many Licenses, my database mappings are all many-to-one relationships which means my License class holds a reference to the Product class. The License class does not have a property for the ProductId since that can be retrieved via the Product reference. I don't want to muck up the License class with both a reference to Product and a ProductId property just to make binding in the UI easier.
Because of this I can't set the DataPropertyName to an Id field. It needs to be the class reference name like so:
DataGridViewComboBoxColumn dataGridViewComboBoxColumn =
(DataGridViewComboBoxColumn)myDataGridView.Columns("LicenseComboBoxColumn");
dataGridViewComboBoxColumn.DataPropertyName = "License"; // not LicenseID
****Update****
I was able to get this to partially work without creating the ProductId property by specifying the Product.Id as the DataPropertyName like so:
dataGridViewComboBoxColumn.DataPropertyName = "License.Id";
However, when doing so, it broke databinding which caused me to manually get and set the cell value.
I've also seen posts about binding to the DataGridView cell, but databinding breaks when I do that and the datasource itself is never updated:
// populate the combo box with Products for each License
foreach (DataGridViewRow row in myDataGridViewProducts.Rows)
{
IProduct myProduct = row.DataBoundItem as IProduct;
DataGridViewComboBoxCell cell = (DataGridViewComboBoxCell)row.Cells("myProductCol");
cell.DataSource = getListOfILicenseObjectsFromDao(myProduct.Id);
cell.Value = myProduct.License.Id;
}
Maybe I'm doing something wrong, or maybe there's a different way. Can anyone help here?
Question 2:
How do I display a different list of Licenses for each Product?
In other words, the combobox list of Licenses will be different for each Product in the grid. I'd like to do this using databinding so I don't have to get and set the values myself.
I found the answer myself. I had this same issue a while ago and found the solution in some old code I dug up. The solution was to add a Self property to the object I wanted to databind to in the combobox (in the example above it would be the License class) and use that property as the ValueMember like so:
foreach (DataGridViewRow row in myDataGridViewProducts.Rows)
{
IProduct myProduct = row.DataBoundItem as IProduct;
DataGridViewComboBoxCell cell = (DataGridViewComboBoxCell)row.Cells("myProductCol");
cell.DataSource = getListOfILicenseObjectsFromDao(myProduct.Id);
cell.DataPropertyName = "License";
cell.DisplayMember = "Name";
cell.ValueMember = "Self"; // key to getting the databinding to work
// no need to set cell.Value anymore!
}
The License class now looks like this:
Public class License
{
public string Name
{
get; set;
}
public ILicense Self
{
get { return this; }
}
// ... more properties
}
Granted I had to "muck" up the Business classes with a property named Self, but that's much better (less confusing to the programmer) than having both a reference to License and a LicenseId property in the Product class IMO. Plus it keeps the UI code much much simpler as there's no need to manually get and set the values - just databind and done.
I'm trying to follow the examples provided in this post, to create a dynamic list constraint in Alfresco 3.3.
So, I've created my own class extending ListOfValuesConstraint:
public class MyConstraint extends ListOfValuesConstraint {
private static ServiceRegistry registry;
#Override
public void initialize() {
loadData();
}
#Override
public List getAllowedValues() {
//loadData();
return super.getAllowedValues();
}
#Override
public void setAllowedValues(List allowedValues) {
}
protected void loadData() {
List<String> values = new LinkedList<String>();
String query = "+TYPE:\"cm:category\" +#cm\\:description:\"" + tipo + "\"";
StoreRef storeRef = new StoreRef("workspace://SpacesStore");
ResultSet resultSet = registry.getSearchService().query(storeRef, SearchService.LANGUAGE_LUCENE, query);
// ... values.add(data obtained using searchService and nodeService) ...
if (values.isEmpty()) {
values.add("-");
}
super.setAllowedValues(values);
}
}
ServiceRegistry reference is injected by Spring, and it's working fine. If I only call loadData() from initialize(), it executes the Lucene query, gets the data, and the dropdown displays it correctly. Only that it's not dynamic: data doesn't get refreshed unless I restart the Alfresco server.
getAllowedValues() is called each time the UI has to display a property having this constraint. The idea on the referred post is to call loadData() from getAllowedValues() too, so the values will be actually dynamic. But when I do this, I don't get any data. The Lucene query is the same, but returns 0 results, so my dropdown only displays -.
BTW, the query I'm doing is: +TYPE:"cm:category" +#cm\:description:"something here", and it's the same on each case. It works from initialize, but doesn't from getAllowedValues.
Any ideas on why is this happening, or how can I solve it?
Thanks
Edit: we upgraded to Alfresco 3.3.0g Community yesterday, but we're still having the same issues.
This dynamic-list-of-values-constraint is a bad idea and I tell you why:
The Alfresco repository should be in a valid state all the time. Your (dynamic) list of constraints will change (that's why you want it to be dynamic). Adding items would not be a problem, but editing and removing items are. If you would remove an item from your option-list, the nodes in the repository with this property value will be invalid.
You will not be able to fix this easily. The standard UI will fail on invalid-state-nodes. Simply editing this value and setting it to something valid will not work. You have been warned.
Because the default UI widget for a ListConstraint is a dropdown, not every dropdown should be a ListConstraint. ListConstraints are designed for something like a Status property: { Draft, Waiting Approval, Approved }. Not for a list of customer-names.
I have seen this same topic come up again and again over the last few years. What you actually want is let the user choose a value from a dynamic list of options (combo box). This is a UI problem, not a dictionary-model-issue. You should setup something like this with the web-config-context.xml (Alfresco web UI) or in Alfresco Share. The last one is more flexible and I would recommend taking that path.
I am using a DataRepeater to show data from a business objects on the screen. I am using windows forms in C# to accomplish this. The datasource is not available at compile time so I want to bind the datasource at runtime.
Here is the simplified scenario. I'm using this business class:
public class Product
{
private double _price;
public double Price
{
get
{
return _price;
}
set
{
_price = value;
}
}
}
I have created a ProductDataSource with the VisualStudio interface and bound the price to a label. Now I filled the datasource of my repeater in code:
dataRepeater1.DataSource = _productDataAgent.GetProducts();
When I startup my application the prices are correctly filled in the labels. So far so good.
Now I want the price labels to be updated when the product is updated. The Visual Studio interface helps me, and let me choose a 'Data Source Update Mode'. So I choose "OnPropertyChanged".
Here comes the tricky part. How does the .NET runtime know that the price property is updated from the backend. So I modify my business class to implement INotifyPropertyChanged. Like this:
public class Product : INotifyPropertyChanged
{
private double _price;
public double Price
{
get
{
return _price;
}
set
{
_price = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Price"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
The problem is this doesn't work. When I update a product it remeanes un-updated in the interface. When I debug and change the property, I see that the PropertyChanged event is null so no one is listening.
Delving a little deeper in to the problem I found the following on the System.Windows.Forms.Binding Constructor page on MSDN:
An event named PropertyNameChanged.
So I tried using a (custom) PriceChanged event, but that did not work.
Am I doing something wrong here? I am comming from using WPF, so maybe this works a little different in Windows Forms? Is this because I am binding at runtime?
Jep found the sollution. Apparently you cannot simply bind to a list of products. You will see the products initially, but they will not be updated when a property is changed. Instead you need to statically bind to a BindingSource. Just create an object datasource using the Visual Studio (in the data menu). Code like this is generated:
private System.Windows.Forms.BindingSource beursProductDisplayBindingSource;
this.beursProductDisplayBindingSource = new System.Windows.Forms.BindingSource(this.components);
this.dataRepeater1.DataSource = this.beursProductDisplayBindingSource;
Now you can dynamically bind like this:
BindingSource productBinding = ((BindingSource)dataRepeater1.DataSource);
_productDataAgent.BeursProducts.ForEach(product => productBinding.Add(product));
Now when implementing INotifyPropertyChanged in your data object like I did is works like expected. Just forgot one step which is not needed when using WPF.
I have a question regarding a data binding(of multiple properties) for custom DataGridViewColumn.
Here is a schema of what controls that I have, and I need to make it bindable with DataGridView datasource. Any ideas or a link to an article discussing the matter?
Controls
Graph Control(custom): Displayed in
the custrom DataGridView column. Has
properties like "Start Date",
"EndDate", Windows Chart control,
which is itself, bindable, etc.
Custom cell(DataGridViewCustomCell inherits
from DataGridViewCell) that holds
the Graph control and processes some
events(OnEnter event, for example,
passes the focus to the custom Graph
column for drag-n-drop type of
events, etc.)
Custom column(DataGridViewCustomColumn
inherits from DataGridViewColumn)
that defined the cell template type:
CellTemplate = new
DataGridViewCustomCell(); and also a
primary choice for data binding
Data Structure:
Main table to be displayed in other DataGridView Columns
Graph table - related to the Main table via parent-child relationship. Holds graph data
Chart table related to the graph table via parent-child relationship. Holds data for the win-form chart, which is a part of my Graph control.
So far I cannot even bind data from the Graph table to by Graph control or Graph-holding Column/Cell.
Thank you for your answer. My data sources is not a SQL data source, and as a matter of fact I was talking about datagridview for win-forms(I'm not sure that was clear).
As I did not get the reply on any of the forums I was asking the question, I figured, I would outline a solution I came up with, for those who may have a similar problem and for possible critique. :-)
(steps 1-2 are also explained in the famous MS example)
1. Create your own classes that inherit from DataGridViewColumn and DataGridViewCell, setup the column template;
2. Create your "CustomEdit" control
In the data item, whatever that is, a DataRow, or a List item, add a read-only property, that return the object itself. This property is bound to the custom column.
Custom Cell:
public partial class MyCell : DataGridViewCell
{
protected override void Paint(...)
{...} // draws control
// receives data item as a value
// in my case I have to custom-draw entire control in this fnc.
public override void InitializeEditingControl(...)
{...} // initialize control editing
// override some other properties
public override Type EditType {
get{
return typeof(MyEditControl);
}
}
public override Type ValueType{
get{
return typeof(MyItem);
}
}
}
Custom Column:
public partial class MyColumn : DataGridViewColumn
{
public MyColumn(){ ...
CellTemplate = new MyCell();
}
}
Edit Control:
public partial class MyEditControl : UserControl, IDataGridViewEditingControl
{... // implements IDataGridViewEditingControl
// value is our data item
}
Data Item, the data sources becomes List<MyItem>
public class MyItem:Object{
...
[XmlIgnore] // I need it because I do serialization
public MyItem Self {
get {
return this;
}
}
}
See my question Here
It's easy to do, you just don't use the IDE to do it, you do it all in code. It's a lot of work, but it's not that difficult if you know what your doing. I went from knowing nothing to being able to do it in less than a day so I'm sure you'll be able to do it.
Edit: you can also use a Join in the sql that populates the datagridview