I have a wpf application using Caliburn.Micro. I need to bind a ListBox to a collection of objects, but I want to display one of the object's fields, and also somehow to attach a Guid (another field) to each item. Could you please tell me how I can do that? I don't know if Caliburn.Micro has something specific for it, or I just have to use WPF.
Thanks.
(sorry for my bad english)
If the Guid field is part of your object, you do not need to store it on another place. The listbox will show a field but it is still bounded to the original object, you can get it with ((MyObjectType)MyListBox.SelectedItem).Guid. With Caliburn it is even easier since you just need to bind a property on your VM to SelectedItem.
But if the Guid is not part of your object, you can use the Tag property, as Paul Sasik said. I do not like to use the Tag property so this is another easy (and more flexbible) way you can solve this, you need to encapsulate your object on another object:
public class GuidObject<T>
{
public T Instance {get;set;}
public Guid Guid {get;set;}
}
You can use it like this:
//this is your original guidless items list
var myObjectsList = new[] { new MyObject { Name = "Dostoyevsky" },
new MyObject { Name = "Ozzy" } };
var myObjectsWithGuidList = new ObservableCollection<GuidObject<MyObject>>();
//encapsulate each MyObject on a GuidObject and include a Guid
//if your myObjectsList is already a List, you do not need to call ToList()
myObjectsList.ToList().ForEach(o => myObjectsWithGuidList.Add(new GuidObject<MyObject>() { Instance = o, Guid = Guid.NewGuid() }));
//now myObjectsWithGuidList contains a list of your itens and a Guid field, you can bind it to your ListBox
Here you can see this running.
You can use the Tag property of each ListBox object to store arbitrary information.
From the link:
This property is analogous to Tag properties in other Microsoft
programming models, such as Microsoft Visual Basic for Applications
(VBA) or Windows Forms. Tag is intended to provide a pre-existing
property location where you can store some basic custom information
about any FrameworkElement without requiring you to subclass an
element.
Because this property takes an object, you would need to use the
property element usage in order to set the Tag property in Extensible
Application Markup Language (XAML) to anything other than an object
with a known and built-in type converter, such as a string. Objects
used in this manner are typically not within the standard Windows
Presentation Foundation (WPF) namespaces and therefore may require
namespace mapping to the external namespace in order to be introduced
as XAML elements.
Related
I may be missing something about the fundamentals of WPF design, but I was wondering why many properties on WPF controls are exposed as the type 'Object'?
For example, MenuItem.Icon is an Object, and so is MenuItem.ToolTip. As a near first time user, this was very confusing to me (it felt like I was using a dynamic programming language, having no idea whether setting ToolTip to a String type would even work or not). Moreover, I tried to set the Icon to a 'System.Drawing.Icon' and I get an ArgumentException of "Argument 'picture' must be a picture that can be used as a Icon." Shouldn't the property be typed so it can at least describe what in the world you're supposed to give it?
Honestly, my guess as to the reason is because you cannot implement an interface on a type you did not create (without creating a wrapper), but that's just a guess.
Thanks very much for your answers and insights!
The main reason in my opinion is that since an Object is the "ultimate base class of all classes in the .Net Framework". This gives you flexibility, in WPF you are not limited to a predefined type. Wpf is different and has a learning curve, but it does give you a lot more options to create a product that looks good.
i.e.
You can assign a TextBox to a ToolTip:
TextBox tb = new TextBox();
tb.Text = "Hello World";
this.ToolTip = tb;
a Bitmap
BitmapImage myBitmapImage = new BitmapImage(new Uri((#"C:\Temp\20100706.jpg")));
Image image = new Image();
image.Source = myBitmapImage;
this.ToolTip = image;
and assigning a Image to a MenuItem
BitmapImage myBitmapImage = new BitmapImage(new Uri((#"C:\Temp\20100706.jpg")));
Image image = new Image();
image.Source = myBitmapImage;
menuItem1.Icon = image;
Consider the ToolTip for example. A ToolTip is a ContentControl, which can contain any type of CLR (Common Language Runtime) object (such as a string or a DateTime object) or a UIElement object (such as a Rectangle or a Panel). This enables you to add rich content to controls such as Button and CheckBox.
For this reason, elements such as ToolTip are exposed as Object, that is the root of the type hierarchy (with resulting ease of use, flexibility and clarity of the code).
Imagine these properties were typed as UIElements (or some other WPF specific object). How would you add objects to your controls that were not UIElements?
You would have to provide a wrapper derived from a WPF object that exposes the information you require. Most of the time the wrapper would simply call ToString() of the object being wrapped. Seeing as most types you will be using provide a good enough default implementation of ToString() it makes sense to just call this instead of making the developer write wrappers for everything.
Second, imagine if they were typed as some interface. What if you want to communicate something that this interface can't? The only options are (a) the developer lives with the limitations of the framework or (b) Microsoft updates the interface and breaks all existing code which has already been written.
Also consider if you are using a pattern like MVVM. The current design means your view models can expose properties that are not tied to WPF in any way which ultimately makes your code more reusable across different technologies.
Finally, remember that there is a difference between the object that represents the property and they way that WPF renders that information. E.G. if you use a primitive type such as System.String, WPF will create a textblock and set the text property to the result of ToString(). This allows a very clean separation between the data that is displayed by the UI and they way the information is rendered by the UI.
Take a simple class that represents a menu item, for example:
public class MenuItem
{
public string Text { get; set; }
public bool IsChecked { get; set; }
public bool IsEnabled { get; set; }
}
This type only exposes data about the menu item and has no information about how this information should be rendered. In fact, apart from the name of the class (MenuItem) this is not even specific to a menu item and the same data could be used in another UI control such as a checked listbox with no changes required. If the class exposed WPF specific user interface elements then the information would need to be adapted by another type for each different user interface control.
I am working on a winforms application where I am rendering domain/object data via an ultrawingrid. I am using a bindingsource to bind the object to the grid.For simple objects this works quite well.
The thing I'm trying to get my head around is rendering an object with nested objects for e.g a Person class will have a property of Address class. I would like to display the properties of Address (Street, City, Country) as columns on the grid along with the properties of the Person class.
The grid has to be editable and any user changes need to reflect back on the domain object (which I'm doing via the binding source).
What's the best way to go about this?
Binding
I typically use some sort of code like this:
Dim persons = new BindingList(Of Person)
UltraGrid1.DataSource = persons
The binding list will handle the add/remove of rows for you, but it doesn't know about the properties inside Person. To get that part of the binding to work, you'll need to have Person implement INotifyPropertyChanged. This will notify the ultragrid when the properties have changed. The code will look something like this (yes, unfortunately this makes it so you can't use auto-implemented properties):
Private _phoneNumber As String
Public Property PhoneNumber As String
Get
Return Me._phoneNumber
End Get
Set(ByVal value As String)
If value <> _phoneNumber Then
Me._phoneNumber = value
NotifyPropertyChanged("PhoneNumber")
End If
End Set
End Property
Flattening object hierarchies
It looks like what you're ask for isn't directly possible. There are a few options:
Unbound columns in the UI that you fill in during the InitializeRow event
Modify your Person class to expose the properties of Address with some pass through code to handle the setting of the properties.
(I can provide a code samples if needed)
One-to-many Nested Objects
If you, for example, had multiple addresses per person, you could show them nested in an expandable section under each Person row. To do this, inside your Person you'd have a BindingList(Of Address) which also implements INotifyPropertyChanged. Not exactly what you want, but an option :)
Words of caution
A few notes if you are doing MVP. You'll need to have the same reference to the BindingList in your view and presenter, obviously. Also, if you need to reset the contents, I'd recommend calling list.Clear() instead of creating a new one. If you create a new one in your presenter, you'll have broken the connection with the UltraGrid and you'll have to re-set the DataSource property in the view.
I'm trying to improve on a Winforms project where datatable rows are stored in the Tag property of ListViewItems. When the datatable is refactored to List<T> (or actually classes containing lists) it would help immensely if I could make the Tag property generic by using a subclass of ListView.
In the best of worlds, I'd want the Tag property to be replaced by a public T Tag{get; set;} that wraps base.Tag and casts it.
Second best would be Obsoleting Tag and providing a new property like TypedTag working like above.
I think this would involve subclassing or composite aggregation of at least ListView, ListViewItemCollection, SelectedListViewItemCollection and ListViewItem, and I'm not sure how to do it.
In short:
ListView<Employee> lvwEmployees;
should result in this being possible:
Employee selected = lvwEmployees.SelectedItems[0].TypedTag;
And give a compilation error for this:
DataRow selected = lvwEmployees.SelectedItems[0].TypedTag;
Is it possible? Is it already done?
Project is dotnet 2.0 but I think I'll try to have it upgraded if it helps this matter.
EDIT: It turns out that the owner constructor argument is all a certain collection needs to hook up to the inner collection. Hence the following works:
ListView a = new ListView();
a.Items.Add("Hello");
Assert.AreEqual(1, new ListView.ListViewItemCollection(a).Count);
This makes it rather easy to create a generic tagged ListView. I'll post a complete solution later. :)
EDIT2: Here's the solution:
http://thecarlr.blogspot.com/2010/11/generic-listview.html
EDIT3: For designer support, just add a non generic subclass and use that.
Example: If you intended to use ListView<Employee> in the form, create a ListViewEmployee : ListView<Employee> in another file, and use ListViewEmployee in the form.
The easiest way to add one of theese listviews would be to add a normal listview to the form, and then change it's type in the source files. (And if you don't know where it's declared or instantiated, find out or use the normal listview instead.)
You made the wrong class generic. SelectedItems[0] is a ListViewItem, not a ListView.
There isn't anything you can do to change the type of the Items and SelectedItems properties. You can certainly derive your own class from ListViewItem and just add the property you want to store. No need for another Tag property. You'll have no trouble adding them but you'll need to cast back to your derived class when you retrieve them back from the Selected/Items collection.
In general, avoid this kind of code by using the ListView only as a view of your model. The ListViewItem.Index should then always be good to get a typesafe reference back from your model.
Here's the solution:
http://thecarlr.blogspot.com/2010/11/generic-listview.html
Enjoy :)
/Carl
VS Designer simply cannot handle abstract or generic controls (not for want of asking).
One way around that limitation is to write a type safe wrapper around a standard ListView.
Something like this:
public class TypedListView<T> where T : class
{
public TypedObjectListView(ListView lv) {
this.lv = lv;
}
private ListView lv;
public virtual T SelectedObject {
get { return (T)this.lv.SelectedItems[0].Tag; }
}
// Lots more methods/properties
}
You create a normal ListView in Designer, and then when you wanted to access it, you create and use your adapter instead. Like this:
var typedListView = new TypedListView<Employee>(this.listView1);
Employee selectedEmployee = typedListView.SelectedObject;
You would need to provide a typed version of every ListView properties or method you wanted to use.
The ObjectListView project takes this approach to create a TypedObjectListView which does exactly what are you asking for.
A domain model collection (normally a List or IEnumerable) is delegated to a ViewModel.
Thats means my CustomerViewModel has a order collection of type List or IEnumerable.
No change in the list is recognized by the bound control. But with ObservableCollection it is.
This is a problem in the MVVM design pattern.
How do you cope with it?
UPDATE: Sample of how I do it:
public class SchoolclassViewModel : ViewModelBase
{
private Schoolclass _schoolclass;
private ObservableCollection<PupilViewModel> _pupils = new ObservableCollection<PupilViewModel>();
public SchoolclassViewModel(Schoolclass schoolclass)
{
_schoolclass = schoolclass;
_schoolclass.Pupils = new List<Pupil>();
foreach (var p in schoolclass.Pupils)
Pupils.Add(new PupilViewModel(p));
}
public Schoolclass GetSchoolclass
{
get { return _schoolclass; }
}
public int ID { get; set; }
public string SchoolclassName
{
get { return _schoolclass.SchoolclassName;}
set
{
if(_schoolclass.SchoolclassName != value)
{
_schoolclass.SchoolclassName = value;
this.RaisePropertyChanged("SchoolclassName");
}
}
}
public ObservableCollection<PupilViewModel> Pupils
{
get{ return _pupils;}
set
{
_pupils = value;
this.RaisePropertyChanged("Pupils");
}
}
}
I deal with this by not doing it the way you describe.
If I need to present a Foo object and its related Bar objects in the view, the FooViewModel will generally implement a Bars property of type ObservableCollection<BarViewModel>.
Note that this is irrespective of whether or not the underlying Foo class has a Bars property of type IEnumerable<Bar>. The Foo class might not. The application might not even need to be able to iterate over all of the Bar objects for a Foo, except in the UI.
Edit
When my view is a simple representation of the application's object model, I pretty much do things as you do in your sample. The code in my constructor is generally a bit more compact:
_Bars = new ObservableCollection<BarViewModel>(
_Foo.Bars.Select(x => new BarViewModel(x)));
but it's essentially the same thing.
But this assumes that Foo actually exposes a Bars property. It might not. Or maybe only some Bar objects should appear in the view. Or maybe they should appear intermingled with other objects, and the FooViewModel should expose a CompositeCollection of some kind.
The point I'm making is that the view model is a model of the view. This doesn't necessarily have a direct correspondence to the underlying object model.
To pick a simple example: My program may give the user a way of putting items into five different categories by dragging and dropping them into five different ListBox controls. Ultimately, doing this sets a Category property on the Item object. My view model is going to have a collection of CategoryViewModel objects, each with a property of type ObservableCollection<ItemViewModel>, so that dragging items back and forth between collections is simple to implement.
The thing is, there may not even be a Category class in the application's object model, let alone a collection of Category objects. Item.Category might just be a property of type string. The CategoryViewModel isn't mirroring the application's object model. It only exists to support the view in the UI.
Ok, I'll go ahead and add my thoughts as an answer instead of in the comments. :)
I think the bottom line is that this is just the reality of the way WPF and databinding work. In order for two-way databinding to operate, collections need a means of notifying controls that are bound to them, and the standard lists and collections used in most domain objects don't/won't/shouldn't support this. As I mentioned in a comment, being required to implement INotifyPropertyChanged for non-collection properties is another requirement that may not be met by a standard domain object.
Domain objects are not intended to to be viewmodels, and for this reason you may find that you need to map back and forth between the two types of objects. This is not dissimilar to having to map back and forth between domain objects and data access objects. Each type of object has a different function in the system, and each should be specifically designed to support their own role in the system.
All that said, Agies's idea of using AOP to automatically generate proxy classes is very interesting, and something I intend to look into.
What I do is instead of using ObservableCollection in my domain model is use my own collection type that implements the INotifyCollectionChanged interface amongst other useful standard and custom interfaces. My way of thinking is that much like Rockford Lhotka suggests in his book that change notification is useful in to more than just a presentation layer since other business objects within the domain layer often need some sort of notification when state changes in another object.
With this methodology you could create your own collection type that still has the benefits of change notification and as well as what ever custom behaviors you need. The base class for your collection could be implemented as purely infrastructure code and then a subclass could be created that could contain business logic using the subtype layering techinque used in this book. So in the end you could have a collection that can wrap types of IEnumerable<> and provide the change notification stuff your looking for as well for both your domain model and presentation code.
I am new to WPF and trying to wrap my head around WPF's framework, what it does and does not do for you.
To clarify this, I would like to know what is the difference between this:
public List<MyCustomObject> MyCustomObjects
{
get { return (List<MyCustomObject>)GetValue(MyCustomObjectsProperty); }
set { SetValue(MyCustomObjectsProperty, value); }
}
public static readonly DependencyProperty MyCustomObjectsProperty =
DependencyProperty.Register("MyCustomObjects", typeof(List<MyCustomObject>),
typeof(Main), new UIPropertyMetadata(new List<MyCustomObject>()));
and this:
public ObservableCollection<MyCustomObject> MyCustomObjects { get; set; }
public Main ()
{
MyCustomObjects = new ObservableCollection<<MyCustomObject>();
}
Ok, we must put some order into things, there's a few concepts mixed in together here.
First of all, you're asking what the difference is between a field-backed property and a dependency property. Google would be your best friend, however I recommend this blog post by WPF's vanguard Josh Smith: Overview of dependency properties in WPF
In short: dependency properties support the richness that is WPF: Styling, animation, binding, metadata, and more.
Secondly, you're asking what the difference is between a List and an ObservableCollection. Well the latter provides change notifications (in the forms of events) on any change to the collection (addition, removal, change of order, clearing, etc.), and the former does not. You can read more about that here: The ObservableCollection Class
In short: ObservableCollection provides change notifications which are required for the UI to automatically reflect changes in the view model.
In addition to Aviad and Reed's answers, I would like to point out a serious bug in your first code sample :
public static readonly DependencyProperty MyCustomObjectsProperty =
DependencyProperty.Register("MyCustomObjects", typeof(List<MyCustomObject>),
typeof(Main), new UIPropertyMetadata(new List<MyCustomObject>()));
The new List<MyCustomObject>() used as the default value will be created only once, so by default all instances of your type will share the same List<MyCustomObject> instance, which is probably not what you want... The only sensible default value here is null
In the first case, you're setting up a Dependency Property containing a List<T> instance.
In the second, you're making a normal CLR property, but having it setup as an ObservableCollection<T>.
For WPF Data Binding, there are some differences here.
Typically, you want all of your properties in the DataContext (which is the object that, by default, things "bind" to) to either implement INotifyPropertyChanged or to be a Dependency Property. This lets the binding framework know when changes are made to that object. Normally, though, you'd only use a Dependency Property if your working with a custom control - it's usually a better idea to have your object to which your data bound be a separate class, assigned to the DataContext. (For details here, see Josh Smith on MVVM or my recent detailed post on MVVM...)
However, with a collection, you typically also want the binding system to know when the items within the collection change (ie: an item is added). ObservableCollection<T> handles this by implementing INotifyCollectionChanged.
By using the second approach (using an ObservableCollection<T>), your UI can tell when items were added or removed from the collection - not just when a new collection is assigned. This lets things work automatically, like a ListBox adding elements when a new item is added to your collection.
1:
You're using a dependency property to "tell" the framework when that property is changed. This will have the following consequences for your binding:
MyCustomObjects.Add(new MyCustomObject()); //Wont update the view through databinding
MyCustomObjects = new List<MyCustomObject>(); //Will update the view through databinding
You could gain the same databinding functionality by implementing INotifyPropertyChanged on which ever class exposes the property, but dependency properties a capable of much more than just notifying about changes. These are rather advanced features though, which you aren't likely to come across in your average joe app :)
2:
You're using an observable collection, which implements INotifyCollectionChanged for you, to tell the databinding whenever the content of the collection has changed. This will have the opposite consequences than #1:
MyCustomObjects.Add(new MyCustomObject()); //Will update the view through databinding
MyCustomObjects = new ObservableCollection<MyCustomObject>(); //Won't update the view through databinding