Пользовательское управление Silverlight наследования. Повторно использовать шаблон? - PullRequest
3 голосов
/ 12 февраля 2010

У меня есть следующий сценарий:

[TemplatePart(Name = GoToEditModeButtonPart, Type = typeof(DoubleClickButton))]
public class ValueBoxWithLabel : ContentControl
{
    public const string GoToEditModeButtonPart = "GoToEditModeButtonPart";

    #region LabelText Dependency Property ...

    #region IsInEditMode Dependency Property ...

    public event EventHandler<ModeChangedEventArgs> ModeChanged;

    public ValueBoxWithLabel()
    {
        DefaultStyleKey = typeof (ValueBoxWithLabel);
    }

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        //IsInEditMode invokes ModeChanged in the dependency property
        ((DoubleClickButton) GetTemplateChild(GoToEditModeButtonPart)).DoubleClick += (sender, args) => IsInEditMode = true;
    }

    private void InvokeModeChanged(ModeChangedEventArgs e)
    {
        EventHandler<ModeChangedEventArgs> mode = ModeChanged;
        if (mode != null)
            mode(this, e);
    }
}

ValueBox является необходимой панелью для любого поля ввода. Теперь это просто, но будет повторно использоваться во всем приложении и будет содержать более сложное поведение и макет.

TextBox в качестве ввода является обязательным, поэтому я делаю этот элемент управления:

public class TextBoxWithLabel : ValueBoxWithLabel
{
    #region Text Dependency Property ...

    public TextBoxWithLabel()
    {
        DefaultStyleKey = typeof (TextBoxWithLabel);
    }
}

У меня есть текущий файл generic.xaml, который не работает, но дает представление о том, что я хочу:

<ResourceDictionary>

<ControlTemplate x:Key="ValueBoxWithLabelTemplate">
    <StackPanel Style="{StaticResource ValueBoxWithLabelPanelStyle}">
        <TextBlock Style="{StaticResource LabelStyle}" Text="{TemplateBinding LabelText}" />
        <Grid>
            <ContentPresenter Content="{TemplateBinding Content}" />
            <local:DoubleClickButton Background="Black" x:Name="GoToEditModeButtonPart"></local:DoubleClickButton>
        </Grid>
    </StackPanel>
</ControlTemplate>

<Style TargetType="local:ValueBoxWithLabel">
    <Setter Property="Template" Value="{StaticResource ValueBoxWithLabelTemplate}" />
</Style>

<Style TargetType="local:TextBoxWithLabel">
    <Setter Property="Template" Value="{StaticResource ValueBoxWithLabelTemplate}" />
    <Setter Property="Content">
        <Setter.Value>
            <TextBox Style="{StaticResource ValueBoxStyle}" Text="{TemplateBinding Text}" />
        </Setter.Value>
    </Setter>
</Style>

Поскольку ValueBoxWithLabel чаще всего используется с TextBox, я хочу создать для этого элемент управления, который использует один и тот же шаблон, поэтому мне не нужно копировать / вставлять шаблон, и у меня есть возможность поддерживать оба последних с такими же изменениями.

Как я могу повторно использовать ValueBoxWithLabelTemplate и переопределять только свойство содержимого, сохраняя оставшуюся часть шаблона?

1 Ответ

1 голос
/ 12 февраля 2010

Это интригующий подход. Я сам не пробовал, но похоже, что подход может сработать.

Проблема, с которой вы столкнулись в данный момент, заключается в том, что вы пытаетесь использовать свойство Content ContentPresenter. Однако для этого требуется назначить конкретный экземпляр элемента управления, который в этом случае вы делаете с TextBox. Вы не можете использовать TemplateBinding в TextBox, потому что он не является частью ControlTemplate.

Даже без проблемы TemplateBinding вы все равно сможете создать с ним только один элемент управления, поскольку вы не можете «повторно использовать» один и тот же экземпляр TextBox более чем в одном месте. Вот почему у нас есть шаблоны на первом месте.

Шаблоны полностью

Решение проблемы с шаблонами не должно быть таким сложным. Поистине хитрым было бы найти способ привязать внутреннее управление контентом специализированных элементов управления к свойствам специализированного класса. Это трудно сделать, когда вы не можете использовать TemplateBinding.

Сначала я просто обрисую изменения, которые вам, по крайней мере, нужно будет сделать, чтобы получить элементы управления рендерингом: -

<ControlTemplate x:Key="ValueBoxWithLabelTemplate">
    <StackPanel Style="{StaticResource ValueBoxWithLabelPanelStyle}">
        <TextBlock Style="{StaticResource LabelStyle}" Text="{TemplateBinding LabelText}" />
        <Grid>
            <ContentPresenter ContentTemplate="{TemplateBinding ContentTemplate}" />
            <local:DoubleClickButton Background="Black" x:Name="GoToEditModeButtonPart"></local:DoubleClickButton>
        </Grid>
    </StackPanel>
</ControlTemplate>

TextBoxWithLabel становится: -

<Style TargetType="local:TextBoxWithLabel">
    <Setter Property="Template" Value="{StaticResource ValueBoxWithLabelTemplate}" />
    <Setter Property="ContentTemplate">
        <Setter.Value>
            <DataTemplate>
                <TextBox Style="{StaticResource ValueBoxStyle}"  />
            </DataTemplate>
        </Setter.Value>
    </Setter>
</Style>

Я думаю, что (без тестирования) будет отображаться.

Проблема связывания

Однако, как вы можете видеть, привязка к свойству Text отсутствует. Теперь есть пара вещей, о которых я могу подумать, которые могут помочь вам решить эту проблему привязки, но они включают либо злоупотребление DataContext, либо создание подкласса ContentPresenter, чтобы помочь сквозным свойствам от внешнего управления к внутреннему один. Оба очень уродливы.

Заключение

Для такого простого базового шаблона вам, вероятно, лучше дублировать шаблон, чем перепрыгивать через все обручи, необходимые для повторного использования.

...