在 UserControl 中重新计算 属性
Re-evaluate property in UserControl
我正在使用 C#-WPF。
我自己做了一个UserControl,一个简单的十字。有了坐标,我可以在图像上画一个 X。
参数为:
- CenterPoint:十字的中心
- 前景:十字的颜色
- 厚度:十字的厚度
我的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
作为 X
和 Y
,并且我已经也将 Thickness
更改为 float
,因为 WPF 使用 float
作为线条粗细,您不妨充分利用。
我正在更新 SizeChanged
事件的坐标,这很关键,否则,坐标属性将仅在实际大小为 0 x 0 和 [=25 时在控件创建时设置=] 和 Height
是 NaN
。
最后,您应该使用 ActualWidth
和 ActualHeight
,它们已更新 "live",不同于 Height
和 Width
,它们是 design-time 属性值。如果给它一个固定的Width
和Height
,效果是一样的;如果需要,使用 ActualWidth
和 ActualHeight
允许它拉伸以适合容器。
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;
}
但这取决于你和圣安德鲁。
我正在使用 C#-WPF。
我自己做了一个UserControl,一个简单的十字。有了坐标,我可以在图像上画一个 X。
参数为:
- CenterPoint:十字的中心
- 前景:十字的颜色
- 厚度:十字的厚度
我的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
作为 X
和 Y
,并且我已经也将 Thickness
更改为 float
,因为 WPF 使用 float
作为线条粗细,您不妨充分利用。
我正在更新 SizeChanged
事件的坐标,这很关键,否则,坐标属性将仅在实际大小为 0 x 0 和 [=25 时在控件创建时设置=] 和 Height
是 NaN
。
最后,您应该使用 ActualWidth
和 ActualHeight
,它们已更新 "live",不同于 Height
和 Width
,它们是 design-time 属性值。如果给它一个固定的Width
和Height
,效果是一样的;如果需要,使用 ActualWidth
和 ActualHeight
允许它拉伸以适合容器。
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;
}
但这取决于你和圣安德鲁。