views:

577

answers:

1

Hi, 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?

+1  A: 

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.

AnthonyWJones
Thanks for the feedback. Propably wont get time this weekend, but i'll try it first thing monday, and report back :-)
Lars Udengaard
Hi again Anthony. I got around trying youre solution, but no luck.The problem is that im not able to access the "Parts" in the control template, thus the following line from above will not work:((DoubleClickButton) GetTemplateChild(GoToEditStateButtonPart)).DoubleClick += (sender, args) => IsInEditMode = true;Any thoughts?
Lars Udengaard
You have a lot of good pointers on the issue. Due to time pressure on our current project. I havent had time to dig deeper. I might post more later. Thanks for youre input.
Lars Udengaard