OnApplyTemplate()在绑定正确的值之前执行

时间:2014-07-02 14:03:49

标签: c# wpf xaml mvvm

带有View摘录的原始问题

我在MVVM模式项目中使用了此custom control的改编版本 校准微观框架。

<gauge:STGaugeControl x:Name="Gauge"
    Radius="120"
    ScaleRadius="105" 
    ScaleStartAngle="120" 
    ResetPointer1OnStartUp="True"
    ScaleSweepAngle="300"
    Pointer1Length="95" 
    Pointer2Length="95" 
    Pointer3Length="95" 
    PointerCapRadius="25" 
    MinValue="{Binding MinGauge, FallbackValue=-6000}" 
    MaxValue="{Binding MaxGauge, FallbackValue=6000}" 
    MajorDivisionsCount="{Binding DivCount, FallbackValue=12}" 
    MinorDivisionsCount="5" 
    CurrentValue1="{Binding Value1}"
    CurrentValue2="{Binding Value2}"
    CurrentValue3="{Binding Value3}"
    ScaleLabelRadius="80"
    ...
    >

这是我的观点的一部分。现在,当代码运行时,值将被绑定并按预期工作。但是,缩放是在自定义控件的OnApplyTemplate()中调用的方法中绘制的,并且由于某种原因无法访问绑定值。当我删除回退时,它使用FallbackValue或默认的零值。 例如,当我使用鼠标悬停在事件上时调用缩放绘制方法时,它按预期工作。
是否早期调用OnApplyTemplate()来访问绑定值,还是在我的所有教程中还有其他错误和错过的内容?

自定义控制代码和标记摘录

自定义控制代码

[TemplatePart(Name = "LayoutRoot", Type = typeof(Grid))]
[TemplatePart(Name = "Pointer1", Type = typeof(Path))]
[TemplatePart(Name = "Pointer2", Type = typeof(Path))]
[TemplatePart(Name = "Pointer3", Type = typeof(Path))]
[TemplatePart(Name = "RangeIndicatorLight", Type = typeof(Ellipse))]
[TemplatePart(Name = "PointerCap", Type = typeof(Ellipse))]
public class STGaugeControl : Control
{
#region Private variables
private Grid rootGrid;
*snip*

#region Dependency properties
public static readonly DependencyProperty MinValueProperty =
    DependencyProperty.Register("MinValue", typeof(double), typeof(STGaugeControl), null);

public static readonly DependencyProperty MaxValueProperty =
    DependencyProperty.Register("MaxValue", typeof(double), typeof(STGaugeControl), null);

public static readonly DependencyProperty MajorDivisionsCountProperty =
    DependencyProperty.Register("MajorDivisionsCount", typeof(double), typeof(STGaugeControl), null);

public static readonly DependencyProperty MinorDivisionsCountProperty =
    DependencyProperty.Register("MinorDivisionsCount", typeof(double), typeof(STGaugeControl), null);
*snip*

#region Wrapper properties
public double MinValue
{
    get
    {
        return (double)GetValue(MinValueProperty);
    }
    set
    {
        SetValue(MinValueProperty, value);
    }
}

public double MaxValue
{
    get
    {
        return (double)GetValue(MaxValueProperty);
    }
    set
    {
        SetValue(MaxValueProperty, value);
    }
}
*snip*

#region Constructor
static STGaugeControl()
{
    DefaultStyleKeyProperty.OverrideMetadata(typeof(STGaugeControl), new FrameworkPropertyMetadata(typeof(STGaugeControl)));
}

#region Methods
private static void OnCurrentValue1PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    //Get access to the instance of CircularGaugeConrol whose property value changed
    STGaugeControl gauge = d as STGaugeControl;
    gauge.OnCurrentValue1Changed(e);
}
*snip*

public override void OnApplyTemplate()
{
    base.OnApplyTemplate();
    //Get reference to known elements on the control template
    rootGrid = GetTemplateChild("LayoutRoot") as Grid;
    pointer1 = GetTemplateChild("Pointer1") as Path;
    pointer2 = GetTemplateChild("Pointer2") as Path;
    pointer3 = GetTemplateChild("Pointer3") as Path;
    pointerCap = GetTemplateChild("PointerCap") as Ellipse;
    lightIndicator = GetTemplateChild("RangeIndicatorLight") as Ellipse;

    //Draw scale and range indicator
    DrawScale();

    //Set Zindex of pointer and pointer cap to a really high number so that it stays on top of the 
    //scale and the range indicator
    Canvas.SetZIndex(pointer1, 100001);
    Canvas.SetZIndex(pointer2, 100002);
    Canvas.SetZIndex(pointer3, 100003);
    Canvas.SetZIndex(pointerCap, 100005);

    if (ResetPointer1OnStartUp)
    {
        //Reset Pointer
        MovePointer1(ScaleStartAngle);
    }

    if (ResetPointer2OnStartUp)
    {
        //Reset Pointer
        MovePointer2(ScaleStartAngle);
    }

    if (ResetPointer3OnStartUp)
    {
        //Reset Pointer
        MovePointer3(ScaleStartAngle);
    }
}
*snip*

