How to populate options of h:selectOneMenu from database? - database

I am creating a web application, where you have to read a list of objects / entities from a DB and populate it in a JSF <h:selectOneMenu>. I am unable to code this. Can someone show me how to do it?
I know how to get a List<User> from the DB. What I need to know is, how to populate this list in a <h:selectOneMenu>.
<h:selectOneMenu value="#{bean.name}">
...?
</h:selectOneMenu>

Based on your question history, you're using JSF 2.x. So, here's a JSF 2.x targeted answer. In JSF 1.x you would be forced to wrap item values/labels in ugly SelectItem instances. This is fortunately not needed anymore in JSF 2.x.
Basic example
To answer your question directly, just use <f:selectItems> whose value points to a List<T> property which you preserve from the DB during bean's (post)construction. Here's a basic kickoff example assuming that T actually represents a String.
<h:selectOneMenu value="#{bean.name}">
<f:selectItems value="#{bean.names}" />
</h:selectOneMenu>
with
#ManagedBean
#RequestScoped
public class Bean {
private String name;
private List<String> names;
#EJB
private NameService nameService;
#PostConstruct
public void init() {
names = nameService.list();
}
// ... (getters, setters, etc)
}
Simple as that. Actually, the T's toString() will be used to represent both the dropdown item label and value. So, when you're instead of List<String> using a list of complex objects like List<SomeEntity> and you haven't overridden the class' toString() method, then you would see com.example.SomeEntity#hashcode as item values. See next section how to solve it properly.
Also note that the bean for <f:selectItems> value does not necessarily need to be the same bean as the bean for <h:selectOneMenu> value. This is useful whenever the values are actually applicationwide constants which you just have to load only once during application's startup. You could then just make it a property of an application scoped bean.
<h:selectOneMenu value="#{bean.name}">
<f:selectItems value="#{data.names}" />
</h:selectOneMenu>
Complex objects as available items
Whenever T concerns a complex object (a javabean), such as User which has a String property of name, then you could use the var attribute to get hold of the iteration variable which you in turn can use in itemValue and/or itemLabel attribtues (if you omit the itemLabel, then the label becomes the same as the value).
Example #1:
<h:selectOneMenu value="#{bean.userName}">
<f:selectItems value="#{bean.users}" var="user" itemValue="#{user.name}" />
</h:selectOneMenu>
with
private String userName;
private List<User> users;
#EJB
private UserService userService;
#PostConstruct
public void init() {
users = userService.list();
}
// ... (getters, setters, etc)
Or when it has a Long property id which you would rather like to set as item value:
Example #2:
<h:selectOneMenu value="#{bean.userId}">
<f:selectItems value="#{bean.users}" var="user" itemValue="#{user.id}" itemLabel="#{user.name}" />
</h:selectOneMenu>
with
private Long userId;
private List<User> users;
// ... (the same as in previous bean example)
Complex object as selected item
Whenever you would like to set it to a T property in the bean as well and T represents an User, then you would need to bake a custom Converter which converts between User and an unique string representation (which can be the id property). Do note that the itemValue must represent the complex object itself, exactly the type which needs to be set as selection component's value.
<h:selectOneMenu value="#{bean.user}" converter="#{userConverter}">
<f:selectItems value="#{bean.users}" var="user" itemValue="#{user}" itemLabel="#{user.name}" />
</h:selectOneMenu>
with
private User user;
private List<User> users;
// ... (the same as in previous bean example)
and
#ManagedBean
#RequestScoped
public class UserConverter implements Converter {
#EJB
private UserService userService;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) {
if (submittedValue == null || submittedValue.isEmpty()) {
return null;
}
try {
return userService.find(Long.valueOf(submittedValue));
} catch (NumberFormatException e) {
throw new ConverterException(new FacesMessage(String.format("%s is not a valid User ID", submittedValue)), e);
}
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object modelValue) {
if (modelValue == null) {
return "";
}
if (modelValue instanceof User) {
return String.valueOf(((User) modelValue).getId());
} else {
throw new ConverterException(new FacesMessage(String.format("%s is not a valid User", modelValue)), e);
}
}
}
(please note that the Converter is a bit hacky in order to be able to inject an #EJB in a JSF converter; normally one would have annotated it as #FacesConverter(forClass=User.class), but that unfortunately doesn't allow #EJB injections)
Don't forget to make sure that the complex object class has equals() and hashCode() properly implemented, otherwise JSF will during render fail to show preselected item(s), and you'll on submit face Validation Error: Value is not valid.
public class User {
private Long id;
#Override
public boolean equals(Object other) {
return (other != null && getClass() == other.getClass() && id != null)
? id.equals(((User) other).id)
: (other == this);
}
#Override
public int hashCode() {
return (id != null)
? (getClass().hashCode() + id.hashCode())
: super.hashCode();
}
}
Complex objects with a generic converter
Head to this answer: Implement converters for entities with Java Generics.
Complex objects without a custom converter
The JSF utility library OmniFaces offers a special converter out the box which allows you to use complex objects in <h:selectOneMenu> without the need to create a custom converter. The SelectItemsConverter will simply do the conversion based on readily available items in <f:selectItem(s)>.
<h:selectOneMenu value="#{bean.user}" converter="omnifaces.SelectItemsConverter">
<f:selectItems value="#{bean.users}" var="user" itemValue="#{user}" itemLabel="#{user.name}" />
</h:selectOneMenu>
See also:
Our <h:selectOneMenu> wiki page

View-Page
<h:selectOneMenu id="selectOneCB" value="#{page.selectedName}">
<f:selectItems value="#{page.names}"/>
</h:selectOneMenu>
Backing-Bean
List<SelectItem> names = new ArrayList<SelectItem>();
//-- Populate list from database
names.add(new SelectItem(valueObject,"label"));
//-- setter/getter accessor methods for list
To display particular selected record, it must be one of the values in the list.

Roll-your-own generic converter for complex objects as selected item
The Balusc gives a very useful overview answer on this subject. But there is one alternative he does not present: The Roll-your-own generic converter that handles complex objects as the selected item. This is very complex to do if you want to handle all cases, but pretty simple for simple cases.
The code below contains an example of such a converter. It works in the same spirit as the OmniFaces SelectItemsConverter as it looks through the children of a component for UISelectItem(s) containing objects. The difference is that it only handles bindings to either simple collections of entity objects, or to strings. It does not handle item groups, collections of SelectItems, arrays and probably a lot of other things.
The entities that the component binds to must implement the IdObject interface. (This could be solved in other way, such as using toString.)
Note that the entities must implement equals in such a way that two entities with the same ID compares equal.
The only thing that you need to do to use it is to specify it as converter on the select component, bind to an entity property and a list of possible entities:
<h:selectOneMenu value="#{bean.user}" converter="selectListConverter">
<f:selectItem itemValue="unselected" itemLabel="Select user..."/>
<f:selectItem itemValue="empty" itemLabel="No user"/>
<f:selectItems value="#{bean.users}" var="user" itemValue="#{user}" itemLabel="#{user.name}" />
</h:selectOneMenu>
Converter:
/**
* A converter for select components (those that have select items as children).
*
* It convertes the selected value string into one of its element entities, thus allowing
* binding to complex objects.
*
* It only handles simple uses of select components, in which the value is a simple list of
* entities. No ItemGroups, arrays or other kinds of values.
*
* Items it binds to can be strings or implementations of the {#link IdObject} interface.
*/
#FacesConverter("selectListConverter")
public class SelectListConverter implements Converter {
public static interface IdObject {
public String getDisplayId();
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null || value.isEmpty()) {
return null;
}
return component.getChildren().stream()
.flatMap(child -> getEntriesOfItem(child))
.filter(o -> value.equals(o instanceof IdObject ? ((IdObject) o).getDisplayId() : o))
.findAny().orElse(null);
}
/**
* Gets the values stored in a {#link UISelectItem} or a {#link UISelectItems}.
* For other components returns an empty stream.
*/
private Stream<?> getEntriesOfItem(UIComponent child) {
if (child instanceof UISelectItem) {
UISelectItem item = (UISelectItem) child;
if (!item.isNoSelectionOption()) {
return Stream.of(item.getValue());
}
} else if (child instanceof UISelectItems) {
Object value = ((UISelectItems) child).getValue();
if (value instanceof Collection) {
return ((Collection<?>) value).stream();
} else {
throw new IllegalStateException("Unsupported value of UISelectItems: " + value);
}
}
return Stream.empty();
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value == null) return null;
if (value instanceof String) return (String) value;
if (value instanceof IdObject) return ((IdObject) value).getDisplayId();
throw new IllegalArgumentException("Unexpected value type");
}
}

