在我的自定义控件中再添加一些 属性

Add some more property to my custom control

所以我有这个 Gauge:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:controlsGauge="clr-namespace:MyApplication.Controllers">

Gauge.xaml

    <Style TargetType="controlsGauge:Gauge">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="controlsGauge:Gauge">
                    <Viewbox>
                        <Grid Height="200"
                              Width="200">
                            <!-- Ticks -->
                            <Line x:Name="TicksProxy"
                                  Visibility="Collapsed"
                                  Fill="{TemplateBinding TickBrush}" />
                            <ItemsControl ItemsSource="{TemplateBinding Ticks}"
                                          VerticalAlignment="Center"
                                          HorizontalAlignment="Center">
                                <ItemsControl.ItemsPanel>
                                    <ItemsPanelTemplate>
                                        <Canvas />
                                    </ItemsPanelTemplate>
                                </ItemsControl.ItemsPanel>
                                <ItemsControl.ItemTemplate>
                                    <DataTemplate>
                                        <Rectangle Height="15"
                                                   Width="8"
                                                   Fill="{Binding Fill, ElementName=TicksProxy}">
                                            <Rectangle.RenderTransform>
                                                <TransformGroup>
                                                    <TranslateTransform X="-2.5"
                                                                        Y="-95" />
                                                    <RotateTransform Angle="{Binding}" />
                                                </TransformGroup>
                                            </Rectangle.RenderTransform>
                                        </Rectangle>
                                    </DataTemplate>
                                </ItemsControl.ItemTemplate>
                            </ItemsControl>

                            <!-- Scale -->
                            <Path Name="PART_Scale"
                                  Stroke="{TemplateBinding ScaleBrush}"
                                  StrokeThickness="{TemplateBinding ScaleWidth}" />

                            <!-- Trail -->
                            <Path Name="PART_Trail"
                                  Stroke="{TemplateBinding TrailBrush}"
                                  StrokeThickness="{TemplateBinding ScaleWidth}" />
                            <!-- Scale Ticks -->
                            <Line x:Name="ScaleTicksProxy"
                                  Visibility="Collapsed"
                                  Fill="{TemplateBinding ScaleTickBrush}"
                                  X1="{TemplateBinding ScaleWidth}" />
                            <ItemsControl ItemsSource="{TemplateBinding Ticks}"
                                          VerticalAlignment="Center"
                                          HorizontalAlignment="Center">
                                <ItemsControl.ItemsPanel>
                                    <ItemsPanelTemplate>
                                        <Canvas />
                                    </ItemsPanelTemplate>
                                </ItemsControl.ItemsPanel>
                                <ItemsControl.ItemTemplate>
                                    <DataTemplate>
                                        <Rectangle Height="{Binding X1, ElementName=ScaleTicksProxy}"
                                                   Width="2"
                                                   Fill="{Binding Fill, ElementName=ScaleTicksProxy}">
                                            <Rectangle.RenderTransform>
                                                <TransformGroup>
                                                    <TranslateTransform X="-0.5"
                                                                        Y="-77" />
                                                    <RotateTransform Angle="{Binding}" />
                                                </TransformGroup>
                                            </Rectangle.RenderTransform>
                                        </Rectangle>
                                    </DataTemplate>
                                </ItemsControl.ItemTemplate>
                            </ItemsControl>

                            <!-- Value and Unit -->
                            <StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Margin="0,145,0,0">
                                <StackPanel.RenderTransform>
                                    <RotateTransform Angle="0.5"/>
                                </StackPanel.RenderTransform>
                                <StackPanel Orientation="Horizontal">
                                    <TextBlock Name="PART_ValueText"
                                           Foreground="Orange"
                                           FontSize="16"
                                           FontFamily="Comic Sans MS"
                                           FontWeight="SemiBold"
                                           Text="{TemplateBinding Value}"
                                           TextAlignment="Center"
                                           Margin="0 0 0 0" />
                                    <TextBlock Foreground="Orange"
                                           FontSize="16"
                                           FontFamily="Comic Sans MS"
                                           FontWeight="SemiBold"
                                           Text="%"
                                           Margin="0 0 0 0" />
                                </StackPanel>
                                <TextBlock Foreground="{TemplateBinding UnitBrush}"
                                           FontSize="16"
                                           TextAlignment="Center"
                                           Text="{TemplateBinding Unit}"
                                           Margin="0" />
                            </StackPanel>

                            <!-- Needle -->
                            <Grid>
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="*" />
                                    <RowDefinition Height="*" />
                                </Grid.RowDefinitions>

                                <!-- The RenderTransform is in code behind. -->
                                <Path Name="PART_Needle"
                                      Stretch="Uniform"
                                      HorizontalAlignment="Center"
                                      Fill="{TemplateBinding NeedleBrush}"
                                      Data="M 0,0 l 0,100 l 5,0 l 0,-100 l -5,0"
                                      RenderTransformOrigin="0.5,1">
                                </Path>
                            </Grid>
                        </Grid>
                    </Viewbox>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>

