在 DotVVM 的自定义标记控件中呈现 ITemplate

Rendering an ITemplate in custom markup control in DotVVM

我正在尝试创建一个自定义控件来呈现一个可以在其中选择日期的日历。我需要能够将 ITemplate 传递给每天都会呈现的控件。日子由 Repeater 渲染。

由于目前没有允许绑定到 ITemplate 的现有控件(或将 ITemplate 与集合以外的任何内容一起使用),我如何从中呈现 ITemplate controlProperty容易吗? 我更希望有某种只呈现 ITemplate 的控件,这样它就可以在其他地方重复使用。

部分控制标记:

    <!-- ... -->
    <dot:Repeater ID="DaysRepeater" DataSource="{value: Days}" class="list-group list-group-flush calendar-grid">
        <ItemTemplate>
            <div class="{{value: "calendar-day calendar-day-" + DayOfWeekIndex }}">
                <dot:LinkButton ID="DayButton" class="{{value: "list-group-item list-group-item-action " + (Selected ? "active calendar-day-btn" : "calendar-day-btn") }}"
                                Click="{controlCommand: SelectDate(_this.Date)}">
                    {{value: DayText}}
                    <!-- RENDER TEMPLATE HERE -->
                </dot:LinkButton>
            </div>
        </ItemTemplate>
    </dot:Repeater>

代码隐藏中的 ItemTemplate:

        [MarkupOptions(AllowBinding = false, MappingMode = MappingMode.InnerElement, Required = false)]
        [ConstantDataContextChange(typeof(ICollection<CalendarDayModel>)), CollectionElementDataContextChange(1)]
        public ITemplate ItemTemplate
        {
            get { return (ITemplate)GetValue(ItemTemplateProperty)!; }
            set { SetValue(ItemTemplateProperty, value); }
        }

        public static readonly DotvvmProperty ItemTemplateProperty =
            DotvvmProperty.Register<ITemplate, Calendar>(t => t.ItemTemplate);

控件的用法示例:

<cc:Calendar DataContext="{value: CalendarViewModel}" MultiSelect="true">
    <ItemTemplate>
        Selected: {{value: Selected}}
    </ItemTemplate>
</cc:Calendar>

不幸的是,我们在 DotVVM 中没有任何控件可以绑定模板并只渲染它。但是,您可以使用以下技巧:

  1. Repeater的模板中使用PlaceHolder控件:
<dot:Repeater ID="DaysRepeater" DataSource="{value: Days}" class="list-group list-group-flush calendar-grid">
    <ItemTemplate>
        <div class="{{value: "calendar-day calendar-day-" + DayOfWeekIndex}}">
            <dot:LinkButton ID="DayButton" class="{{value: "list-group-item list-group-item-action " + (Selected ? "active calendar-day-btn" : "calendar-day-btn") }}"
                            Click="{controlCommand: SelectDate(_this.Date)}">
                {{value: DayText}}
                <dot:PlaceHolder ID="TemplateHost" />
            </dot:LinkButton>
        </div>
    </ItemTemplate>
</dot:Repeater>
  1. Repeater 模板替换为您自己的模板,该模板将首先呈现原始模板,然后找到占位符,并将内部模板放入其中:
protected override void OnInit(IDotvvmRequestContext context)
{
    var repeater = (Repeater)FindControlInContainer("DaysRepeater");
    repeater!.ItemTemplate = new TemplateWrapper(ItemTemplate, repeater!.ItemTemplate);

    base.OnInit(context);
}

class TemplateWrapper : ITemplate 
{
    private readonly ITemplate innerTemplate;
    private readonly ITemplate repeaterTemplate;

    public TemplateWrapper(ITemplate innerTemplate, ITemplate repeaterTemplate)
    {
        this.innerTemplate = innerTemplate;
        this.repeaterTemplate = repeaterTemplate;
    }

    public void BuildContent(IDotvvmRequestContext context, DotvvmControl container)
    {
        repeaterTemplate.BuildContent(context, container);

        var placeholder = container.FindControlInContainer("TemplateHost");
        innerTemplate.BuildContent(context, placeholder!);
    }
}