-1

I want to drive a RadioButtonLists SelectedValue property from a property in my datasource, but I'm not managing to make it work.

I have a <asp:RadioButtonList on my page bound to an <asp:ObjectDataSource. This datasource in turn provides the following model class:

public class CollectionWithDefault : Collection<string>
{
    CollectionWithDefault(IEnumerable<string> items, string defaultItem)
    {
        foreach (var item in items)
            Add(item);

        DefaultItem = defaultItem;
    }

    public string DefaultItem { get; }
}

Notice that this class is a standard collection of strings that also exposes which one of them is the default option.

Consider that I have the following implementation for a value provider. This is a simple in-memory implementation, but keep in mind that this could be coming from a database or any other source:

public static class ItemProvider
{
    public static CollectionWithDefault GetAvailableItems()
    {
        var items = new [] { "option1", "option2", "option3" };

        return new CollectionWithDefault(items, items[1]);
    }
}

I tried the following:

<asp:ObjectDataSource runat="server"
    ID="ItemSource"
    TypeName="MyNamespace.ItemProvider"
    SelectMethod="GetAvailableItems" />
<asp:RadioButtonList runat="server" 
    DataSourceID="ItemSource"
    SelectedValue='<%# Eval("DefaultItem") #>' />

I'm getting the following exception in the Eval call:

System.InvalidOperationException: 'Databinding methods such as Eval(), XPath(), and Bind() can only be used in the context of a databound control.'

How can I ensure that the correct radio is preselected based on the field coming from my datasource?

Changing the collection model itself to make it work is acceptable, but I can't set the SelectedValue from codebehind. I wanted to rely on the datasource control to do the heavy lifting.

julealgon
  • 7,072
  • 3
  • 32
  • 77
  • Pretty sure you'll have to do it with code behind, setting the selected index or selected value. `SelectedValue` is not a valid attribute of the `asp:RadioButtonList` control. – wazz Jun 22 '18 at 19:41
  • @wazz `SelectedValue` is definitely valid, it just doesn't show in intellisense. – julealgon Jun 22 '18 at 20:10
  • I get an error: `The 'SelectedValue' property cannot be set declaratively.` – wazz Jun 22 '18 at 20:16
  • Can you share a piece of code where you are seeing this @wazz? I've never seen this error message before, and I just double checked my real production code and I can assure you `RadioButtonList` has a `SelectedValue` and that you should be able to declaratively set it. – julealgon Jun 22 '18 at 20:45
  • I'm talking about in the markup, on the control. ``. (Plus listitems and closing tag.) – wazz Jun 22 '18 at 20:58
  • It appears that you get this error when trying to set a value directly @wazz. Check [here](https://stackoverflow.com/questions/580487/aspdropdownlist-in-itemtemplate-why-is-selectedvalue-attribute-allowed)]. This is not what I'm doing, since I need the value to be databound to the source using a databinding expression. – julealgon Jun 22 '18 at 21:25
  • I think there's a difference. The example in the link is in an `Edit Template` and there is an existing value to be bound -- you are now editing, what is the initial value to set? As though editing a table-row. When editing, values have to be set (bound) for you to edit. But you are saying, 'here's a collection of items for the list, now I want to make *this* one the selected one.' You are declaring it; it's not bound in any way. Maybe there's a way, I'm not 100% sure. – wazz Jun 23 '18 at 00:00
  • Have you tried `Bind` instead of `Eval`? – wazz Jun 23 '18 at 00:06
  • Yes, `Bind()` throws the exact same exception @wazz – julealgon Jun 23 '18 at 02:15
  • 1
    @wazz, FYI I found a way out of this and added as an answer. – julealgon Jun 28 '18 at 19:52

1 Answers1

1

I managed to make this work seamlessly without requiring manual assignments in codebehind by extending the original RadioButtonList control and modifying the core databinding method to honor ListItem objects.

It goes something like this:

public class MyRadioButtonList : RadioButtonList
{
    protected override void PerformDataBinding(IEnumerable dataSource)
    {
        if (dataSource is IEnumerable<ListItem> listItems)
        {
            ...

            foreach (var listItem in listItems)
                Items.Add(listItem);

            ...
        }
        else
        {
            base.PerformDataBinding(dataSource);
        }
    }    
}

With this in place, it was just a matter of converting my source model into a IEnumerable<ListItem> on the presentation layer (easy to accomplish with an adapter/proxy implementation) and then feed these ListItems to the control.

Once I got this in place, I could see my selected items reflected correctly in the UI based on the datasource field. Considering how trivial the extension is, I feel it was quite worth it :)

The same inheritance approach can probably be used for similar controls like CheckBoxList, which suffers from the very same limitation.

For the more adventurous folks, one could also make this work by introducing extra DataSelectedField and DataEnabledField properties in the control and using Eval on top of them as part of the original databinding algorithm (which already does this with DataTextField and DataValueField). I felt this would be a little bit more involved for my use case and decided to go with a simpler override, but it is definitely a valid approach that could even live along my proposed solution for an even more robust RadioButtonList.

julealgon
  • 7,072
  • 3
  • 32
  • 77