Gauge.cs

[TemplatePart(Name = NeedlePartName, Type = typeof(Path))]
[TemplatePart(Name = ScalePartName, Type = typeof(Path))]
[TemplatePart(Name = TrailPartName, Type = typeof(Path))]
[TemplatePart(Name = ValueTextPartName, Type = typeof(TextBlock))]

public class 仪表:UserControl { #region常量

private const string NeedlePartName = "PART_Needle";

private const string ScalePartName = "PART_Scale";

private const string TrailPartName = "PART_Trail";

private const string ValueTextPartName = "PART_ValueText";

private const double Degrees2Radians = Math.PI / 180;

#endregion Constants

#region Dependency Property Registrations

public static readonly DependencyProperty MinimumProperty =
    DependencyProperty.Register("Minimum", typeof(double), typeof(Gauge), new PropertyMetadata(0.0));

public static readonly DependencyProperty MaximumProperty =
    DependencyProperty.Register("Maximum", typeof(double), typeof(Gauge), new PropertyMetadata(100.0));

public static readonly DependencyProperty ScaleWidthProperty =
    DependencyProperty.Register("ScaleWidth", typeof(Double), typeof(Gauge), new PropertyMetadata(26.0));

public static readonly DependencyProperty ValueProperty =
    DependencyProperty.Register("Value", typeof(double), typeof(Gauge), new PropertyMetadata(0.0, OnValueChanged));

public static readonly DependencyProperty UnitProperty =
    DependencyProperty.Register("Unit", typeof(string), typeof(Gauge), new PropertyMetadata(string.Empty));

public static readonly DependencyProperty NeedleBrushProperty =
    DependencyProperty.Register("NeedleBrush", typeof(Brush), typeof(Gauge), new PropertyMetadata(new SolidColorBrush(Colors.OrangeRed)));

public static readonly DependencyProperty ScaleBrushProperty =
    DependencyProperty.Register("ScaleBrush", typeof(Brush), typeof(Gauge), new PropertyMetadata(new SolidColorBrush(Colors.LightGray)));

public static readonly DependencyProperty TickBrushProperty =
    DependencyProperty.Register("TickBrush", typeof(Brush), typeof(Gauge), new PropertyMetadata(new SolidColorBrush(Colors.DimGray)));

public static readonly DependencyProperty TrailBrushProperty =
    DependencyProperty.Register("TrailBrush", typeof(Brush), typeof(Gauge), new PropertyMetadata(new SolidColorBrush(Colors.Orange)));

public static readonly DependencyProperty ValueBrushProperty =
    DependencyProperty.Register("ValueBrush", typeof(Brush), typeof(Gauge), new PropertyMetadata(new SolidColorBrush(Colors.DimGray)));

public static readonly DependencyProperty ScaleTickBrushProperty =
    DependencyProperty.Register("ScaleTickBrush", typeof(Brush), typeof(Gauge), new PropertyMetadata(new SolidColorBrush(Colors.White)));

public static readonly DependencyProperty UnitBrushProperty =
    DependencyProperty.Register("UnitBrush", typeof(Brush), typeof(Gauge), new PropertyMetadata(new SolidColorBrush(Colors.DimGray)));

public static readonly DependencyProperty ValueStringFormatProperty =
    DependencyProperty.Register("ValueStringFormat", typeof(string), typeof(Gauge), new PropertyMetadata("N0"));

protected static readonly DependencyProperty ValueAngleProperty =
    DependencyProperty.Register("ValueAngle", typeof(double), typeof(Gauge), new PropertyMetadata(null));

public static readonly DependencyProperty TicksProperty =
    DependencyProperty.Register("Ticks", typeof(IEnumerable<double>), typeof(Gauge), new PropertyMetadata(null));

#endregion Dependency Property Registrations

#region Constructors

public Gauge()
{
    this.DefaultStyleKey = typeof(Gauge);
    this.Ticks = this.getTicks();
}

#endregion Constructors

#region Properties

public double Minimum
{
    get { return (double)GetValue(MinimumProperty); }
    set { SetValue(MinimumProperty, value); }
}

public double Maximum
{
    get { return (double)GetValue(MaximumProperty); }
    set { SetValue(MaximumProperty, value); }
}

public Double ScaleWidth
{
    get { return (Double)GetValue(ScaleWidthProperty); }
    set { SetValue(ScaleWidthProperty, value); }
}

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

public string Unit
{
    get { return (string)GetValue(UnitProperty); }
    set { SetValue(UnitProperty, value); }
}

public Brush NeedleBrush
{
    get { return (Brush)GetValue(NeedleBrushProperty); }
    set { SetValue(NeedleBrushProperty, value); }
}

public Brush TrailBrush
{
    get { return (Brush)GetValue(TrailBrushProperty); }
    set { SetValue(TrailBrushProperty, value); }
}

public Brush ScaleBrush
{
    get { return (Brush)GetValue(ScaleBrushProperty); }
    set { SetValue(ScaleBrushProperty, value); }
}

public Brush ScaleTickBrush
{
    get { return (Brush)GetValue(ScaleTickBrushProperty); }
    set { SetValue(ScaleTickBrushProperty, value); }
}

public Brush TickBrush
{
    get { return (Brush)GetValue(TickBrushProperty); }
    set { SetValue(TickBrushProperty, value); }
}

public Brush ValueBrush
{
    get { return (Brush)GetValue(ValueBrushProperty); }
    set { SetValue(ValueBrushProperty, value); }
}

public Brush UnitBrush
{
    get { return (Brush)GetValue(UnitBrushProperty); }
    set { SetValue(UnitBrushProperty, value); }
}

public string ValueStringFormat
{
    get { return (string)GetValue(ValueStringFormatProperty); }
    set { SetValue(ValueStringFormatProperty, value); }
}

public double ValueAngle
{
    get { return (double)GetValue(ValueAngleProperty); }
    set { SetValue(ValueAngleProperty, value); }
}

public IEnumerable<double> Ticks
{
    get { return (IEnumerable<double>)GetValue(TicksProperty); }
    set { SetValue(TicksProperty, value); }
}

#endregion Properties

public override void OnApplyTemplate()
{
    // Draw Scale
    var scale = this.GetTemplateChild(ScalePartName) as Path;
    if (scale != null)
    {
        var pg = new PathGeometry();
        var pf = new PathFigure();
        pf.IsClosed = false;
        var middleOfScale = 77 - this.ScaleWidth / 2;
        pf.StartPoint = this.ScalePoint(-150, middleOfScale);
        var seg = new ArcSegment();
        seg.SweepDirection = SweepDirection.Clockwise;
        seg.IsLargeArc = true;
        seg.Size = new Size(middleOfScale, middleOfScale);
        seg.Point = this.ScalePoint(150, middleOfScale);
        pf.Segments.Add(seg);
        pg.Figures.Add(pf);
        scale.Data = pg;
    }

    OnValueChanged(this, new DependencyPropertyChangedEventArgs());
    base.OnApplyTemplate();
}

private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    Gauge c = (Gauge)d;
    if (!Double.IsNaN(c.Value))
    {
        var middleOfScale = 77 - c.ScaleWidth / 2;
        var needle = c.GetTemplateChild(NeedlePartName) as Path;
        var valueText = c.GetTemplateChild(ValueTextPartName) as TextBlock;
        c.ValueAngle = c.ValueToAngle(c.Value);

        // Needle
        if (needle != null)
        {
            needle.RenderTransform = new RotateTransform() { Angle = c.ValueAngle };
        }

        // Trail
        var trail = c.GetTemplateChild(TrailPartName) as Path;
        if (trail != null)
        {
            if (c.ValueAngle > -146)
            {
                trail.Visibility = Visibility.Visible;
                var pg = new PathGeometry();
                var pf = new PathFigure();
                pf.IsClosed = false;
                pf.StartPoint = c.ScalePoint(-150, middleOfScale);
                var seg = new ArcSegment();
                seg.SweepDirection = SweepDirection.Clockwise;
                // We start from -150, so +30 becomes a large arc.
                seg.IsLargeArc = c.ValueAngle > 30;
                seg.Size = new Size(middleOfScale, middleOfScale);
                seg.Point = c.ScalePoint(c.ValueAngle, middleOfScale);
                pf.Segments.Add(seg);
                pg.Figures.Add(pf);
                trail.Data = pg;
            }
            else
            {
                trail.Visibility = Visibility.Collapsed;
            }
        }

        // Value Text
        if (valueText != null)
        {
            valueText.Text = c.Value.ToString(c.ValueStringFormat);
        }
    }
}

