在 UserControl 中重新计算 属性

Re-evaluate property in UserControl

我正在使用 C#-WPF。

我自己做了一个UserControl,一个简单的十字。有了坐标,我可以在图像上画一个 X。

参数为:

我的Cross.xaml:

<UserControl x:Name="userControl"
             x:Class="Project.Cross">
    <Grid>
        <Line Stroke="{Binding Foreground, ElementName=userControl}"
              StrokeThickness="{Binding Thickness, ElementName=userControl}"
              X1="{Binding X1, ElementName=userControl, Mode=OneWay}"
              X2="{Binding X2, ElementName=userControl, Mode=OneWay}"
              Y1="{Binding Y1, ElementName=userControl, Mode=OneWay}"
              Y2="{Binding Y2, ElementName=userControl, Mode=OneWay}" />

        <Line Stroke="{Binding Foreground, ElementName=userControl}"
              StrokeThickness="{Binding Thickness, ElementName=userControl}"
              X1="{Binding X2, ElementName=userControl, Mode=OneWay}"
              X2="{Binding X1, ElementName=userControl, Mode=OneWay}"
              Y1="{Binding Y1, ElementName=userControl, Mode=OneWay}"
              Y2="{Binding Y2, ElementName=userControl, Mode=OneWay}"/>
    </Grid>
</UserControl>

我的 Cross.xaml.cs :

public partial class Cross : UserControl
{
    public Cross()
    {
        InitializeComponent();
    }

    public readonly static DependencyProperty CenterPointProperty = DependencyProperty.Register("CenterPoint",
        typeof(PointF), typeof(Cross),
        new PropertyMetadata(default(PointF)));

    public PointF CenterPoint
    {
        get { return (PointF)GetValue(CenterPointProperty); }
        set { SetValue(CenterPointProperty, value); }
    }

    public readonly static DependencyProperty ThicknessProperty = DependencyProperty.Register("Thickness",
        typeof(int), typeof(Cross),
        new PropertyMetadata(2));

    public int Thickness
    {
        get { return (int)GetValue(ThicknessProperty); }
        set { SetValue(ThicknessProperty, value); }
    } 

    public float X1
    {
        get
        {
            return (float)(CenterPoint.X - (Width / 2));
        }
    }
    public float X2
    {
        get
        {
            return (float)(CenterPoint.X + (Width / 2));
        }
    }
    public float Y1
    {
        get
        {
            return (float)(CenterPoint.Y - (Height / 2));
        }
    }
    public float Y2
    {
        get
        {
            return (float)(CenterPoint.Y + (Height / 2));
        }
    }
}

我可以这样称呼它:

<local:Cross CenterPoint="{Binding Point}" Thickness="8" Foreground="Yellow" Height="40" Width="40"/>

我有一个问题,十字不显示。我添加了断点,当我更改 CenterPoint 时,值 X1、X2... 似乎没有刷新。如何强制 C# 重新评估这些值? (希望这能解决我的问题)

谢谢

如果您希望在 CenterPoint 依赖项 属性 设置为新值时刷新 X1、X2、Y1 和 Y2 read-only 属性的绑定,UserControl 应实现 INotifyPropertyChanged 界面。然后,您可以为依赖项 属性 注册一个 PropertyChangedCallback 并为 read-only 属性引发 PropertyChanged 事件:

public partial class Cross : UserControl, INotifyPropertyChanged
{
    public Cross()
    {
        InitializeComponent();
    }

    public readonly static DependencyProperty CenterPointProperty = DependencyProperty.Register("CenterPoint",
        typeof(PointF), typeof(Cross),
        new PropertyMetadata(default(PointF), new PropertyChangedCallback(OnCenterPointUpdated))));

    private static void OnCenterPointUpdated(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        Cross cross = d as Cross;
        cross.NotifyPropertyChanged("X1");
        cross.NotifyPropertyChanged("X2");
        cross.NotifyPropertyChanged("Y1");
        cross.NotifyPropertyChanged("Y2");
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    ...
}

mm8 的方法将适用于更新 X1 等,但还有其他问题需要解决才能使控制正常工作。

我正在使用另一种方法来更新坐标属性:将 X1 等变为 read-only dependency properties,并让您的用户控件在 CenterPoint 更改时以及控件的尺寸变化。

我还将您的 winforms PointF 更改为 WPF 的 System.Windows.Point,它使用 double 作为 XY,并且我已经也将 Thickness 更改为 float,因为 WPF 使用 float 作为线条粗细,您不妨充分利用。

我正在更新 SizeChanged 事件的坐标,这很关键,否则,坐标属性将仅在实际大小为 0 x 0 和 [=25 时在控件创建时设置=] 和 HeightNaN

最后,您应该使用 ActualWidthActualHeight,它们已更新 "live",不同于 HeightWidth,它们是 design-time 属性值。如果给它一个固定的WidthHeight,效果是一样的;如果需要,使用 ActualWidthActualHeight 允许它拉伸以适合容器。

