0

I am writing a custom drop down list. I want to make it reusable (obviously), so I have a method PopulateList. I can populate list in two ways though. First, is passing a list or an array of strings to PopulateList. But I almost never want to select a string, right? Most of the time it's some complex objects. For example, say I have a class Dog with two properties, Name and Breed. I want to show a drop down list with the names of the dogs. I can do listOfDogs.Select(x => x.Name).ToList() and there I have it, a list of dogs that I can pass to PopulateList. Now, since those are strings and my dropdown doesn't know about the objects themselves, when it will raise ItemSelected event, it won't be able to say which object was selected, but rather which string, and I will have to do the mapping on the handling side. And that's okay, but that mapping part is kinda ugly.

The second way is doing this:

void PopulateList<T>(List<T> list, Func<T, string> selector )
{
    var stringValues = list.Select(selector).ToList();
    ...
}

and using it like this:

var list = new List<Dog>
{
    new Dog {Breed = "German Shepard", Name = "Nancy"},
    new Dog {Breed = "Collie", Name = "Stacy"} // weird name choices, I know
};

PopulateList(list, dog => dog.Name);

This way, dropdown won't care what kind of objects it is dealing with and will be able to know which object was selected and I will be able to beautifully wrap it in EventArgs and pass it to the event handler.

My question is, what are the advantages of using the second approach? Is it unnecessary complexity?

EDIT:

I ended up implementing it like this:

async Task<T> PopupSelect<T>(string title, List<T> objects, Func<T, string> selector, bool hasDefault, T defaultObject = default(T))

It is very easy to use. For example:

_button.Clicked += async delegate
{
    var list = new List<Tuple<string, string>>()
    {
        Tuple.Create("asdf", "Dog"),
        Tuple.Create("a", "Cat"),
        Tuple.Create("d", "Mouse"),
        Tuple.Create("e", "Rat"),
        Tuple.Create("r", "Hamster"),
        Tuple.Create("2", "Elephant"),
        Tuple.Create("344", "Lion"),
        Tuple.Create("ascv", "Tiger"),
        Tuple.Create("vv", "Buffalo"),
    };

    var selected = await PopupSelect("Select a pet", list, s => s, true, list[5]);

    if (selected == null)
        Debug.WriteLine("Sorry, no value was selected");
    else
        Debug.WriteLine(selected.Item2);
  • A better example of a selectable object is new dog { id = "12345", name="nancy" } – Robert Harvey Sep 20 '16 at 6:29
  • this was just out of head, for the sake of example, doesn't really matter – nicks Sep 20 '16 at 6:30
  • It does matter. "Breed" is not unique. – Robert Harvey Sep 20 '16 at 6:31
  • support binding and access to the object via the selectedvaluepath – paparazzo Sep 24 '16 at 12:30
1

Generally, I found it very useful to put objects in dropdown lists. It provided me with convenient flexibility at times. Here is a useful situation I had: I needed to have a dropdown list of months in a year, ordered chronologically. However, I wanted to have a list of names of months, and I also needed to have my list localized, meaning that the names of the months would be in different languages, depending on the selected language setting. I therefore implemented a class DropDownMonth (or something to that effect) that contained the ordinal number of the month (for sorting and for quick retrieval of the integer I needed for later creation of some dates), and an override of ToString method that returned the name of the month in the desired language. I then just placed objects of that class in the dropdown and the problem was solved.

The alternative would be to push the strings in the dropdwon and then get the number of a month by selected index of the list, which just did not seem like an elegant solution to me.

To return to your question, while interesting, I believe that the second approach you listed is unnecessarily complex and that you would achieve exactly what you wanted by placing objects with overriden ToString method in the dropdown.

The only situation where your second approach would be very elegant is if you have a list of objects that you want put in several dropdowns with each dropdown showing a different property of an object. In that case, I would say that the second approach would be a way to go.

1

I would write both, frankly. You can define the same function with two prototypes. To keep DRY, the object-based one can call the string-based one internally. Developers working on your main code base should have the choice of using whatever is more convenient in their situation.

void PopulateList<T>(List<T> list, Func<T, string> selector )
{
    var stringValues = list.Select(selector).ToList();
    PopulateList(stringValues);
}

void PopulateList(List<String> list)
{
    //Populate the list here
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.