2

我正在使用动态创建的问题和选项在 asp.net 中进行测验控件。主控件基本上是一个容纳所有问题的容器。在设计视图中,用户可以通过自定义集合编辑器添加问题。每次我向集合编辑器列表添加问题时,它都会为我生成一个问题标签。每个问题对象内部都有一个标签和n 个继承 Radiobutton 控件的 Option 对象。这些 Option 对象中的每一个依次代表用户可以为每个问题选择的选项。

这一切都有效,除了我现在在我希望能够读取每个单选按钮的 Checked 值的部分。当我想在一个页面中实现这个测验并检查我想在这个页面中放置一个按钮并调用控件内部的以下函数的问题时:

$

    public String checkQuestions()
    {


        if (questions != null)
        {

            foreach (Question question in questions)
            {

                options = question.readOptions();

                int i = 0;
                foreach (Option option in options)
                {
                    testLabel.Text = option.Checked.ToString(); // test purposes only
                }

            }



        }
       return errors;

    }

但是,一旦我选择了一个单选按钮并单击提交按钮,所有选项的 Checked 值将始终变为 false。基本上它在回发后失去了它的检查值,我只是试图解决它。如果有人能指出我正确的方向,将不胜感激。

4

1 回答 1

4

乍一看,我要检查两件事。首先,确保您正在实施IPostBackDataHandler. 这需要您实现两种方法,LoadPostData并且RaisePostDataChangedEvent. 在我的第一个猜测中,第一个可能是您问题的根源。

手动处理回发

LoadPostData接受一个字符串postDataKey和 a并返回一个指示值是否由于回发而改变的值。您不需要按照.Net 最初打算的方式实现这一点,例如,我创建了一个包含多个单选按钮的控件(由于此处不重要的原因,不能简单地成为控件),因此确保它们是全部由一个属性命名并检查:NameValueCollection postCollectionboolRadioButtonListstring GroupNamepostCollectionGroupName

public bool LoadPostData(string postDataKey,
    System.Collections.Specialized.NameValueCollection postCollection)
{
    bool oldValue = _isChecked;

    postCollection = HttpContext.Current.Request.Form; // See note below

    _isChecked = (postCollection[this.GroupName] == this.Text);

    return oldValue == _isChecked;
}

你会注意到我正在重新定义postCollection这里;这是因为postCollection只包含HttpRequest.Form与 ASP.Net 认为您的控件应该关心的内容相对应的子集。由于您也在此处构建复合控件,因此您可能也想做同样的事情。

如果这在第一次不起作用,请不要担心;值得在调试模式下逐步检查传递给此方法的内容(或将内容输出到HttpContext.Trace.

快速警告

最后一件事:LoadPostData仅当发布的表单包含名称与UniqueID您的控件匹配的字段时才调用。由于您的控件是一个复合控件,您可能需要稍微牛仔一下,如下所示:

protected override void Render(HtmlTextWriter writer)
{
    base.Render(writer);

    writer.WriteBeginTag("input");
    writer.WriteAttribute("type", "hidden");
    writer.WriteAttribute("name", this.UniqueID);
    writer.WriteAttribute("value", "post");
    writer.Write(" />");
}

这是一个肮脏的黑客,但它会工作;o)

手动处理视图状态

如果手动处理回发不能解决您的问题,则可能是您需要弄乱控件的视图状态。别担心,只要您遵循一些简单的规则,这远没有看起来那么可怕。

要手动处理您的视图状态,您只需要重写两个调用的方法,显然足够了,LoadViewState并且SaveViewState. 第一个需要一个object视图状态来膨胀,另一个返回相同的object结构。如果你让你的SaveViewState覆盖返回包含你需要保存所有需要持久的重要属性的结构的东西,那么你只需在你的LoadViewState方法中再次膨胀它。

这是第一个狡猾的技巧出现的地方。您应该使用某些数据类型来保存视图状态,并且您永远不应该使用任何其他类型(因为其他类型的存储效率非常低)。可能对您最有用的类型是System.Web.UI.PairSystem.Web.UI.Triplet以及我们的老朋友System.Collections.ArrayListSystem.Collections.Hashtable。Pairs 和 Triplets 只存储两个或三个 type 值object;ArrayLists 实际上是一个List<object>.

我猜想,在您的情况下,您可能希望存储(1)布尔标志的 ArrayList,存储单选按钮的“检查性”或(2)字符串或整数的 ArrayList,存储 ID 或索引选中的单选按钮。

在我之前提到的控件中,我只需要存储检查和Text属性,所以我的LoadViewStateSaveViewState方法看起来像这样:

protected override void LoadViewState(object savedState)
{
    Pair state = savedState as Pair;

    if (state != null)
    {
        _isChecked = state.First as Nullable<bool> ?? false;
        this.Text = state.Second as string;
    }
}


protected override object SaveViewState()
{
    return new Pair(_isChecked, this.Text);
}

同样,如果这在第一次不起作用,您几乎肯定想要单步执行代码或将东西扔到 Trace 中。重要的是,您可能希望避免从这些方法中抛出异常,以防您的视图状态损坏或不存在或其他情况。

进一步阅读视图状态

当我弄乱视图状态时,我会为一些非常有用的文章添加书签。第一个解释了为什么您应该只在视图状态中存储某些类型(例如使用ArrayListand Hashtable,而不是List<T>and Dictionary<TKey, TValue>),第二个很好地深入解释了所有这些视图状态的实际工作原理。

  1. 不要让 BinaryFormatter 得到它!
  2. 真正理解 ViewState

我希望所有这些都有助于解决您的问题。

于 2010-10-05T16:36:00.150 回答