I'm doing it like this:
Models are ViewScoped
converter:
#Named
#ViewScoped
public class ViewScopedFacesConverter implements Converter, Serializable
{
private static final long serialVersionUID = 1L;
private Map<String, Object> converterMap;
#PostConstruct
void postConstruct(){
converterMap = new HashMap<>();
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object object) {
String selectItemValue = String.valueOf( object.hashCode() );
converterMap.put( selectItemValue, object );
return selectItemValue;
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String selectItemValue){
return converterMap.get(selectItemValue);
}
}
and bind to component with:
<f:converter binding="#{viewScopedFacesConverter}" />
If you will use entity id rather than hashCode you can hit a collision- if you have few lists on one page for different entities (classes) with the same id

Call me lazy but coding a Converter seems like a lot of unnecessary work. I'm using Primefaces and, not having used a plain vanilla JSF2 listbox or dropdown menu before, I just assumed (being lazy) that the widget could handle complex objects, i.e. pass the selected object as is to its corresponding getter/setter like so many other widgets do. I was disappointed to find (after hours of head scratching) that this capability does not exist for this widget type without a Converter. In fact if you supply a setter for the complex object rather than for a String, it fails silently (simply doesn't call the setter, no Exception, no JS error), and I spent a ton of time going through BalusC's excellent troubleshooting tool to find the cause, to no avail since none of those suggestions applied. My conclusion: listbox/menu widget needs adapting that other JSF2 widgets do not. This seems misleading and prone to leading the uninformed developer like myself down a rabbit hole.
In the end I resisted coding a Converter and found through trial and error that if you set the widget value to a complex object, e.g.:
<p:selectOneListbox id="adminEvents" value="#{testBean.selectedEvent}">
... when the user selects an item, the widget can call a String setter for that object, e.g. setSelectedThing(String thingString) {...}, and the String passed is a JSON String representing the Thing object. I can parse it to determine which object was selected. This feels a little like a hack, but less of a hack than a Converter.

