I'm binding a GridView to a collection of objects that look like this:
public class Transaction
{
public string PersonName { get; set; }
public DateTime TransactionDate { get; set; }
public MoneyCollection TransactedMoney { get; set;}
}
MoneyCollection simply inherits from ObservableCollection<T>, and is a collection of MyMoney type object.
In my GridView, I just want to bind a column to the MoneyCollection's ToString() method. However, binding it directly to the TransactedMoney property makes every entry display the text "(Collection)", and the ToString() method is never called.
Note that I do not want to bind to the items in MoneyCollection, I want to bind directly to the property itself and just call ToString() on it.
I understand that it is binding to the collection's default view. So my question is - how can I make it bind to the collection in such a way that it calls the ToString() method on it?
This is my first WPF project, so I know this might be a bit noobish, but pointers would be very welcome.
You can add property StringRepresentation or something like this in MyMoney class. If you do not want to affect this class, you should write a wrapper - MyMoneyViewModel which will have all needed properties. This is a common way. HIH!
Write a IValueConverter implementation that calls ToString() on the bound collection and returns it and use this converter in the XAML binding expression.
Related
Setup:
Class Model with one property which type is an interface.
Create an instance and add it to a winforms bindingsource
Add a textbox to winforms and configure the text value to use the bindingsource with the property as a datamember
Model
public class Model
{
public ICustomer Customer { get; set; }
}
public class Customer : ICustomer
{
public string Name { get; set; }
public override string ToString()
{
return Name;
}
}
public interface ICustomer
{
string Name { get; set; }
}
Binding
this.textBox1.DataBindings.Add(new System.Windows.Forms.Binding("Text", this.bsModel, "Customer", true));
Problem
When I run this code, the textbox remains empty (instead of showing the name of the customer).
BUT it does show it when I change the type of the Customer property in Model to the concrete Customer type.
I can't find any reason on MSDN why this is? Any ideas?
(Preferably no workaround like storing the toString value into another property, there is a framework doing this binding where I prefer not to hack into)
Probably you should specify the property that should be binded to the Text property of the TextBox
this.textBox1.DataBindings.Add(new System.Windows.Forms.Binding
("Text", this.bsModel, "Customer.Name", true));
If you remove the interface ICustomer and use directly the concrete class Customer then the binding code uses the ever present ToString() method that you have overridden in the concrete class and thus you get your textbox set.
For example, try to change the ToString to return a Surname property
Set the formattingEnabled property to false fixes it so it uses the toString() method as in the concrete implementation.
this.textBox1.DataBindings.Add(new System.Windows.Forms.Binding("Text", this.bsModel, "Customer", false));
Problem:
I just browsed through the source at msdn and bumped into the following line:
if (e.Value != null && (e.Value.GetType().IsSubclassOf(type) || e.Value.GetType() == type || e.Value is System.DBNull))
return e.Value;
According to MSDN:
The IsSubclassOf method cannot be used to determine whether an interface derives from another interface, or whether a class implements an interface.
So this will evaluate to false and the further conversion will end up in returning null.
By setting formattingEnabled to false the parse method won't be called and the value will be simply returned instead.
Not sure if it's done on purpose or it's a bug. But I've got the feeling I better set the formattingEnabled to false for concrete types as well.
#Steve solution is working as well! (thanks)
But he's working around the interface type.
I just prefer simply working with toString(), since it can change over time and it's much easier to maintain.
I have following structure:
public class Base : BaseViewModel
{
public string Name{ get; set; }
}
public class SubBase<T> : Base
{
public virtual IEnumerable<T> Values{ get; set; }
private T selectedValue;
public T SelectedValue
{
get { return selectedValue; }
set
{
selectedValue= value;
OnPropertyChanged();
}
}
}
The problem is that I create lists of BaseFiltr as follows
ObservableCollection<BaseFiltr>
and then I have one of elements from list which is of type BaseFiltr and I want to get its SelectedValue property but unfortunately it is not possible since it is placed in derived class. I cannot place this property in BaseFiltr since I would have to mark it as generic. BaseFiltr is created only for list's purpose to avoid setting as follows:
ObservableCollection<BaseFiltr<some type>>
Any suggestions what might be done? Thank you in advance.
I have one of elements from list which is of type BaseFiltr and I want to get its WybranaWartosc property
You have a fundamental problem - BaseFiltr does not have a WybranaWartosc property - only SubBaseFiltr<T> does. You could check each item to see if it's a SubBaseFiltr<T> and then cast and probe it's WybranaWartosc property.
BaseFiltr is created only for list's purpose to avoid setting as follows: ObservableCollection<BaseFiltr<some type>>
It appears you're sacrificing proper typing for a slight decrease in complexity. If your collection is really a collection of SubBaseFiltr<T> objects, then use that type.
I have a ListBox, and it's items consist of custom class objects (can be any class).
Then I set the DisplayMemberPath so the ListBox shows the right property of that custom class fine.
Now I need to enumerate the Items list of ListBox, and get the DisplayMember value of each item in the list, without knowing the type of the class in the list. Is there any way to get this DisplayMember value without Reflection?
In WPF, you don't need to implement an interface, or a base class for a container control to read the value of a property. In an ideal world, it would make sense to declare a base class or interface and have all of your custom classes extend, or implement these, but the benefit of that is really to keep your data type safe.
For example, in WPF, this is perfectly legal and will work just the same:
public class RadioButtonData
{
public string Label { get; set; }
public bool IsSelected { get; set; }
}
public class CustomData
{
public string Label { get; set; }
public string Value { get; set; }
}
...
private ObservableCollection<object> objects = new ObservableCollection<object>();
public ObservableCollection<object> Objects
{
get { return objects; }
set { objects = value; NotifyPropertyChanged("Objects"); }
}
...
Objects.Add(new RadioButtonData() { Label = "Some Value" });
Objects.Add(new CustomData() { Label = "Another Value" });
...
<ListBox ItemsSource="{Binding Objects}" DisplayMemberPath="Label" />
So as long as your various classes have the same name of property, then they will all be displayed in the same way, like above. They don't even have to be of the same type... just as long as the name matches that used in the ListBox.DisplayMemberPath property.
UPDATE >>>
Ah sorry, I misunderstood your question. In the case that you want to access these property values in code, then you have four basic options:
Define an Interface with a particular property and make your custom classes implement it.
Declare a base class with a particular property and make your custom classes extend it.
Create a (potentially long) section of if else statements that checks the type of each object and then accesses the relevant property.
Use reflection.
In my personal opinion, I would recommend options 1 or 2 first, then 4 and lastly 3. I'm really not sure what you have against reflection, but it's really not that bad, or slow... I'd certainly prefer to use it rather than having an else if statement for every possible type used.
I have a collection of custom objects and i want to bind the index property of the ItemsControl to one of the int property in my custom object. how do i define such binding in the template? do i need a converter? any suggestions? thanks
First problem: ItemsControl doesn't have an Index or SelectedIndex property. For that, you need something that derives from Selector (like ComboBox, ListBox, etc.).
In that case, you can accomplish what you want easily using the SelectedValue and SelectedValuePath properties.
public class MyCustomObject {
public int CustomObjectIndex {get;set;}
}
public class ViewModel : INotifyPropertyChanged {
public IEnumerable<MyCustomObject> Items {get { return something;} }
// Setting this must raise PropertyChanged.
public int SelectedIndex {get; set; }
}
<ComboBox ItemsSource={Binding Items}
SelectedValue={Binding SelectedIndex, Mode=TwoWay}
SelectedValuePath="CustomObjectIndex" />
what you want to do doesnt make sense...
imagine you have a custom object with properties (name, wishedIndex) (wishedIndex as integer or whatever other magic to evaluate the wished index)
and now you have several of these objects --> several of wished indices.
Somewhere in your architecture you made a bad design choice. if you post more code we can find out
I have a problem that is difficult to explain. Essentially I have a list of a certain class we can call MyObj. One of the properties of this object is a custom list itself. I would like to bind this List to a dataGridView and have this particular property that is also a list show up. Any ideas? Am I being clear enough? :-P..
Here is the idea. I have my own custom list object overriding the ToString() method:
public class CategoriesList : List<Category>
{
public override string ToString()
{...}
}
This is used as a property in an object such as:
public MyObj
{
public string Property1 {get; set; }
public string Property2 {get; set; }
public CategoriesList Categories {get; set; }
}
In turn, I have a list of these objects such as:
List<MyObj> myDataSouce = SomeRepository.GetMyObjList();
Where I bind this to a datagrid view:
MyDataGridView.DataSource = myDataSource;
Property1 and Property2 are automatically generated. Is there any way to have the CategoriesList property be added as well? I previously thought Overriding the ToString() method on a class would be enough..
I am really lost on this one as I have no idea how to even google for it :-P
Assuming that you'd like to display a specific value in place of the list in the datagridview, you'll want to use a custom TypeConverter. Otherwise you'll need to place a control in the datagridview column that supports lists, like a drop down list and bind to that.
For the former:
Basically decorate your categories property with a custom typeconverter:
[TypeConverter(typeof(MyConverter))]
public CategoriesList Categories { get; set; }
Then use a custom type converter that basically tells the datagrid that when it encounters the categories property what do display:
public class MyConverter : TypeConverter
{
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is CategoriesList) {
return value.ToString();
}
return base.ConvertFrom(context, culture, value);
}
}
You'll need to add your column to be databound manually by adding an unbound column and specify the DataPropertyName for the property to be mapped to that column, in this case "Categories"
If you're looking to display second level properties as well then this may help:
http://blogs.msdn.com/b/msdnts/archive/2007/01/19/how-to-bind-a-datagridview-column-to-a-second-level-property-of-a-data-source.aspx
This might help... look at my answer there, I haven't tried it with a property that is also a type of list but I think the idea is the same.
Or this one as well, I also have an answer there with a sample code too...