动态地向ASP.NET转发器添加行以响应事件

时间:2014-12-08 15:04:29

标签: c# asp.net repeater

我正在尝试创建ASP.NET服务器控件(纯代码,没有ascx模板 - 因为控件必须完全包含在.dll中,并且它不能依赖于外部.ascx文件),并且我在动态添加时遇到问题转发的项目。

我想将项目添加到转发器以响应SelectedIndexChanged事件,但是当我在该事件中执行第二个DataBind()时,我将丢失ViewModel中的数据(例如,文本框包含默认数据而不是用户输入的文本)。 / p>

我的代码的简化版本(大部分借用了MS复合控件示例 - http://msdn.microsoft.com/en-us/library/3257x3ea%28v=vs.100%29.aspx):

[ToolboxData("<{0}:FilterControl runat=server />")]
public class FilterControl : CompositeControl, IPostBackDataHandler
{
    private List<FilteringProperty> elements = new List<FilteringProperty>();
    private DropDownList filteringElementsDropDownList;
    private Repeater usedFiltersRepeater;

    [Bindable(true), DefaultValue(null), Description("Active filters")]
    public List<FilteringProperty> UsedElements
    {
        get
        {
            EnsureChildControls();
            if (ViewState["UsedElements"] == null)
            {
                ViewState["UsedElements"] = new List<FilteringProperty>();
            }
            return (List<FilteringProperty>)ViewState["UsedElements"];
        }
        set
        {
            EnsureChildControls();
            ViewState["UsedElements"] = value;
        }
    }

    protected override void RecreateChildControls()
    {
        EnsureChildControls();
    }

    protected override void CreateChildControls()
    {
        Controls.Clear();

        filteringElementsDropDownList = new DropDownList { AutoPostBack = true };
        usedFiltersRepeater = new Repeater();

        foreach (var element in elements)
        {
            filteringElementsDropDownList.Items.Add(new ListItem(element.DisplayName));
        }

        filteringElementsDropDownList.SelectedIndexChanged += (sender, e) =>
        {
            string selectedText = filteringElementsDropDownList.SelectedValue;
            FilteringProperty condition = elements.First(x => x.DisplayName == selectedText);

            var toRemove = filteringElementsDropDownList.Items.Cast<ListItem>().FirstOrDefault(x => x.Text == condition.DisplayName);
            if (toRemove != null)
            {
                filteringElementsDropDownList.Items.Remove(toRemove);
            }

            UsedElements.Add(condition);
            // ======> A <========
        };

        usedFiltersRepeater.ItemDataBound += (sender, args) =>
        {
            FilteringProperty dataItem = (FilteringProperty)args.Item.DataItem;

            Control template = args.Item.Controls[0];
            TextBox control = (TextBox)template.FindControl("conditionControl");
            control.Text = dataItem.DisplayName;
            // ======> C <========
        };

        usedFiltersRepeater.ItemTemplate = // item template
        usedFiltersRepeater.DataSource = UsedElements;
        usedFiltersRepeater.DataBind();
        // ======> B <========

        Controls.Add(filteringElementsDropDownList);
        Controls.Add(usedFiltersRepeater);
    }
}

我用(A),(B)和(C)

标记了代码的重要部分

问题是,(A)在DataBinding(B和C)之后执行,因此在下一次回发之前,UsedElements中的更改不可见。

可以在(A)之后添加usedFiltersRepeater.DataBind();,但是重新创建所有控件而没有来自viewstate的数据(即空)

有没有办法在数据绑定后动态更改转发器,以便保留包含控件的数据?

Tl; dr - 我有一个DropDownList,我想在SelectedIndexChanged上向Repeater添加可编辑的项目(不会丢失viewstate)。

1 个答案:

答案 0 :(得分:0)

我终于解决了我的问题。 我的解决方案相当脏,但似乎工作正常。

而不是简单的数据绑定:

  • 我从转发器中的所有控件获取状态并将其保存在临时变量中(每个控件的状态包括所有内容,例如下拉列表的选定索引)使用我的函数GetState()
  • 以任何我想要的方式修改此状态
  • 使用我的函数SetState()
  • 恢复完整状态

例如:

FilterState state = GetState();
state.Conditions.Add(new ConditionState { Item = condition });
SetState(state);