我希望有一种方法可以让 View 以通用方式关注来自 Controller 的特定模型属性。
我目前拥有的是:
Controller :
// To become an extension/base class method
private void FocusOnField<TModel, TProperty>(Expression<Func<TModel, TProperty>> fieldExpression)
{
ViewData["FieldToFocus"] = fieldExpression;
}
...
FocusOnField((ConcreteModelClass m) => m.MyProperty);
查看
public static class ViewPageExtensions
{
public static MvcHtmlString FocusScript<TModel>(this ViewPage<TModel> viewPage)
{
if (viewPage.ViewData["FieldToFocus"] != null)
{
return MvcHtmlString.Create(
@"<script type=""text/javascript"" language=""javascript"">
$(document).ready(function() {
setTimeout(function() {
$('#" + viewPage.Html.IdFor((System.Linq.Expressions.Expression<Func<TModel, object>>)viewPage.ViewData["FieldToFocus"]) + @"').focus();
}, 500);
});
</script>");
}
else
{
return MvcHtmlString.Empty;
}
}
}
我现在面临的问题是,在 View 的 FocusScript
中方法 我不知道要关注的属性的返回类型,并转换为 (System.Linq.Expressions.Expression<Func<TModel, object>>)
任何不返回对象的属性都会失败。
我不能只为属性添加第二个通用参数,因为我不知道 Controller 希望我关注的属性的返回类型是什么。
如何编写我的 View 扩展方法 FocusScript
以一种通用的方式,以便它可以与不同返回类型的属性一起使用?
为什么会出现问题?
我知道我可以在 Controller 中传递我想要关注的控件的 ID,然后让 javascript 读取该 ID,找到该控件并关注它。但是,我不喜欢在 Controller 中硬编码属于 View 的内容(控件的 ID)。我想告诉方法我想要什么属性,它应该知道要使用的 Id,就像 View 通常获取/创建控件的 Id 一样。
假设我有一个模型:
class MyModel
{
public int IntProperty { get; set; }
public string StringProperty { get; set; }
}
在 Controller 的不同地方我想关注一个不同的领域:
FocusOnField((MyModel m) => m.IntProperty);
...
FocusOnField((MyModel m) => m.StringProperty);
现在,在第一种情况下,表达式是一个返回整数的函数,在第二种情况下,它返回一个字符串。结果,我不知道将我的 ViewData["FieldToFocus"] 转换到什么(将其传递给 IdFor<>()
),因为它因属性而异..
最佳答案
我想我已经找到了解决您问题的方法 - 它适用于我的环境,但我不得不猜测您的代码可能看起来如何。
public static class ViewPageExtensions
{
public static MvcHtmlString GetIdFor<TViewModel, TProperty>(ViewPage<TViewModel> viewPage, Expression<Func<TViewModel, TProperty>> expression)
{
return viewPage.Html.IdFor(expression);
}
public static MvcHtmlString FocusScript<TViewModel>(this ViewPage<TViewModel> viewPage)
{
if (viewPage.ViewData["FieldToFocus"] == null)
return MvcHtmlString.Empty;
object expression = viewPage.ViewData["FieldToFocus"];
Type expressionType = expression.GetType(); // expressionType = Expression<Func<TViewModel, TProperty>>
Type functionType = expressionType.GetGenericArguments()[0]; // functionType = Func<TViewModel, TProperty>
Type[] functionGenericArguments = functionType.GetGenericArguments(); // functionGenericArguments = [TViewModel, TProperty]
System.Reflection.MethodInfo method = typeof(ViewPageExtensions).GetMethod("GetIdFor").MakeGenericMethod(functionGenericArguments); // method = GetIdFor<TViewModel, TProperty>
MvcHtmlString id = (MvcHtmlString)method.Invoke(null, new[] { viewPage, expression }); // Call GetIdFor<TViewModel, TProperty>(viewPage, expression);
return MvcHtmlString.Create(
@"<script type=""text/javascript"" language=""javascript"">
$(document).ready(function() {
setTimeout(function() {
$('#" + id + @"').focus();
}, 500);
});
</script>");
}
}
也许有更优雅的方法,但我认为它归结为您试图将对象(即 ViewData["FieldToFocus"]
的返回类型)转换为正确的表达式树 Expression<Func<TViewModel, TProperty>>
但正如您所说,您不知道 TProperty 应该是什么。
另外,为了完成这项工作,我必须向 GetIdFor
添加另一个静态方法。因为目前我不太确定如何调用扩展方法。它只是一个包装器来调用 IdFor
扩展方法。
你可以让它不那么冗长(但可能不那么可读)。
object expression = viewPage.ViewData["FieldToFocus"];
MethodInfo method = typeof(ViewPageExtensions).GetMethod("GetIdFor")
.MakeGenericMethod(expression.GetType().GetGenericArguments()[0].GetGenericArguments());
MvcHtmlString id = (MvcHtmlString)method.Invoke(null, new[] { viewPage, expression });
最后一个想法,HtmlHelper.IdFor
的输出不同于ExpressionHelper.GetExpressionText
? IdFor我不是很懂,不知道它会不会一直给你一个匹配属性名的字符串。
ViewData["FieldToFocus"] = ExpressionHelper.GetExpressionText(fieldExpression);
关于c# - 将模型属性表达式传递给 View ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11450774/