public partial class Cross : UserControl
{
    public Cross()
    {
        InitializeComponent();

        SizeChanged += Cross_SizeChanged;
    }

    private void Cross_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        UpdateXYProperties();
    }

    protected void UpdateXYProperties()
    {
        X1 = (float)(CenterPoint.X - (ActualWidth / 2));
        X2 = (float)(CenterPoint.X + (ActualWidth / 2));
        Y1 = (float)(CenterPoint.Y - (ActualHeight / 2));
        Y2 = (float)(CenterPoint.Y + (ActualHeight / 2));
    }

    public readonly static DependencyProperty CenterPointProperty = DependencyProperty.Register(nameof(CenterPoint),
        typeof(Point), typeof(Cross),
        new PropertyMetadata(default(Point), CenterPoint_PropertyChanged));

    private static void CenterPoint_PropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        ((Cross)obj).UpdateXYProperties();
    }

    public Point CenterPoint
    {
        get { return (Point)GetValue(CenterPointProperty); }
        set { SetValue(CenterPointProperty, value); }
    }

    public readonly static DependencyProperty ThicknessProperty = DependencyProperty.Register(nameof(Thickness),
        typeof(float), typeof(Cross),
        new PropertyMetadata(2.0F));

    public float Thickness
    {
        get { return (float)GetValue(ThicknessProperty); }
        set { SetValue(ThicknessProperty, value); }
    }

    #region Read-Only Properties
    #region X1 Property
    public float X1
    {
        get { return (float)GetValue(X1Property); }
        protected set { SetValue(X1PropertyKey, value); }
    }

    internal static readonly DependencyPropertyKey X1PropertyKey =
        DependencyProperty.RegisterReadOnly("X1", typeof(float), typeof(Cross),
            new PropertyMetadata(0.0F));

    public static readonly DependencyProperty X1Property = X1PropertyKey.DependencyProperty;
    #endregion X1 Property

    #region X2 Property
    public float X2
    {
        get { return (float)GetValue(X2Property); }
        protected set { SetValue(X2PropertyKey, value); }
    }

    internal static readonly DependencyPropertyKey X2PropertyKey =
        DependencyProperty.RegisterReadOnly("X2", typeof(float), typeof(Cross),
            new PropertyMetadata(0.0F));

    public static readonly DependencyProperty X2Property = X2PropertyKey.DependencyProperty;
    #endregion X2 Property

    #region Y1 Property
    public float Y1
    {
        get { return (float)GetValue(Y1Property); }
        protected set { SetValue(Y1PropertyKey, value); }
    }

    internal static readonly DependencyPropertyKey Y1PropertyKey =
        DependencyProperty.RegisterReadOnly("Y1", typeof(float), typeof(Cross),
            new PropertyMetadata(0.0F));

    public static readonly DependencyProperty Y1Property = Y1PropertyKey.DependencyProperty;
    #endregion Y1 Property

    #region Y2 Property
    public float Y2
    {
        get { return (float)GetValue(Y2Property); }
        protected set { SetValue(Y2PropertyKey, value); }
    }

    internal static readonly DependencyPropertyKey Y2PropertyKey =
        DependencyProperty.RegisterReadOnly("Y2", typeof(float), typeof(Cross),
            new PropertyMetadata(0.0F));

    public static readonly DependencyProperty Y2Property = Y2PropertyKey.DependencyProperty;
    #endregion Y2 Property
    #endregion Read-Only Properties
}

最后,您不需要明确地进行绑定 Mode=OneWay,因为 Line.X1 等无法更新绑定源(这是您自己的 X1 等在这种情况下),因此默认情况下它们已经是 OneWay

<Grid>
    <Line 
        Stroke="{Binding Foreground, ElementName=userControl}"
        StrokeThickness="{Binding Thickness, ElementName=userControl}"
        X1="{Binding X1, ElementName=userControl}"
        X2="{Binding X2, ElementName=userControl}"
        Y1="{Binding Y1, ElementName=userControl}"
        Y2="{Binding Y2, ElementName=userControl}" 
        />

    <Line 
        Stroke="{Binding Foreground, ElementName=userControl}"
        StrokeThickness="{Binding Thickness, ElementName=userControl}"
        X1="{Binding X2, ElementName=userControl}"
        X2="{Binding X1, ElementName=userControl}"
        Y1="{Binding Y1, ElementName=userControl}"
        Y2="{Binding Y2, ElementName=userControl}"
        />
</Grid>

最后,几乎肯定是最不重要的一点:CenterPoint 设计的效果是将十字线偏移到控件边界之外,如果它不是控件的实际中心的话。如果那是您的意图,请不要费心阅读本段的其余部分。如果那不是您的意图,您可以按如下方式重写 UpdateXYProperties() 并丢失 CenterPoint 属性:

protected void UpdateXYProperties()
{
    X1 = 0;
    X2 = (float)ActualWidth;
    Y1 = 0;
    Y2 = (float)ActualHeight;
}

但这取决于你和圣安德鲁。