2010-08-09 52 views
2

我正在增强一个开源控件,以添加一些我需要的功能,并且我正陷入以下问题的绝望之中:Silverlight依赖属性snafu,我做错了什么?

控件是一个支持HTML但不通过属性的富文本框;你必须做这样的事情:

var sHtml = "..." 
ctrl.LoadHtml(sHtml) 

var sHtml = ctrl.SaveHtml() 

到目前为止好。但我想通过设置数据绑定的HTML,所以我做了称为HTML依赖属性:

public static readonly DependencyProperty HtmlProperty = 
     DependencyProperty.Register(
      "Html", 
      typeof(string), 
      typeof(RichTextEditor), 
      new PropertyMetadata(string.Empty, new PropertyChangedCallback(HtmlChangedCallback)) 
      ); 

    public string Html 
    { 
     get {return (string)GetValue(HtmlProperty);} 

     set {SetValue(HtmlProperty, value);} 
    } 

    private static void HtmlChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     //get the control 
     var rte = (RichTextEditor)d; 

     //got here, so load the html 
     rte.TextBox.LoadHtml((string)e.NewValue); 
    } 

这一切工作正常。我遇到的问题是,当控制内容发生变化时,我无法知道如何通知财产系统。该控制装置具有一个ContentChanged事件,所以我想这:

private void rtb_ContentChanged(object sender, RichTextBoxEventArgs e) 
    { 
     //tell the html prop that it changed 
     SetValue(HtmlProperty, rtb.SaveHtml()); 
    } 

但这然后触发HtmlChangedCallback和再侵入引起的问题。所以我尝试使用重新入口的标志,但是这样做很麻烦,因为事件的顺序比我预期的要复杂得多,而且在这一点上,我想我必须错过一些东西,所以我在这里问。请帮忙!提前致谢。

顺便说一句,该控件不支持INotifyPropertyChanged,并且实现它超出了范围,因为控件很大,我不想做那么多工作。

+0

“再入口”的原因是什么问题? – Jacob 2010-08-09 22:49:14

+0

嗯,最初的问题是,在HtmlChangedCallback中,我将HTML加载到控件中,该控件触发Content_Changed事件,并调用Content_Changed中的SetValue导致HtmlChangedCallback触发,并且在Silverlight转义出无限循环之前我看到了很多轮。我试着自己设置一个再入口标志,但是我发现事件的顺序是不可预测的,有时他们的事件会不顺序,而不是试图弄清楚,我可以告诉我做错了事,如果我不得不砍掉那么多。 – 2010-08-09 23:05:25

回答

0

这是一个经典的小问题,通常用一个简单的布尔标志来解决。

private bool suppressHtmlChanged = false; 

private static void HtmlChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) 
{ 

    var rte = (RichTextEditor)d; 

    if (!rte.suppressHtmlChanged) 
     rte.TextBox.LoadHtml((string)e.NewValue); 
} 


private void rtb_ContentChanged(object sender, RichTextBoxEventArgs e) 
{ 
    suppressHtmlChanged = true; 
    SetValue(HtmlProperty, rtb.SaveHtml()); 
    suppressHtmlChanged = false; 
} 

这些天,这样的解决方案显得那么老气是不是,我们经常可以说服自己,因为他们不是“优雅”这样的“低科技”的解决方案不可能是正确的。

+0

是的,这确实有窍门,但由于这种控制方式引发事件的方式在具体情况中变得更加复杂。谢谢。 – 2010-08-10 16:00:27

0

为什么你需要通知财产制度?您是否尝试更新数据绑定的来源?该控件不必实现INotifyPropertyChanges;这个接口必须在为数据绑定提供数据的类上实现。

如果您现在忽略Save/LoadHtml并将HTML存储为字符串会怎么样?它没有问题吗?

这是这对我来说工作正常

class MyControl 
{ 
    public int Value 
    { 
     get 
     { 
      return (int)GetValue(ValueProperty); 
     } 

     set 
     { 
      SetValue(ValueProperty, value); 
      Count.Text = value.ToString(); 
     } 
    } 

    public static readonly DependencyProperty ValueProperty = 
     DependencyProperty.Register("Value", 
      typeof(int), 
      typeof(Vote), 
      new PropertyMetadata(0, OnValueChanged)); 

    private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     ((MyControl)d).Value = (int)e.NewValue; 
    } 

计数是一个文本块控制的例子。

+0

是的,我有一个ViewModel,当视图中的Html(即富文本框)发生更改时,它应该被更新。不,它只是存储字符串不起作用,因为属性系统无法知道View已经改变了字符串。我的问题是:什么时候发生这种事情的正确方式? – 2010-08-09 23:01:05

+0

您是否在{Binding}语句中指定了双向模式?为了工作,你需要三件事 - 模型应该实现INotifyPropertyChanges并调用属性更改事件 - 视图应该使用依赖属性(使用Get/SetValue存储) - XAML应该指定双向模式 – AlexEzh 2010-08-09 23:11:18

+0

我不认为这三个步骤已经足够了,因为我已经完成了所有这些步骤。在进一步思考之后,我认为缺少的部分是相当普遍的:当你指定一个绑定来更新来自View的源码(比如TwoWay),并且View没有实现INPC时,它将如何工作?也就是说,View必须通知某个属性发生了变化,我认为应该可以通过DependencyProperty来实现,但我不太确定,因为我找不到它。因此,如果我希望View支持更新Source,View可能需要实现INPC。 – 2010-08-09 23:31:22