//Drawing the scale with the Scale Radius
public void DrawScale()
{
    //Calculate one major tick angle 
    Double majorTickUnitAngle = ScaleSweepAngle / MajorDivisionsCount;

    //Obtaining One minor tick angle 
    Double minorTickUnitAngle = ScaleSweepAngle / MinorDivisionsCount;

    //Obtaining One major ticks value
    Double majorTicksUnitValue = (MaxValue - MinValue) / MajorDivisionsCount;
    majorTicksUnitValue = Math.Round(majorTicksUnitValue, ScaleValuePrecision);

    Double minvalue = MinValue; ;

    // Drawing Major scale ticks
    for (Double i = ScaleStartAngle; i <= (ScaleStartAngle + ScaleSweepAngle); i = i + majorTickUnitAngle)
    {

        //Majortick is drawn as a rectangle 
        Rectangle majortickrect = new Rectangle();
        majortickrect.Height = MajorTickSize.Height;
        majortickrect.Width = MajorTickSize.Width;
        majortickrect.Fill = new SolidColorBrush(MajorTickColor);
        Point p = new Point(0.5, 0.5);
        majortickrect.RenderTransformOrigin = p;
        majortickrect.HorizontalAlignment = HorizontalAlignment.Center;
        majortickrect.VerticalAlignment = VerticalAlignment.Center;

        TransformGroup majortickgp = new TransformGroup();
        RotateTransform majortickrt = new RotateTransform();

        //Obtaining the angle in radians for calulating the points
        Double i_radian = (i * Math.PI) / 180;
        majortickrt.Angle = i;
        majortickgp.Children.Add(majortickrt);
        TranslateTransform majorticktt = new TranslateTransform();

        //Finding the point on the Scale where the major ticks are drawn
        //here drawing the points with center as (0,0)
        majorticktt.X = (int)((ScaleRadius) * Math.Cos(i_radian));
        majorticktt.Y = (int)((ScaleRadius) * Math.Sin(i_radian));

        //Points for the textblock which hold the scale value
        TranslateTransform majorscalevaluett = new TranslateTransform();
        //here drawing the points with center as (0,0)
        majorscalevaluett.X = (int)((ScaleLabelRadius) * Math.Cos(i_radian));
        majorscalevaluett.Y = (int)((ScaleLabelRadius) * Math.Sin(i_radian));

        //Defining the properties of the scale value textbox
        TextBlock tb = new TextBlock();

        tb.Height = ScaleLabelSize.Height;
        tb.Width = ScaleLabelSize.Width;
        tb.FontSize = ScaleLabelFontSize;
        tb.Foreground = new SolidColorBrush(ScaleLabelForeground);
        tb.TextAlignment = TextAlignment.Center;
        tb.VerticalAlignment = VerticalAlignment.Center;
        tb.HorizontalAlignment = HorizontalAlignment.Center;

        //Writing and appending the scale value

        //checking minvalue < maxvalue w.r.t scale precion value
        if (Math.Round(minvalue, ScaleValuePrecision) <= Math.Round(MaxValue, ScaleValuePrecision))
        {
            minvalue = Math.Round(minvalue, ScaleValuePrecision);
            tb.Text = minvalue.ToString();
            minvalue = minvalue + majorTicksUnitValue;

        }
        else
        {
            break;
        }
        majortickgp.Children.Add(majorticktt);
        majortickrect.RenderTransform = majortickgp;
        tb.RenderTransform = majorscalevaluett;
        rootGrid.Children.Add(majortickrect);
        rootGrid.Children.Add(tb);


        //Drawing the minor axis ticks
        Double onedegree = ((i + majorTickUnitAngle) - i) / (MinorDivisionsCount);

        if ((i < (ScaleStartAngle + ScaleSweepAngle)) && (Math.Round(minvalue, ScaleValuePrecision) <= Math.Round(MaxValue, ScaleValuePrecision)))
        {
            //Drawing the minor scale
            for (Double mi = i + onedegree; mi < (i + majorTickUnitAngle); mi = mi + onedegree)
            {
                //here the minortick is drawn as a rectangle 
                Rectangle mr = new Rectangle();
                mr.Height = MinorTickSize.Height;
                mr.Width = MinorTickSize.Width;
                mr.Fill = new SolidColorBrush(MinorTickColor);
                mr.HorizontalAlignment = HorizontalAlignment.Center;
                mr.VerticalAlignment = VerticalAlignment.Center;
                Point p1 = new Point(0.5, 0.5);
                mr.RenderTransformOrigin = p1;

                TransformGroup minortickgp = new TransformGroup();
                RotateTransform minortickrt = new RotateTransform();
                minortickrt.Angle = mi;
                minortickgp.Children.Add(minortickrt);
                TranslateTransform minorticktt = new TranslateTransform();

                //Obtaining the angle in radians for calulating the points
                Double mi_radian = (mi * Math.PI) / 180;
                //Finding the point on the Scale where the minor ticks are drawn
                minorticktt.X = (int)((ScaleRadius) * Math.Cos(mi_radian));
                minorticktt.Y = (int)((ScaleRadius) * Math.Sin(mi_radian));

                minortickgp.Children.Add(minorticktt);
                mr.RenderTransform = minortickgp;
                rootGrid.Children.Add(mr);


            }

        }

    }
}


}
}