private Point ScalePoint(double angle, double middleOfScale)
{
    return new Point(100 + Math.Sin(Degrees2Radians * angle) * middleOfScale, 100 - Math.Cos(Degrees2Radians * angle) * middleOfScale);
}

private double ValueToAngle(double value)
{
    double minAngle = -150;
    double maxAngle = 150;

    // Off-scale to the left
    if (value < this.Minimum)
    {
        return minAngle - 7.5;
    }

    // Off-scale to the right
    if (value > this.Maximum)
    {
        return maxAngle + 7.5;
    }

    double angularRange = maxAngle - minAngle;

    return (value - this.Minimum) / (this.Maximum - this.Minimum) * angularRange + minAngle;
}

private IEnumerable<double> getTicks()
{
    double tickSpacing = (this.Maximum - this.Minimum) / 10;
    for (double tick = this.Minimum; tick <= this.Maximum; tick += tickSpacing)
    {
        yield return ValueToAngle(tick);
    }
}

}

我控:

<Controllers:Gauge
    x:Name="gauge"
    Minimum="0"
    Maximum="100"
    NeedleBrush="Transparent"
    ScaleTickBrush="Transparent"
    ScaleBrush="Red"
    ScaleWidth="30"                        
    TickBrush="Red"
    TrailBrush="LightSeaGreen"
    ValueBrush="Orange"
    ValueStringFormat="N1"
    Width="220"
    Height="220"
    VerticalAlignment="Center"
    HorizontalAlignment="Center"                        
    Margin="0,0,0,0">

