views:

1036

answers:

3

How do you bind a DropDownlist DataText or DataValue field from a object. When the object in question is at a second level, e.g. the object to bind is not in the first level of the returned object Users.ContactDetails.Telephone, as the code below doesn't work:

ddl.DataSource = dal.dal_Users.GetAllUsers();
ddl.DataTextField = "Telephone";

I have tried a range of ideas but can't seem to find any information on if this can be done or not.

+2  A: 

If you are using C# 3 then you can use the ConvertAll<> etension method to create a new anonymous type that moves the inner property up to the top level.

Try something like this:

IEnumerable<User> users = dal.dal_Users.GetAllUsers();

ddl.DataSource = users.ConvertAll(u => 
 new { Value = u.Name, Telephone = u.ContactDetails.Telephone });
ddl.DataTextField = "Telephone";
Andrew Hare
+1  A: 

EDIT: I got enough feedback about this topic that I felt it was worthy of a blog post

One approach that I took when binding domain objects to ddl controls is to inherit from a base class that has an overload in the constr to allow for a text/value only "subset" of the original. The code below is used in a Model View Presenter Or Model View ViewModel implementation that abstracts out the ddl control so you can push any UI on the top of this business code.

below is an example of how I bind a ddl with a collection of user objects, and set the selected based on the related object (unit of work)

 public void PopulateUserDDL(UnitOfWork UnitOfWorkObject)
 {
     List<User> UserCollection = mUserService.GetUserCollection();  //get a collection of objects to bind this ddl

     List<ILookupDTO> UserLookupDTO = new List<ILookupDTO>();

     UserLookupDTO.Add(new User(" ", "0"));
     //insert a blank row ... if you are into that type of thing

     foreach (var User in UserCollection) {
         UserLookupDTO.Add(new User(User.FullName, User.ID.ToString()));
     }

     LookupCollection LookupCollectionObject = new LookupCollection(UserLookupDTO);
     LookupCollectionObject.BindTo(mView.UserList);

     if (UnitOfWorkObject == null) {
         return;
     }

     if (UserCollection == null) {
         return;
     }

     for (i = 0; i <= UserCollection.Count - 1; i++) {
         if (UserCollection(i).ID == UnitOfWorkObject.User.ID) {
             LookupCollectionObject.SelectedIndex = (i + 1);
             //because we start at 0 instead of 1 (we inserted the blank row above)
             break;
         }
     }
 }

below is what the overload will look like in your user object

/// <summary>
/// This constr is used when you want to create a new Lookup Collection of ILookupDTO
/// </summary>
/// <param name="txt">The text that will show up for the user in the lookup collection</param>
/// <param name="val">The value that will be attached to the item in the lookup collection</param>
/// <remarks></remarks>
public New(string txt, string val) : base(txt, val)
{
}

below will be needed in the base class for any Entity/Domain Objects/DTO/etc

 public New(string txt, string val)
 {
     mText = txt;
     mValue = val;
 }

this base class should also implement ILookupDTO - because this will force you to make public a Text and Value property that are used by the user object in this example

 public interface ILookupDTO
 {
     string Text {
         get;
         set;
     }
     string Value {
         get;
         set;
     }
 }

the below is the def for LookupCollection used in the bind method at the top of this answer

 public class LookupCollection
 {
     private readonly IEnumerable<ILookupDTO> dtos;
     private ILookupList mList;

     public LookupCollection(IEnumerable<ILookupDTO> dtos)
     {
         this.dtos = dtos;
     }

     public void BindTo(ILookupList list)
     {
         mList = list;

         mList.Clear();

         foreach (ILookupDTO dto in dtos) {
             mList.Add(dto);
         }
     }

     public int SelectedIndex {
         get { return mList.SelectedIndex; }
         set { mList.SelectedIndex = value; }
     }

     public string SelectedValue {
         get { return mList.SelectedValue; }
         set { mList.SelectedValue = value; }
     }
 }

below is the interface for ILookupList - used in the implementation of each UI specific control (see the web and wpf examples below)

 public interface ILookupList
 {
     void Add(ILookupDTO dto);
     void Clear();
     int Count();
     int SelectedIndex {
         get;
         set;
     }
     string SelectedValue {
         get;
         set;
     }
 }

now in your code-behind you will need to do something like this in your read only property for the view

Return New WebLookupList(ddlUsers)

here is an implementation for a web specific ddl

 public class WebLookupList : ILookupList
 {

     private readonly ListControl listControl;

     public WebLookupList(ListControl listControl)
     {
         this.listControl = listControl;
     }

     public void Clear()
     {
         listControl.Items.Clear();
     }

     public void Add(Interfaces.ILookupDTO dto)
     {
         listControl.Items.Add(new ListItem(dto.Text, dto.Value));
     }

     public int Count()
     {
         return listControl.Items.Count;
     }

     public int SelectedIndex {
         get { return listControl.SelectedIndex; }
         set { listControl.SelectedIndex = value; }
     }

     public string SelectedValue {
         get { return listControl.SelectedValue; }
         set { listControl.SelectedValue = value; }
     }
 }

here is the implementation for a WPF specific ddl if using wpf your read only property for the view would look like - Return New WPFLookupList(ddlUsers)

 public class WPFLookupList : ILookupList
 {

     private readonly ComboBox combobox;

     public WPFLookupList(ComboBox combobox)
     {
         this.combobox = combobox;
     }

     public void Add(Interfaces.ILookupDTO dto)
     {
         ComboBoxItem item = new ComboBoxItem();
         item.Content = dto.Text;
         item.Tag = dto.Value;

         combobox.Items.Add(item);
     }

     public void Clear()
     {
         combobox.Items.Clear();
     }

     public int Count()
     {
         return combobox.Items.Count;
     }

     public int SelectedIndex {
         get { return combobox.SelectedIndex; }
         set { combobox.SelectedIndex = value; }
     }

     public string SelectedValue {
         get { return combobox.SelectedValue.Tag; }
         set { combobox.SelectedValue.Tag = value; }
     }
 }

You only need 1 of these at a time, but I thought showing both would help make clear what we are abstracting away

Because this was such a huge answer, I could post a sample project for download showing this in action if requested - let me know

Toran Billups
+1  A: 

The only way i can see that this could be done in .Net 2.0 would be something like this

  List<Users> userList = dal_Users.GetAllUsers();

  foreach (Users u in userList )
  {
     ListItem li = new ListItem();
     li.Text = u.ContactDetails.Telephone;
     li.Value = u.userID.ToString();
     ddl.Items.Add(li);
  }

  ddl.DataBind();
TheAlbear