我有一个自定义控件定义如下,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;
}
}
}
答案 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 Binding
从generic.xaml
访问它们(假设您已添加GraphControls
XAML命名空间前缀):
<Ellipse Fill="{Binding MarkerFill,
RelativeSource={RelativeSource AncestorType={x:Type GraphControls:Marker}}}" />