Related

Update a wpf label periodically

I am new to WPF and C# im trying to understand how can I update a UI element from a BL class (to keep a seperation between the logic and the UI) the bl gets periodic updates from a c++ network component and should update the form once a new argument comes in (I read on the msdn website but I want to see some concrete examples to make sure I got it right)
Because of your gets periodic updates from a c++ network component comment, I am assuming that you already have a system to update your property. I would expose that property from your business class in a view model class, a class with public properties and ICommand functions designed specifically to supply all of the required data to a view, or UserControl.
To be honest, I wouldn't have that (or any) functionality in a business class (depending what you mean by business class)... I'd personally put it straight into the view model, or have a manager/service class that exposed it.
If you insist on keeping it where it is, you'll have to implement either an event or a delegate in your business class so that users of that class can be alerted as to when the value changes. Then you could simply attach a handler to your event/delegate from the view model class and easily update the exposed property whenever the actual property changes.
So it would go a little something like this... in your business class (I am assuming that your value is an int, but you can change this if it is incorrect... the principal is the same):
public delegate void FieldUpdate(int value);
public FieldUpdate OnFieldUpdate { get; set; }
...
private int field;
public int Field
{
get { return field; }
set
{
if (value != field)
{
field = value;
if (OnFieldUpdate != null) OnFieldUpdate(field);
}
}
}
Then in your view model:
private YourBusinessClass instance = new YourBusinessClass();
public YourBusinessClass Instance
{
get { return instance; }
set { instance = value; NotifyPropertyChanged("Instance"); }
}
Attach a handler:
instance.OnFieldUpdate += OnBusinessClassFieldUpdate;
...
public void OnBusinessClassFieldUpdate(int value)
{
Instance = value;
}
Now whenever the field is updated in the business class, the view model (and data bound UI controls) will automatically update via the delegate.