Generic.xaml:

<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SpeedTorque">
*snip*

<Style TargetType="local:STGaugeControl" >
    <Setter Property="ResetPointer1OnStartUp" Value="True" />
    <Setter Property="ResetPointer2OnStartUp" Value="True" />
    <Setter Property="ResetPointer3OnStartUp" Value="True" />
    <Setter Property="ScaleValuePrecision" Value="5" />
    *snip*
    <Setter Property="GaugeBackgroundColor" Value="Black" />
    <Setter Property="DialTextColor" Value="White" />
    <Setter Property="DialTextFontSize" Value="8" />

    <Setter Property="Template" >
        <Setter.Value>
            <ControlTemplate TargetType="local:STGaugeControl">
                <!-- Root Grid-->
                <Grid x:Name="LayoutRoot" 
                      Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Radius, Converter={StaticResource radiusToDiameterConverter}}" 
                      Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Radius, Converter={StaticResource radiusToDiameterConverter}}" >


                    <Ellipse x:Name="OuterFrame" StrokeThickness="8" 
                             Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Radius, Converter={StaticResource radiusToDiameterConverter}}" 
                             Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Radius, Converter={StaticResource radiusToDiameterConverter}}" 
                             Fill="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=GaugeBackgroundColor, Converter={StaticResource backgroundColorConverter}}">

                        <Ellipse.Stroke>
                            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                <GradientStop Color="#FF636060" Offset="1"/>
                                <GradientStop Color="#FF5F5C5C" Offset="0"/>
                                <GradientStop Color="#FFEEDEDE" Offset="0.35"/>
                                <GradientStop Color="#FFA09595" Offset="0.705"/>
                            </LinearGradientBrush>
                        </Ellipse.Stroke>
                    </Ellipse>
                    *snip*

                    <!-- Pointer1 -->
                    <Path x:Name="Pointer1" Stroke="#FFE91C1C" StrokeThickness="2" 
                          Width="{TemplateBinding Pointer1Length}" 
                          Height="{TemplateBinding Pointer1Thickness}" HorizontalAlignment="Center"
                          Data="M1,1 L1,10 L156,6 z" Stretch="Fill"  RenderTransformOrigin="0,0.5" 
                          RenderTransform="{Binding RelativeSource={RelativeSource TemplatedParent}, 
                        Path=Pointer1Length, Converter={StaticResource pointerCenterConverter}}"
                          Visibility="{TemplateBinding Pointer2Visibility}" >
                        <Path.Fill>
                            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                <GradientStop Color="#33890A0A" Offset="0.197"/>
                                <GradientStop Color="#33C40808" Offset="1"/>
                                <GradientStop Color="#33E32323" Offset="0.61"/>
                            </LinearGradientBrush>
                        </Path.Fill>
                    </Path>
                    *snip*

                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
</ResourceDictionary>

1 个答案:

答案 0 :(得分:0)

我觉得您需要将您的比例绑定到VM属性,并在每次更改它所依赖的参数时重新计算它。 我想每次将新值设置为MinGauge或MaxGauge时,您都需要更新VM中的Scale属性。