代码之家  ›  专栏  ›  技术社区  ›  ShaneKm

建立MVC CMS

  •  4
  • ShaneKm  · 技术社区  · 14 年前

    最好的方法是什么?我已经有了一个MVC应用程序,我想添加/构建一个简单的功能,比如:

    1. 向该模板添加区域
    2. 通过所见即所得添加内容。

    不知道从哪里开始。

    任何信息都非常感谢。

    这是给.NET MVC的

    1 回复  |  直到 12 年前
        1
  •  10
  •   Anders Fjeldstad    14 年前

    假设您使用的是ASP.NET MVC,并且您希望保持它的简单性,那么下面的内容如何:

    public abstract class TemplateBase
    {
        public abstract string TemplateName { get; }
    }
    
    public class SingleColumnTemplate : TemplateBase
    {
        public override string TemplateName { get { return "Single-column page"; } }
        public AreaContainer CenterColumn { get; protected set; }
    
        public SingleColumnTemplate()
        {
            CenterColumn = new AreaContainer("Center column");
        }
    }
    
    public class TwoColumnTemplate : TemplateBase
    {
        public override string TemplateName { get { return "Two-column page"; } }
        public AreaContainer LeftColumn { get; protected set; }
        public AreaContainer RightColumn { get; protected set; }
    
        public TwoColumnTemplate()
        {
            LeftColumn = new AreaContainer("Left column");
            RightColumn = new AreaContainer("Right column");
        }
    }
    
    // TODO Add more template types
    
    public class AreaContainer
    {
        public string ContainerName { get; set; }
        public IList<AreaBase> Areas { get; protected set; }
    
        public AreaContainer(string name)
        {
            ContainerName = name;
            Areas = new List<AreaBase>();
        }
    }
    
    public abstract class AreaBase
    {
        public abstract string AreaName { get; }
    }
    
    public class HtmlArea : AreaBase
    {
        public override string AreaName { get { return "HTML content"; } }
        public string HtmlContent { get; set; }
    }
    
    // TODO Add more area types
    
    public class Page
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public TemplateBase Template { get; set; }
    }
    

    编辑现有页面的控制器操作可能看起来像:

    public class PageAdminController : Controller
    {
        [HttpGet]
        ActionResult Edit(int id) 
        {
            var page = GetPageFromStorageById(id);
            // TODO If the page is not found, issue 404
            return View(page);
        }
    
        // ...
    }
    

    在视野中( Views/PageAdmin/Edit.aspx ),它应该强类型为 ViewPage<Page> ,您可以使用 HtmlHelper.EditorFor(...) 方法以呈现适当的模板视图,前提是已为每个模板类型创建了部分视图:

    <!-- Inside the edit view for Page (Edit.aspx) -->
    <%: Html.HiddenFor(m => m.Id) %>
    <%: Html.EditorFor(m => m.Title) %>
    <%: Html.EditorFor(m => m.Template) %>
    

    在文件夹中 Views/PageAdmin/EditorTemplates SingleColumnTemplate.ascx , TwoColumnTemplate.ascx HtmlArea.ascx ). 您可能还希望为 AreaContainer .

    Page 具有类型的属性 TemplateBase ,这是一个抽象类 DefaultModelBinder 不知道如何填充它。您可以通过编写自定义模型绑定器来解决这个问题,该绑定器不知何故“知道”要实例化哪个实现类。它怎么会知道呢?我可以想到的一个选项是在视图中包含一个隐藏字段,该字段包含页面模板的实际运行时类型的名称。我想这有点老生常谈,但既然你追求简单,我想没问题。在这种情况下,只需包含一个名为的属性, RuntimeTypeName 模板库 班级:

    public string RuntimeTypeName { get { return GetType().FullName; } }
    

    因为它只是打电话 GetType()

    然后必须确保为 模板库 TemplateBase.RuntimeTypeName 财产。换句话说,在 单列模板.ascx 两列模板.ascx

    <%: Html.HiddenFor(m => m.RuntimeTypeName) %>
    

    使用此信息创建正确类型模板的模型活页夹可能如下所示:

    /// <summary>
    /// Model binder hack that builds upon the DefaultModelBinder, 
    /// but that can detect the "proper" subclass/implementing class 
    /// type for a model, assuming the name of that type is contained
    /// in a field called "RuntimeTypeName".
    /// </summary>
    public class InheritanceSupportingModelBinder : DefaultModelBinder
    {
        // Assume that the name of the field that contains the 
        // runtime type name is called "RuntimeTypeName"
        public const string RuntimeTypeNameField = "RuntimeTypeName";
        private Type RuntimeType { get; set; }
    
        // This method is called by the DefaultModelBinder to find out which
        // properties of the current model that it should attempt to bind
        protected override PropertyDescriptorCollection GetModelProperties(
            ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            // If we have found out the runtime type of the model through
            // looking at the "special" field above, use the properties of that type.
            // Otherwise, use the default behavior.
            if (RuntimeType != null)
            {
                return TypeDescriptor.GetProperties(RuntimeType);
            }
            else
            {
                return base.GetModelProperties(controllerContext, bindingContext);
            }
        }
    
        // This method is called by the DefaultModelBinder when it 
        // tries to create an instance of the model class. If the 
        // class is abstract, an exception will be thrown. Therefore
        // we try to read the name of the actual type from the 
        // RuntimeTypeName (hidden) field and return an instance of that type.
        protected override object CreateModel(ControllerContext controllerContext, 
                                              ModelBindingContext bindingContext, 
                                              Type modelType)
        {
            if (bindingContext.ValueProvider.ContainsPrefix(
                bindingContext.ModelName + "." + RuntimeTypeNameField))
            {
                var result = bindingContext.ValueProvider.GetValue(
                    bindingContext.ModelName + "." + RuntimeTypeNameField);
    
                if (result != null && !string.IsNullOrEmpty(result.AttemptedValue))
                {
                    // Check that the type indicated by the hidden field is really
                    // a subclass of (or implementing) the indicated base class
                    var tempType = Type.GetType(result.AttemptedValue);
                    if (modelType.IsAssignableFrom(tempType))
                    {
                        RuntimeType = modelType = tempType;
                    }
                }
            }
            return base.CreateModel(controllerContext, bindingContext, modelType);
        }
    }
    

    免责声明: 我自己也是ASP.NET MVC的初学者,所以这个模型活页夹很可能有问题。我通过查看 通过反复试验。这只是一个例子,但根据我(快速和肮脏)的测试,它似乎是可行的。

    当然,您需要在Global.asax中注册它才能启动:

    ModelBinders.Binders.Add(
        typeof(TemplateBase), 
        new InheritanceSupportingModelBinder());
    

    但我们还没结束!记住 AreaContainer.Areas IList<AreaBase> -从那以后 AreaBase 也是一个抽象类,我们必须应用相同的技巧才能正确绑定它。也就是说,添加 财产 面积基准 面积基准 Global.asax .

    如果我们已经遵循了所有这些步骤,我们可以在 PageAdminController

    [HttpPost]
    public ActionResult Edit(Page page)
    {
        if (!ModelState.IsValid)
        {
            return View(page);
        }
        // TODO Save page to database or whatever
        // TODO Redirect to page index
    }
    

    创建一个新页面的操作方法只是一个练习,应该没那么难(用户从列表中选择模板,显示正确的表单,像上面这样的后期处理操作)。

    HtmlHelper.DisplayFor(...) 而不是 EditorFor(...) ,创建相应的局部视图,然后设置。

    对于所见即所得的内容编辑,您可能希望使用第三方组件。 CKEditor TinyMCE , YUI Rich Text Editor Telerik Editor 是一些例子。