SEAM Parametrized inputtext

I have a problem with <h:inputText>. In particular I have a series of inputtext, combobox, calendar on an xhtml page.
Each of them has the value attribute like follow value="#{myBean.first}", value="#{myBean.second}", and so on.
In this manner myBean must have an enormous number of setter and getter.
I need to use only one setter and only one getter like the following:
public void setValue(String theId, String theValue){}
public String getValue(String theId){}
So in these only two methods I use a Map with key=theId and value=theValue inserted by user.
My question is how can do this in xhtml page?
That's how value-tag would be? Is there a special notice for passing a parameter to setter/getter?
Note that the "parameter" added to the inputText could be an object.
How can I do?
If you want to store the key/value pairs in a Map, you can reference the map directly in your UI components. Say your backing bean has this:
public Map<String, Object> getDataMap() { return dataMap; }
public void setDataMap(Map<String,Object> dataMap) { this.dataMap = dataMap; }
Your xhtml could look like this:
<h:inputText value="#{myBean.dataMap['first']}" />
<h:inputText value="#{myBean.dataMap['second']}" />

Exposing collection methods when creating a custom collection

I have to develop a custom collection of objects. The reason is two fold, I need to be able to assign an internal name to the collection and the collection also needs to implement some abstract methods to be treated like any other entity that I have.
So I created an EntityList class. Below is a snippet of the class. It contains a id and a list of entities, plus a bunch of methods. My question is this, so far I have put in the list management methods that I require, such as Add, Insert, Remove and Clear. If you have an EntityList reference called myEntityList you could perform something like myEntityList.Add(newEntity). I do like this approach, but really these methods are just handing off the work to the list. I could also not implement any of these methods and you could perform the same action as above by using myEntityList.Items.Add(newEntity). However, here you are directly accessing a method of a property of the object. I wanted to remove the Items property altogether, however I often need to iterate through the list using a foreach and for that I need access to the actual list.
Here is my class definition, it does not have the overrides to the abstact methods included.
class EntityList
{
String entityId;
List<EntityBase> _entities;
public EntityList()
{
_entities = new List<EntityBase>();
}
public List<EntityBase> Items
{
get { return _entities; }
//set { _entities = value; }
}
public void Add(EntityBase entity)
{
_entities.Add(entity);
}
public void Insert(int index, EntityBase entity)
{
_entities.Insert(index, entity);
}
public void Remove(EntityBase entity)
{
_entities.Remove(entity);
}
public void Clear()
{
_entities.Clear();
}
}
Am I violating some cardinal rules here? How should I manage the list when it is a member of another class?
Just inherit from List<EntityBase> then you won't need to redeclare and implement the list methods.
i.e.
class EntityList : List<EntityBase>
{
String entityId;
//add your extra methods.
}
you should make your class implement IList<EntityBase> (or at the very least, IEnumerable<EntityBase>) this way, you can treat it just like a "normal" list. Before you do this, though, you should probably read the documentation and decide which is best for your needs.
MSDN on IList is here
MSDN on IEnumerable is here

How to write a commom class for two ViewModel classes?

