子类化 silverlight 模板化控件

Subclass a silverlight templated control

我想创建几个自定义控件。这是我要实现的目标的屏幕截图。

每个控件包含两部分,一个标签(TextBlock)和一个编辑器(UIElement)。标签部分对所有控件都是通用的。我不想在我创建的每个自定义控件中继续定义标签。所以我想到了创建一个基地class。这是基础编辑器 class。

[TemplatePart(Name = PartLabelName, Type = typeof (TextBlock))]
[TemplatePart(Name = PartEditorName, Type = typeof (ContentPresenter))]
public abstract class Editor : Control
{
    protected const string PartLabelName = "PartLabel";
    protected const string PartEditorName = "PartEditor";

    public static readonly DependencyProperty LabelStyleProperty = DependencyProperty.Register(
        "LabelStyle", typeof (Style), typeof (Editor), new PropertyMetadata(default(Style)));

    public static readonly DependencyProperty LabelHeightProperty = DependencyProperty.Register(
        "LabelHeight", typeof (double), typeof (Editor), new PropertyMetadata(default(double)));

    public static readonly DependencyProperty LabelWidthProperty = DependencyProperty.Register(
        "LabelWidth", typeof (double), typeof (Editor), new PropertyMetadata(default(double)));

    public static readonly DependencyProperty LabelProperty = DependencyProperty.Register(
        "Label", typeof (string), typeof (Editor), new PropertyMetadata(default(string)));

    public static readonly DependencyProperty EditorControlProperty = DependencyProperty.Register(
        "EditorControl", typeof (FrameworkElement), typeof (Editor), new PropertyMetadata(default(FrameworkElement)));

    public static readonly DependencyProperty EditorWidthProperty = DependencyProperty.Register(
        "EditorWidth", typeof (double), typeof (Editor), new PropertyMetadata(default(double)));

    public static readonly DependencyProperty EditorHeightProperty = DependencyProperty.Register(
        "EditorHeight", typeof (double), typeof (Editor), new PropertyMetadata(default(double)));

    public static readonly DependencyProperty EditorStyleProperty = DependencyProperty.Register(
        "EditorStyle", typeof (Style), typeof (Editor), new PropertyMetadata(default(Style)));

    public static readonly DependencyProperty LabelColorProperty = DependencyProperty.Register(
        "LabelColor", typeof (Brush), typeof (Editor), new PropertyMetadata(default(Brush)));

    private ContentPresenter _partEditorControl;
    private TextBlock _partLabel;

    protected Editor()
    {
        DefaultStyleKey = typeof (Editor);
    }

    public Brush LabelColor
    {
        get { return (Brush) GetValue(LabelColorProperty); }
        set { SetValue(LabelColorProperty, value); }
    }

    public Style EditorStyle
    {
        get { return (Style) GetValue(EditorStyleProperty); }
        set { SetValue(EditorStyleProperty, value); }
    }

    public double EditorHeight
    {
        get { return (double) GetValue(EditorHeightProperty); }
        set { SetValue(EditorHeightProperty, value); }
    }

    public double EditorWidth
    {
        get { return (double) GetValue(EditorWidthProperty); }
        set { SetValue(EditorWidthProperty, value); }
    }

    public Style LabelStyle
    {
        get { return (Style) GetValue(LabelStyleProperty); }
        set { SetValue(LabelStyleProperty, value); }
    }

    public double LabelHeight
    {
        get { return (double) GetValue(LabelHeightProperty); }
        set { SetValue(LabelHeightProperty, value); }
    }

    public double LabelWidth
    {
        get { return (double) GetValue(LabelWidthProperty); }
        set { SetValue(LabelWidthProperty, value); }
    }

    public FrameworkElement EditorControl
    {
        get { return (FrameworkElement) GetValue(EditorControlProperty); }
        set { SetValue(EditorControlProperty, value); }
    }

    public string Label
    {
        get { return (string) GetValue(LabelProperty); }
        set { SetValue(LabelProperty, value); }
    }

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        _partLabel = GetTemplateChild(PartLabelName) as TextBlock;
        _partEditorControl = GetTemplateChild(PartEditorName) as ContentPresenter;

        if (_partLabel == null || _partEditorControl == null)
        {
            throw new NullReferenceException("Template parts are not available");
        }
    }

这里是基本编辑器的样式。

