我正在尝试创建一个非常简单的WPF用户控件来表示数字时钟。
我有几件事我希望客户端代码能够更改,例如前景文字颜色,字体等,所以我为它们制作了一些公共属性。部分代码如下所示:
public partial class DigitalClock : System.Windows.Controls.UserControl
{
public string Color { get; set; }
private Timer timer;
private string DisplayString { get { return DateTime.Now.ToString("dd-MM-yy HH:mm:ss"); } }
public DigitalClock()
{
InitializeComponent();
this.timer = new Timer();
this.timer.Tick += new EventHandler(UpdateClock);
this.timer.Interval = 1000;
this.timer.Enabled = true;
this.timer.Start();
UpdateClock(null, null);
try
{
//exception thrown here as this.Color is null
Color color = (Color)ColorConverter.ConvertFromString(this.Color);
tbClock.Foreground = new SolidColorBrush(color);
}
catch (Exception ex)
{
Console.WriteLine(">>>" + ex.Message);
}
}
private void UpdateClock(object sender, EventArgs e)
{
tbClock.Text = DisplayString;
}
}
}
我在另一个页面上使用它:
<CustomControls:DigitalClock color="#ff000000" />
没有语法错误,屏幕上出现时钟,但只要代码点击试图设置颜色的行,我就会得到一个Object reference is not set to an instance of an object
。
我认为这与设置Color属性的时间点有关,因为在计时器的第一个“tick”之后,该值不再为null。我该如何解决这个问题?
答案 0 :(得分:3)
当您将控件插入另一个XAML文档时,将在您的控件被实例化之后设置从此文档设置的属性,这意味着在您的构造函数调用您可能具有的Color
属性时在您的其他XAML文档中设置仍然具有其默认值。
要做你想做的事,你可以:
听取您的控件的Loaded
事件(请参阅https://msdn.microsoft.com/en-us/library/ms742302%28v=vs.110%29.aspx),这将在设置控件实例的所有属性后调用(您可能需要在此处启动计时器) ,并在Unloaded
事件中将其停止,以确保在屏幕上未实例化控件时不会勾选它,
您可以为Color
属性的setter编写一个正文来传播更改:
public string Color
{
set { tbClock.Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString(value)); }
}
Brush
的属性,而不是颜色:public Brush ClockForeground
{
get { return tnClock.Foreground; }
set { tnClock.Foreground = value; }
}
因此,在您的其他XAML文档中,您可以通过让XAML解析器自动将颜色名称转换为画笔来直接设置颜色:
<local:DigitalClock ClockForeground="Yellow" />
tbClock
是TextBlock
):public Brush ClockForeground
{
get { return (Brush)GetValue(ClockForegroundProperty); }
set { SetValue(ClockForegroundProperty, value); }
}
public static readonly DependencyProperty ClockForegroundProperty = DependencyProperty.Register("ClockForeground", typeof(Brush), typeof(DigitalClock));
public DigitalClock()
{
InitializeComponents();
...
BindingOperations.SetBinding(tbClock, TextBlock.ForegroundProperty, new Binding
{
Source = this,
Path = new PropertyPath(ClockForegroundProperty)
});
}
答案 1 :(得分:2)
您不应将属性声明为CLR属性。您应该创建相反的依赖项属性,默认情况下允许您进行绑定,验证以及许多其他属性。看看这个:http://www.codeproject.com/Articles/32825/How-to-Creating-a-WPF-User-Control-using-it-in-a-W。在您的示例句柄事件中加载如下:
this.Loaded += DigitalClock_Loaded;
void DigitalClock_Loaded(object sender, RoutedEventArgs e)
{
//your actions
Color color = (Color)ColorConverter.ConvertFromString(this.Color);
}
属性尚未绑定在构造函数中。
答案 2 :(得分:1)
而不是在构造函数中指定颜色,因为它将始终为null,因为在实例化对象之前不会设置该属性,所以使用支持字段使用Color
属性的setter似乎更好
另外,请注意在这种情况下应使用依赖项属性,并利用绑定。