我有一个结构如下的 MainViewModel 类:
public class MainViewModel
{
...some properties
private ObservableCollection<Strike> m_strikes;
public ObservableCollection<Strike> Strikes
{
get
{
if (this.m_strikes == null)
this.m_strikes = new ObservableCollection<Strike>();
return this.m_strikes;
}
}
}
public class Strike
{
private ObservableCollection<Reivindication> m_reivindications;
public ObservableCollection<Reivindication> Reivindications
{
get { return m_reivindications; }
set
{
m_reivindications = value;
this.OnPropertyChanged("Reivindications");
}
}
private ObservableCollection<StrikeDate> m_stikeDates;
public ObservableCollection<StrikeDate> StrikeDates
{
get { return m_stikeDates; }
set
{
m_stikeDates = value;
this.OnPropertyChanged("StrikeDates");
}
}
}
Reivindication 和 StrikeDates 类有一些与问题无关的属性。
我有一个呈现 MainViewModel 模型的局部视图:
@model Models.MainViewModel
<link rel="Stylesheet" href="../../Content/Site.css" type="text/css" />
<div class="content-wrapper">
<img src="~/Images/someimage.png" height="300" style="position:absolute" />
<section id="loginForm" style="position:relative;width:685px;height:600px;">
@using (Html.BeginForm("Save", "Home", FormMethod.Post, new { ReturnUrl = ViewBag.ReturnUrl }))
{
@Html.HiddenFor(m => m.Id)
<div style="position:relative;top:270px;" id="strikesDiV">
@Ajax.ActionLink("Add Strike...","AddStrike","Home",
new { containerPrefix = this.ViewData["ContainerPrefix"], @class="addStrikeItem"},
new AjaxOptions { InsertionMode = InsertionMode.InsertAfter,HttpMethod = "POST", UpdateTargetId = "strikesDiV" },
null)
@foreach (Models.Strike strike in Model.Strikes)
{
Html.RenderPartial("_StrikePartial", strike);
}
</div>
<input type="submit" class="k-button" value="Save" style="position:absolute; left:620px;top:0px;" />
}
</section>
</div>
我的 _StrikePartial 视图有:
@using Report.Helpers
@model Models.Strike
<link rel="Stylesheet" href="../../Content/Site.css" type="text/css" />
<div class="editorRow" style="border-style:solid;border-width:1px;min-height:100px;overflow:auto;">
@using(Html.BeginCollectionItem("Strikes"))
{
<text>Identification</text> @Html.TextBoxFor(m => m.Identification, new {data_containerPrefix=this.ViewData["ContainerPrefix"]}) <a href="#" class="deleteRow">delete</a>
<div style="top:10px;" id="reivindicationsDiV">
@Ajax.ActionLink("Add Reivindication...", "AddReivindication", "Home",
new { containerPrefix = this.ViewData["ContainerPrefix"], @class="addReivindicationItem"},
new AjaxOptions { InsertionMode = InsertionMode.InsertAfter, HttpMethod = "POST", UpdateTargetId = "reivindicationsDiV"},
new { style = "float:right" })
@foreach (Models.Reivindication reivindication in Model.Reivindications)
{
Html.RenderPartial("_ReivindicationPartial", reivindication);
}
</div>
<div style="top:10px;" id="strikeDatesDiV">
@Ajax.ActionLink("Add strike date...","AddStrikeDate","Home",
new { containerPrefix = this.ViewData["ContainerPrefix"], @class="addStrikeDateItem"},
new AjaxOptions { InsertionMode = InsertionMode.InsertAfter,HttpMethod = "POST", UpdateTargetId = "strikeDatesDiV" },
new { style="float:right" })
@foreach (Models.StrikeDate strikeDate in Model.StrikeDates)
{
Html.RenderPartial("_StrikeDatePartial", strikeDate);
}
</div>
}
</div>
<script>
$("a.deleteRow").live("click", function () {
$(this).parents("div.editorRow:first").remove();
return false;
});
</script>
和部分之一(_ReivindicationPartial):
@using Report.Helpers
@model Models.Reivindication
<link rel="Stylesheet" href="../../Content/Site.css" type="text/css" />
<div class="reivindicationEditorRow">
@using (Html.BeginCollectionItem("Reivindications"))
{
<text>Reivindication code</text> @Html.TextBoxFor(m => m.Code, new { style="width:150px", data_containerPrefix=this.ViewData["ContainerPrefix"]})
<text>Result</text> @Html.TextBoxFor(m => m.Outcome, new { style="width:150px" , data_containerPrefix=this.ViewData["ContainerPrefix"]})
<a href="#" class="deleteRow">delete</a>
}
</div>
<script>
$("a.deleteRow").live("click", function () {
$(this).parents("div.reivindicationEditorRow:first").remove();
return false;
});
</script>
我有这个助手类:
public static class HtmlPrefixScopeExtensions
{
private const string idsToReuseKey = "__htmlPrefixScopeExtensions_IdsToReuse_";
public static IDisposable BeginCollectionItem(this HtmlHelper html, string collectionName)
{
if (html.ViewData["ContainerPrefix"] != null)
{
collectionName = string.Concat(html.ViewData["ContainerPrefix"], ".", collectionName);
}
var idsToReuse = GetIdsToReuse(html.ViewContext.HttpContext, collectionName);
string itemIndex = idsToReuse.Count > 0 ? idsToReuse.Dequeue() : Guid.NewGuid().ToString();
var htmlFieldPrefix = string.Format("{0}[{1}]", collectionName, itemIndex);
html.ViewData["ContainerPrefix"] = htmlFieldPrefix;
// autocomplete="off" is needed to work around a very annoying Chrome behaviour whereby it reuses old values after the user clicks "Back", which causes the xyz.index and xyz[...] values to get out of sync.
html.ViewContext.Writer.WriteLine(string.Format("<input type=\"hidden\" name=\"{0}.index\" autocomplete=\"off\" value=\"{1}\" />", collectionName, html.Encode(itemIndex)));
return BeginHtmlFieldPrefixScope(html, htmlFieldPrefix);
}
public static IDisposable BeginHtmlFieldPrefixScope(this HtmlHelper html, string htmlFieldPrefix)
{
return new HtmlFieldPrefixScope(html.ViewData.TemplateInfo, htmlFieldPrefix);
}
private static Queue<string> GetIdsToReuse(HttpContextBase httpContext, string collectionName)
{
// We need to use the same sequence of IDs following a server-side validation failure,
// otherwise the framework won't render the validation error messages next to each item.
string key = idsToReuseKey + collectionName;
var queue = (Queue<string>)httpContext.Items[key];
if (queue == null)
{
httpContext.Items[key] = queue = new Queue<string>();
var previouslyUsedIds = httpContext.Request[collectionName + ".index"];
if (!string.IsNullOrEmpty(previouslyUsedIds))
foreach (string previouslyUsedId in previouslyUsedIds.Split(','))
queue.Enqueue(previouslyUsedId);
}
return queue;
}
private class HtmlFieldPrefixScope : IDisposable
{
private readonly TemplateInfo templateInfo;
private readonly string previousHtmlFieldPrefix;
public HtmlFieldPrefixScope(TemplateInfo templateInfo, string htmlFieldPrefix)
{
this.templateInfo = templateInfo;
previousHtmlFieldPrefix = templateInfo.HtmlFieldPrefix;
templateInfo.HtmlFieldPrefix = htmlFieldPrefix;
}
public void Dispose()
{
templateInfo.HtmlFieldPrefix = previousHtmlFieldPrefix;
}
}
}
在我的 Home 控制器中,我添加了一个这样的项目:
[HttpPost]
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
public PartialViewResult AddStrike(string containerPrefix)
{
ViewData["ContainerPrefix"] = containerPrefix;
return PartialView("_StrikePartial", new Strike());
}
[HttpPost]
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
public PartialViewResult AddReivindication(string containerPrefix)
{
ViewData["ContainerPrefix"] = containerPrefix;
return PartialView("_ReivindicationPartial", new Reivindication());
}
[HttpPost]
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
public PartialViewResult AddStrikeDate(string containerPrefix)
{
ViewData["ContainerPrefix"] = containerPrefix;
return PartialView("_StrikeDatePartial", new StrikeDate());
}
如果我添加第一个 Strike 并在罢工内部添加一个 reivindication 它呈现 OK。如果我添加第二次罢工并在第二次罢工中添加重新证明,则页面会在第一次罢工中呈现新的重新证明。但是,如果我单击保存,则 MainViewModel 是正确的。在调试中,Strike[1].Reivindications[0] 看起来是正确的。只有渲染是错误的。这是渲染页面:
div id="reivindicationsDiV" style="top: 10px;">
Principais reivindicações expressas e resultados obtidos
<a style="float: right;" href="/Report/Home/AddReivindication?containerPrefix=Strikes%5Bd3fc7e4a-0571-40cb-9b2d-b4383b22eae8%5D&class=addReivindicationItem" data-ajax-update="#reivindicationsDiV" data-ajax-mode="after" data-ajax-method="POST" data-ajax="true">Adicionar Reivindicação...</a>
<link href="../../Content/Site.css" rel="Stylesheet" type="text/css">
<div class="reivindicationEditorRow">
<input name="Strikes[d3fc7e4a-0571-40cb-9b2d-b4383b22eae8].Reivindications.index" type="hidden" value="dd1716b2-a832-4f2a-8b99-f230b20df2b5" autocomplete="off">
Reivindicação <input name="Strikes[d3fc7e4a-0571-40cb-9b2d-b4383b22eae8].Reivindications[dd1716b2-a832-4f2a-8b99-f230b20df2b5].Code" id="Strikes_d3fc7e4a-0571-40cb-9b2d-b4383b22eae8__Reivindications_dd1716b2-a832-4f2a-8b99-f230b20df2b5__Code" style="width: 150px;" type="text" value="11" data-val-number="The field Code must be a number." data-val="true" data-containerprefix="Strikes[d3fc7e4a-0571-40cb-9b2d-b4383b22eae8].Reivindications[dd1716b2-a832-4f2a-8b99-f230b20df2b5]"> Resultado <input name="Strikes[d3fc7e4a-0571-40cb-9b2d-b4383b22eae8].Reivindications[dd1716b2-a832-4f2a-8b99-f230b20df2b5].Outcome" id="Strikes_d3fc7e4a-0571-40cb-9b2d-b4383b22eae8__Reivindications_dd1716b2-a832-4f2a-8b99-f230b20df2b5__Outcome" style="width: 150px;" type="text" value="12" data-val-number="The field Outcome must be a number." data-val="true" data-containerprefix="Strikes[d3fc7e4a-0571-40cb-9b2d-b4383b22eae8].Reivindications[dd1716b2-a832-4f2a-8b99-f230b20df2b5]"> <a class="deleteRow" href="#">delete</a>
</div>
<link href="../../Content/Site.css" rel="Stylesheet" type="text/css">
<div class="reivindicationEditorRow">
<input name="Strikes[a3c19f26-f4ed-47c9-9870-d6b2d3bc0adc].Reivindications.index" type="hidden" value="4e3d2042-dce9-4143-af69-ab816a69297b" autocomplete="off">
Reivindicação <input name="Strikes[a3c19f26-f4ed-47c9-9870-d6b2d3bc0adc].Reivindications[4e3d2042-dce9-4143-af69-ab816a69297b].Code" id="Strikes_a3c19f26-f4ed-47c9-9870-d6b2d3bc0adc__Reivindications_4e3d2042-dce9-4143-af69-ab816a69297b__Code" style="width: 150px;" type="text" value="" data-val-number="The field Code must be a number." data-val="true" data-containerprefix="Strikes[a3c19f26-f4ed-47c9-9870-d6b2d3bc0adc].Reivindications[4e3d2042-dce9-4143-af69-ab816a69297b]"> Resultado <input name="Strikes[a3c19f26-f4ed-47c9-9870-d6b2d3bc0adc].Reivindications[4e3d2042-dce9-4143-af69-ab816a69297b].Outcome" id="Strikes_a3c19f26-f4ed-47c9-9870-d6b2d3bc0adc__Reivindications_4e3d2042-dce9-4143-af69-ab816a69297b__Outcome" style="width: 150px;" type="text" value="" data-val-number="The field Outcome must be a number." data-val="true" data-containerprefix="Strikes[a3c19f26-f4ed-47c9-9870-d6b2d3bc0adc].Reivindications[4e3d2042-dce9-4143-af69-ab816a69297b]"> <a class="deleteRow" href="#">delete</a>
</div>
</div>
Id 似乎是正确的,但第二次重新证明应该在第二次罢工日期 DiV 中呈现(此处未显示)这让我发疯,欢迎任何帮助提前谢谢