WPF用户控件属性没有获得值

时间:2015-02-17 14:16:27

标签: c# .net wpf

我正在尝试创建一个非常简单的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。我该如何解决这个问题?

3 个答案:

答案 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)); }
}
  • 如果要设置其他XAML文档的颜色,还可以提供类型为Brush的属性,而不是颜色:
public Brush ClockForeground
{
    get { return tnClock.Foreground; }
    set { tnClock.Foreground = value; }
}

因此,在您的其他XAML文档中,您可以通过让XAML解析器自动将颜色名称转换为画笔来直接设置颜色:

<local:DigitalClock ClockForeground="Yellow" />
  • 或者更好的是,您可以在控件上声明依赖项属性并使用数据绑定(假设此处tbClockTextBlock):
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似乎更好

另外,请注意在这种情况下应使用依赖项属性,并利用绑定。