Question

i have the following scenario:

[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 is essential a panel for any inputbox. It is simple now, but will be reused throughout the application, and will contain more complex behavoir and layout.

TextBox as input is the must used, therefore i make this Control:

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

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

I then have the current generic.xaml, which doesnt work, but it gives in idea of what i want:

<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>

Since a ValueBoxWithLabel is most used with a TextBox i want to make a control for this, which reuses the same template, so i dont need to copy/paste the template, and have the headace of keeping both up-to-date with the same changes.

How can i reuse the ValueBoxWithLabelTemplate and only override the content property keeping the rest of the template?

Was it helpful?

Solution

Its an intriguing approach. I've not tried it myself but it looks like the approach might work.

The problem you have currently have is you are trying to use the Content property of the ContentPresenter. However that requires a concrete instance of a control be assigned which in this case you are doing with a TextBox. You can't use TemplateBinding in the TextBox because it is not part of a ControlTemplate.

Even without the TemplateBinding issue you would only be able to create one control with it anyway, since you can't "re-use" the same instance of a TextBox in more than one place. That's why we have templates in first place.

Templates all the way

Solving the templating issue shouldn't be that hard. The really tricking thing would be to find a way to bind the inner content control of specialised controls to properties of specialised class. This is hard to so when you can't use TemplateBinding.

First I'll just outline the changes you would at least need to make in order to get the controls rendering:-

<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>

The TextBoxWithLabel becomes:-

<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>

This I think (without testing it) will render.

The Binding Issue

However as you can see the binding on the Text property is missing. Now there are a couple of things I can think of that could help you solve this binding problem but they involve either abusing the DataContext or creating a sub-class of ContentPresenter to help pass-through properties from outer control to the inner one. Both are real ugly.

Conclusion

For such a simple base template you are probably better off duplicating the template than jumping through all the hoops necessary to acheive some kind of re-use.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top