I have two viewmodel classes called ChangePwdViewModel.cs and ExpiringPwdViewModel.cs.
ChangPwd.xaml binds to ChangePwdViewModel and ExpiringPwd.xaml binds to ExpiringPwdViewModel.
Both have the property as below.
private string _message;
public string Message
{
get { return _message; }
set { _message = value; OnPropertyChanged("Message"); }
}
In each class, there's a function called ValidatePwd() to validate the new password.
In this function, Message property is updated.
Eg.
if (IsAlphaNumeric(this.NewPassword) == false || IsAlphaNumeric(this.CfmPassword) == false)
{
this.Message = "Invalid new password, only characters and numbers are accepted, password must contain at least one character and one number";
this.ResetPasswordFields();
return false;
}
I want to create a common class to write this function and used by two viewmodel. But, How can I update the Message Property of the viewmodels from this class?
How about putting it in a base class:
class ViewModelBase
{
private string _message;
public string Message
{
get { return _message; }
set { _message = value; OnPropertyChanged("Message"); }
}
public bool VerifyPassword(string newPassword)
{
....
}
}
class ChangePwdViewModel : ViewModelBase
{
}
class ExpiringPwdViewModel : ViewModelBase
{
}
Update:
If you can't use a base class because your view models already have a base class then you could use an interface as suggested by others. However this means that you will still have to implement the interface in all your view model classes so you don't gain that much in terms of avoiding multiple implementations (except that you have a contract for your view models then which is usually a good thing to have).
You can achieve some kind of "multiple inheritance" in C# by using a tool like Dynamic Proxy which allows you to create mixins. So you could implement the Message property and password verification in one class and then create a mixin proxy which merges the view model with that implementation. It's not as nice as you will have to create all your view model instances via the proxy generator but it can be made to work. Have a look at this tutorial if it sounds like an option for you.
You could have the two ViewModel classes implement a common interface, say IMessage that implemented a single property - Message.
Then your common class or a function would take a parameter of type IMessage that it could use to update the message.
I would suggest to avoid base classes (could cause potential design issues in future) in such cases, I would rather suggest to pass through constructor an algorithm of validation, smth like this:
public class MyViewModel
{
public MyViewModel(Func<bool> validationAlgorithm)
{
// ... save function to use later for a validation
}
}

How to use a factory for DataGrid.CanUserAddRows = true

