更改依赖项属性不会导致自定义控件在需要时重新呈现

时间:2014-02-06 16:29:14

标签: wpf custom-controls rerender

我有一个自定义控件定义如下,Generic.xaml文件只包含一个带有一个名为'cvRoot'的画布的模板。

当其中一个依赖项属性发生更改时,我无法让控件正确地重新呈现。虽然它可以在我在应用程序中使用时绘制,但当我尝试测量它时,我总是得到零尺寸。这非常令人沮丧,因为我需要获得正确对齐控件的大小。

我尝试在Dependency Property寄存器方法中使用FrameworkPropertyMetadata并将选项设置为AffectsRender,但这不会改变发生的情况。我还在Callback中为每个属性尝试了InvalidateVisual,没有任何乐趣。

我真的把头发拉到这里,有什么想法吗?

namespace   GraphControls
{
    public class Marker :   Control
    {
        private Canvas cvRoot   =   null;
        private static Action   EmptyDelegate   =   delegate() { };

        static Marker()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(Marker), new FrameworkPropertyMetadata(typeof(Marker)));
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            cvRoot = GetTemplateChild("cvRoot") as Canvas;
        }



        protected   override void   OnRender(DrawingContext drawingContext)
        {
            base.OnRender(drawingContext);

            cvRoot.Children.Clear();

            // Text
            //
            TextBlock   tb = new TextBlock();
            tb.Text =   Text;
            tb.FontSize =   MarkerFontSize;
            tb.Foreground   =   TextColour;
            tb.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));

            // Background   Ellipse
            //
            Ellipse el = new Ellipse();
            el.Width = tb.DesiredSize.Height;
            el.Height   =   tb.DesiredSize.Height;
            el.Fill =   MarkerFill;

            // Add to   Canvas and reposition
            //
            cvRoot.Children.Add(el);
            cvRoot.Children.Add(tb);
            Canvas.SetLeft(tb, (el.Width / 2)   -   (tb.DesiredSize.Width   /   2));
            Canvas.SetTop(tb,   (el.Height / 2) -   (tb.DesiredSize.Height / 2));

            // Resize   the owning Canvas   so that external Measures   work
            //
            cvRoot.Width = el.Width;
            cvRoot.Height   =   el.Height;

        }

        public static   DependencyProperty TextProperty =   DependencyProperty.Register("Text", typeof(string), typeof(Marker),
                                        new PropertyMetadata("X",   new PropertyChangedCallback(OnGenericDependencyPropertyChanged)));
        public static   DependencyProperty MarkerFontSizeProperty   =   DependencyProperty.Register("MarkerFontSize",   typeof(double), typeof(Marker),
                                        new PropertyMetadata(12D,   new PropertyChangedCallback(OnGenericDependencyPropertyChanged)));
        public static   DependencyProperty MarkerFillProperty   =   DependencyProperty.Register("MarkerFill",   typeof(Brush), typeof(Marker),
                                        new PropertyMetadata(new SolidColorBrush(Colors.Black), new PropertyChangedCallback(OnGenericDependencyPropertyChanged)));
        public static   DependencyProperty TextColourProperty   =   DependencyProperty.Register("TextColour",   typeof(Brush), typeof(Marker),
                                        new PropertyMetadata(new SolidColorBrush(Colors.White), new PropertyChangedCallback(OnGenericDependencyPropertyChanged)));

        public string   Text
        {
            get {   return (string)GetValue(TextProperty); }
            set {   SetValue(TextProperty, value); }
        }

        public double   MarkerFontSize
        {
            get {   return (double)GetValue(MarkerFontSizeProperty); }
            set {   SetValue(MarkerFontSizeProperty, value); }
        }

        public Brush MarkerFill
        {
            get {   return (Brush)GetValue(MarkerFillProperty); }
            set {   SetValue(MarkerFillProperty, value); }
        }

        public Brush TextColour
        {
            get {   return (Brush)GetValue(TextColourProperty); }
            set {   SetValue(TextColourProperty, value); }
        }

        private static void OnGenericDependencyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs   e)
        {
            ((Marker)d).Dispatcher.Invoke(DispatcherPriority.Render,    EmptyDelegate);
        }

        protected   override Size   MeasureOverride(Size constraint)
        {
            Size retval =   new Size();

            // Handle   the template not being called   yet
            //
            if (cvRoot !=   null)
            {
                cvRoot.Measure(constraint);
                retval = cvRoot.DesiredSize;
            }

            return retval;

        }

    }
}

1 个答案:

答案 0 :(得分:1)

从您的代码示例中,您似乎使用了错误的控件来扩展。来自MSDN上的Control Authoring Overview页:

  

从控制中获取的好处

     

考虑从Control派生而不是使用UserControl类   如果符合以下任何条件:

     

•您希望通过控件可以自定义控件的外观   的ControlTemplate。

     

•您希望您的控件支持不同的主题。

     

从UserControl派生的好处

     

如果满足以下所有条件,请考虑从UserControl派生:

     

•您希望以与构建方式类似的方式构建控件   应用

     

•您的控件仅包含现有组件。

     

•您不需要支持复杂的自定义。

因此,如果您使用UserControl并在XAML中声明您的UI元素,我相信您会有更多的运气。但是,如果您确定要使用CustomControl,那么您应该在ControlTemplate中的XAML中定义所有的UI元素在generic.xaml中控制你。它们将全部自动渲染,您无需手动刷新任何内容。

此外,您不应该真的使用OnRender方法来执行您在XAML中可以执行的操作。这通常用于为控件定义新图形,这些图形尚未可用于WPF中的其他UI元素。只需创建DependencyProperties并将数据绑定到要在UI中控制的任何属性。需要注意的是,您必须使用RelativeSource Bindinggeneric.xaml访问它们(假设您已添加GraphControls XAML命名空间前缀):

<Ellipse Fill="{Binding MarkerFill, 
    RelativeSource={RelativeSource AncestorType={x:Type GraphControls:Marker}}}" />