所以我想输出我的 TickWidth 属性 以便能够更改它(圆圈周围的红线),所以我将其添加到我的 Gauge.cs 文件中:

public static readonly DependencyProperty TickWidthProperty =
    DependencyProperty.Register("TickWidth", typeof(double), typeof(Gauge), new PropertyMetadata(0.0));

public double TickWidth
{
    get { return (double)GetValue(TickWidthProperty); }
    set { SetValue(TickWidthProperty, value); }
}

在我的 Gauge.xaml 文件中,我替换了里面的 Width属性 (Width="8"):

<Rectangle Height="15"
           Width="8"
           Fill="{Binding Fill, ElementName=TicksProxy}">
    <Rectangle.RenderTransform>
        <TransformGroup>
            <TranslateTransform X="-2.5"
                                Y="-95" />
            <RotateTransform Angle="{Binding}" />
        </TransformGroup>
    </Rectangle.RenderTransform>
</Rectangle>

有:

Width="{TemplateBinding TickWidth}"

但是出现错误:

Error 40 The member "TickWidth" is not recognized or is not accessible.

您可以使用 {RelativeSource} 绑定到父控件的 属性:

Width="{Binding TickWidth, RelativeSource={RelativeSource AncestorType=UserControl}}"

或者

Width="{Binding TickWidth, RelativeSource={RelativeSource AncestorType=controlsGauge:Gauge}}"