I would like to use the DataGrid.CanUserAddRows = true feature. Unfortunately, it seems to work only with concrete classes which have a default constructor. My collection of business objects doesn't provide a default constructor.
I'm looking for a way to register a factory that knows how to create the objects for the DataGrid. I had a look at the DataGrid and the ListCollectionView but none of them seems to support my scenario.
The problem:
"I'm looking for a way to register a factory that knows how to create the objects for the DataGrid". (Because my collection of business objects doesn't provide a default constructor.)
The symptoms:
If we set DataGrid.CanUserAddRows = true and then bind a collection of items to the DataGrid where the item doesn't have a default constructor, then the DataGrid doesn't show a 'new item row'.
The causes:
When a collection of items is bound to any WPF ItemControl, WPF wraps the collection in either:
a BindingListCollectionView when the collection being bound is a BindingList<T>. BindingListCollectionView implements IEditableCollectionView but doesn't implement IEditableCollectionViewAddNewItem.
a ListCollectionView when the collection being bound is any other collection. ListCollectionView implements IEditableCollectionViewAddNewItem (and hence IEditableCollectionView).
For option 2) the DataGrid delegates creation of new items to the ListCollectionView. ListCollectionView internally tests for the existence of a default constructor and disables AddNew if one doesn't exist. Here's the relevant code from ListCollectionView using DotPeek.
public bool CanAddNewItem (method from IEditableCollectionView)
{
get
{
if (!this.IsEditingItem)
return !this.SourceList.IsFixedSize;
else
return false;
}
}
bool CanConstructItem
{
private get
{
if (!this._isItemConstructorValid)
this.EnsureItemConstructor();
return this._itemConstructor != (ConstructorInfo) null;
}
}
There doesn't seem to be an easy way to override this behaviour.
For option 1) the situation is a lot better. The DataGrid delegates creation of new items to the BindingListView, which in turn delegates to BindingList. BindingList<T> also checks for the existence of a default constructor, but fortunately BindingList<T> also allows the client to set the AllowNew property and attach an event handler for supplying a new item. See the solution later, but here's the relevant code in BindingList<T>
public bool AllowNew
{
get
{
if (this.userSetAllowNew || this.allowNew)
return this.allowNew;
else
return this.AddingNewHandled;
}
set
{
bool allowNew = this.AllowNew;
this.userSetAllowNew = true;
this.allowNew = value;
if (allowNew == value)
return;
this.FireListChanged(ListChangedType.Reset, -1);
}
}
Non-solutions:
Support by DataGrid (not available)
It would reasonable to expect the DataGrid to allow the client to attach a callback, through which the DataGrid would request a default new item, just like BindingList<T> above. This would give the client the first crack at creating a new item when one is required.
Unfortunately this isn't supported directly from the DataGrid, even in .NET 4.5.
.NET 4.5 does appear to have a new event 'AddingNewItem' that wasn't available previously, but this only lets you know a new item is being added.
Work arounds:
Business object created by a tool in the same assembly: use a partial class
This scenario seems very unlikely, but imagine that Entity Framework created its entity classes with no default constructor (not likely since they wouldn't be serializable), then we could simply create a partial class with a default constructor. Problem solved.
Business object is in another assembly, and isn't sealed: create a super-type of the business object.
Here we can inherit from the business object type and add a default constructor.
This initially seemed like a good idea, but on second thoughts this may require more work than is necessary because we need to copy data generated by the business layer into our super-type version of the business object.
We would need code like
class MyBusinessObject : BusinessObject
{
public MyBusinessObject(BusinessObject bo){ ... copy properties of bo }
public MyBusinessObject(){}
}
And then some LINQ to project between lists of these objects.
Business object is in another assembly, and is sealed (or not): encapsulate the business object.
This is much easier
class MyBusinessObject
{
public BusinessObject{ get; private set; }
public MyBusinessObject(BusinessObject bo){ BusinessObject = bo; }
public MyBusinessObject(){}
}
Now all we need to do is use some LINQ to project between lists of these objects, and then bind to MyBusinessObject.BusinessObject in the DataGrid. No messy wrapping of properties or copying of values required.
The solution: (hurray found one)
Use BindingList<T>
If we wrap our collection of business objects in a BindingList<BusinessObject> and then bind the DataGrid to this, with a few lines of code our problem is solved and the DataGrid will appropriately show a new item row.
public void BindData()
{
var list = new BindingList<BusinessObject>( GetBusinessObjects() );
list.AllowNew = true;
list.AddingNew += (sender, e) =>
{e.NewObject = new BusinessObject(... some default params ...);};
}
Other solutions
implement IEditableCollectionViewAddNewItem on top of an existing collection type. Probably a lot of work.
inherit from ListCollectionView and override functionality. I was partially successful trying this, probably can be done with more effort.
I've found another solution to this problem. In my case, my objects need to be initialized using a factory, and there isn't really any way to get around that.
I couldn't use BindingList<T> because my collection must support grouping, sorting, and filtering, which BindingList<T> does not support.
I solved the problem by using DataGrid's AddingNewItem event. This almost entirely undocumented event not only tells you a new item is being added, but also allows lets you choose which item is being added. AddingNewItem fires before anything else; the NewItem property of the EventArgs is simply null.
Even if you provide a handler for the event, DataGrid will refuse to allow the user to add rows if the class doesn't have a default constructor. However, bizarrely (but thankfully) if you do have one, and set the NewItem property of the AddingNewItemEventArgs, it will never be called.
If you choose to do this, you can make use of attributes such as [Obsolete("Error", true)] and [EditorBrowsable(EditorBrowsableState.Never)] in order to make sure no one ever invokes the constructor. You can also have the constructor body throw an exception
Decompiling the control lets us see what's happening in there.
private object AddNewItem()
{
this.UpdateNewItemPlaceholder(true);
object newItem1 = (object) null;
IEditableCollectionViewAddNewItem collectionViewAddNewItem = (IEditableCollectionViewAddNewItem) this.Items;
if (collectionViewAddNewItem.CanAddNewItem)
{
AddingNewItemEventArgs e = new AddingNewItemEventArgs();
this.OnAddingNewItem(e);
newItem1 = e.NewItem;
}
object newItem2 = newItem1 != null ? collectionViewAddNewItem.AddNewItem(newItem1) : this.EditableItems.AddNew();
if (newItem2 != null)
this.OnInitializingNewItem(new InitializingNewItemEventArgs(newItem2));
CommandManager.InvalidateRequerySuggested();
return newItem2;
}
As we can see, in version 4.5, the DataGrid does indeed make use of AddNewItem. The contents of CollectionListView.CanAddNewItem are simply:
public bool CanAddNewItem
{
get
{
if (!this.IsEditingItem)
return !this.SourceList.IsFixedSize;
else
return false;
}
}
So this doesn't explain why we we still need to have a constructor (even if it is a dummy) in order for the add row option to appear. I believe the answer lies in some code that determines the visibility of the NewItemPlaceholder row using CanAddNew rather than CanAddNewItem. This might be considered some sort of bug.
I had a look at IEditableCollectionViewAddNewItem and it seems to be adding this functionality.
From MSDN
The IEditableCollectionViewAddNewItem
interface enables application
developers to specify what type of
object to add to a collection. This
interface extends
IEditableCollectionView, so you can
add, edit, and remove items in a
collection.
IEditableCollectionViewAddNewItem adds
the AddNewItem method, which takes an
object that is added to the
collection. This method is useful when
the collection and objects that you
want to add have one or more of the
following characteristics:
The objects in the CollectionView are different types.
The objects do not have a default constructor.
The object already exists.
You want to add a null object to the collection.
Although at Bea Stollnitz blog, you can read the following
The limitation of not being able to add a new item when the source has no
default constructor is very well
understood by the team. WPF 4.0 Beta 2
has a new feature that brings us a
step closer to having a solution: the
introduction of
IEditableCollectionViewAddNewItem
containing the AddNewItem method. You
can read the MSDN documentation about
this feature. The sample in MSDN shows
how to use it when creating your own
custom UI to add a new item (using a
ListBox to display the data and a
dialog box to enter the new item).
From what I can tell, DataGrid doesn’t
yet use this method though (although
it’s a bit hard to be 100% sure
because Reflector doesn’t decompile
4.0 Beta 2 bits).
That answer is from 2009 so maybe it's usable for the DataGrid now
The simplest way I could suggest to provide wrapper for your class without default constructor, in which constructor for source class will be called.
For example you have this class without default constructor:
/// <summary>
/// Complicate class without default constructor.
/// </summary>
public class ComplicateClass
{
public ComplicateClass(string name, string surname)
{
Name = name;
Surname = surname;
}
public string Name { get; set; }
public string Surname { get; set; }
}
Write a wrapper for it:
/// <summary>
/// Wrapper for complicated class.
/// </summary>
public class ComplicateClassWraper
{
public ComplicateClassWraper()
{
_item = new ComplicateClass("def_name", "def_surname");
}
public ComplicateClassWraper(ComplicateClass item)
{
_item = item;
}
public ComplicateClass GetItem() { return _item; }
public string Name
{
get { return _item.Name; }
set { _item.Name = value; }
}
public string Surname
{
get { return _item.Surname; }
set { _item.Surname = value; }
}
ComplicateClass _item;
}
Codebehind.
In your ViewModel you need to create wrapper collection for your source collection, which will handle item adding/removing in datagrid.
public MainWindow()
{
// Prepare collection with complicated objects.
_sourceCollection = new List<ComplicateClass>();
_sourceCollection.Add(new ComplicateClass("a1", "b1"));
_sourceCollection.Add(new ComplicateClass("a2", "b2"));
// Do wrapper collection.
WrappedSourceCollection = new ObservableCollection<ComplicateClassWraper>();
foreach (var item in _sourceCollection)
WrappedSourceCollection.Add(new ComplicateClassWraper(item));
// Each time new item was added to grid need add it to source collection.
// Same on delete.
WrappedSourceCollection.CollectionChanged += new NotifyCollectionChangedEventHandler(Items_CollectionChanged);
InitializeComponent();
DataContext = this;
}
void Items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
foreach (ComplicateClassWraper wrapper in e.NewItems)
_sourceCollection.Add(wrapper.GetItem());
else if (e.Action == NotifyCollectionChangedAction.Remove)
foreach (ComplicateClassWraper wrapper in e.OldItems)
_sourceCollection.Remove(wrapper.GetItem());
}
private List<ComplicateClass> _sourceCollection;
public ObservableCollection<ComplicateClassWraper> WrappedSourceCollection { get; set; }
}
And finally, XAML code:
<DataGrid CanUserAddRows="True" AutoGenerateColumns="False"
ItemsSource="{Binding Path=Items}">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Path=Name}"/>
<DataGridTextColumn Header="SecondName" Binding="{Binding Path=Surname}"/>
</DataGrid.Columns>
</DataGrid>
I just wanted to provide an alternate solution to using a BindingList. In my situtation, the Business objects was held in an IEntitySet in a portable project (Silverlight), which did not support IBindingList.
The solution, first and foremost, is to subclass the grid, and overwrite the coerce callback for CanUserAddRows to use IEditableCollectionViewAddNewItem:
public class DataGridEx : DataGrid
{
static DataGridEx()
{
CanUserAddRowsProperty.OverrideMetadata(typeof(DataGridEx), new FrameworkPropertyMetadata(true, null, CoerceCanUserAddRows));
}
private static object CoerceCanUserAddRows(DependencyObject sender, object newValue)
{
var dataGrid = (DataGrid)sender;
var canAddValue= (bool)newValue;
if (canAddValue)
{
if (dataGrid.IsReadOnly || !dataGrid.IsEnabled)
{
return false;
}
if (dataGrid.Items is IEditableCollectionViewAddNewItem v && v.CanAddNewItem == false)
{
// The view does not support inserting new items
return false;
}
}
return canAddValue;
}
}
And then use the AddingNewItem event to create the item:
dataGrid.AddingNewItem += (sender, args) => args.NewItem = new BusinessObject(args);
And if you care for the details, here is the reason why it is a problem in the first place. The coerce callback in the framework looks like this:
private static bool OnCoerceCanUserAddOrDeleteRows(DataGrid dataGrid, bool baseValue, bool canUserAddRowsProperty)
{
// Only when the base value is true do we need to validate that the user
// can actually add or delete rows.
if (baseValue)
{
if (dataGrid.IsReadOnly || !dataGrid.IsEnabled)
{
// Read-only/disabled DataGrids cannot be modified.
return false;
}
else
{
if ((canUserAddRowsProperty && !dataGrid.EditableItems.CanAddNew) ||
(!canUserAddRowsProperty && !dataGrid.EditableItems.CanRemove))
{
// The collection view does not allow the add or delete action
return false;
}
}
}
return baseValue;
}
You see how it gets the IEditableCollectionView.CanAddNew? That means that it only enables adding when the view can insert and construct an item. The funny thing is that when we want to add a new item, it checks the IEditableCollectionViewAddNewItem.CanAddNewItem instead, which only asks if the view supports inserting new items (not creating):
object newItem = null;
IEditableCollectionViewAddNewItem ani = (IEditableCollectionViewAddNewItem)Items;
if (ani.CanAddNewItem)
{
AddingNewItemEventArgs e = new AddingNewItemEventArgs();
OnAddingNewItem(e);
newItem = e.NewItem;
}

Resources