<Style TargetType="local:Editor">
    <Setter Property="LabelWidth" Value="200" />
    <Setter Property="LabelHeight" Value="25" />
    <Setter Property="EditorHeight" Value="25" />
    <Setter Property="EditorWidth" Value="200" />
    <Setter Property="LabelColor" Value="Black" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:Editor">
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition Width="Auto" />
                        </Grid.ColumnDefinitions>
                        <TextBlock x:Name="PartLabel"
                                   Text="{TemplateBinding Label}"
                                   Width="{TemplateBinding LabelWidth}"
                                   Height="{TemplateBinding LabelHeight}"
                                   Style="{TemplateBinding LabelStyle}"
                                   Foreground="{TemplateBinding LabelColor}"
                                   VerticalAlignment="Center" />
                        <ContentPresenter Grid.Column="1"
                                          x:Name="PartEditor"
                                          Height="{TemplateBinding EditorHeight}"
                                          Width="{TemplateBinding EditorWidth}"
                                          Style="{TemplateBinding EditorStyle}"
                                          Content="{TemplateBinding EditorControl}" />
                    </Grid>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

所以我创建了我的第一个扩展编辑器的自定义控件 class。

[TemplatePart(Name = PartLabelName, Type = typeof (TextBlock))]
[TemplatePart(Name = PartEditorName, Type = typeof (ContentPresenter))]
public class NumericUpDownEditor : Editor
{
    private ContentPresenter _partEditorControl;
    private TextBlock _partLabel;

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

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        _partLabel = GetTemplateChild(PartLabelName) as TextBlock;
        _partEditorControl = GetTemplateChild(PartEditorName) as ContentPresenter;

        if (_partLabel == null || _partEditorControl == null)
        {
            throw new NullReferenceException("Template parts are not available");
        }
    }
}

和默认样式

<Style TargetType="local:NumericUpDownEditor">
    <Setter Property="EditorControl">
        <Setter.Value>
            <toolkit:NumericUpDown />
        </Setter.Value>
    </Setter>
</Style>

当我在用户控件中使用此 NumericEditor 时,没有任何显示。

<StackPanel x:Name="LayoutRoot" Orientation="Vertical">
    <customControls:NumericUpDownEditor Label="Numeric Up" />
</StackPanel>

我只想在我创建的每个自定义控件中设置编辑器 UIElement。 我是否也需要在样式中为 NumericEditor 定义模板?或者我还做错了什么?

谢谢

发现错误。我想从 sub-classed 控件中删除默认样式。通过这个 answer 发现我要在 ContentTemplate (DataTemplate) 而不是 Content

中以 subclass 的样式声明编辑器部分控件

修改了编辑器样式和 class 代码

public static readonly DependencyProperty EditorControlTemplateProperty = DependencyProperty.Register(
        "EditorControlTemplate", typeof (DataTemplate), typeof (Editor), new PropertyMetadata(default(DataTemplate)));

<ContentPresenter Grid.Column="1" x:Name="PartEditor"
                  Height="{TemplateBinding EditorHeight}"
                  Width="{TemplateBinding EditorWidth}"
                  Style="{TemplateBinding EditorStyle}"
                  Content="{TemplateBinding EditorControl}"
                  ContentTemplate="{TemplateBinding EditorControlTemplate}" />

修改子class编辑器

[TemplatePart(Name = PartLabelName, Type = typeof (TextBlock))]
[TemplatePart(Name = PartEditorName, Type = typeof (ContentPresenter))]
public class NumericUpDownEditor : Editor
{
    public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
        "Value", typeof (double), typeof (NumericUpDownEditor), new PropertyMetadata(default(double)));

    private ContentPresenter _partEditorControl;
    private TextBlock _partLabel;

    public double Value
    {
        get { return (double) GetValue(ValueProperty); }
        set { SetValue(ValueProperty, value); }
    }

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        _partLabel = GetTemplateChild(PartLabelName) as TextBlock;
        _partEditorControl = GetTemplateChild(PartEditorName) as ContentPresenter;

        if (_partLabel == null || _partEditorControl == null)
        {
            throw new NullReferenceException("Template parts are not available");
        }
    }
}

下面是子classed 控件的样式

<Style TargetType="local:NumericUpDownEditor">
    <Setter Property="EditorControlTemplate">
        <Setter.Value>
            <DataTemplate>
                <toolkit:NumericUpDown
                    Value="{Binding RelativeSource={RelativeSource AncestorType=local:NumericUpDownEditor}, Path=Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
            </DataTemplate>
        </Setter.Value>
    </Setter>
</Style>