2010-05-12 46 views
1

我想创建一个GUI(WPF)库,其中每个(自定义)控件基本上包装内部(第三方)控件。然后,我手动公开每个属性(不是全部,但几乎)。在XAML中所产生的控制是非常简单的:包装WPF控制

<my:CustomButton Content="ClickMe" /> 

而后面的代码非常简单,以及:

public class CustomButton : Control 
{  
    private MyThirdPartyButton _button = null; 

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

    public CustomButton() 
    { 
     _button = new MyThirdPartyButton(); 
     this.AddVisualChild(_button);   
    } 

    protected override int VisualChildrenCount 
    {  
     get 
      { return _button == null ? 0 : 1; } 
    } 

    protected override Visual GetVisualChild(int index) 
    { 
     if (_button == null) 
     { 
      throw new ArgumentOutOfRangeException(); 
     } 
     return _button; 
    } 

    #region Property: Content 
    public Object Content 
    { 
     get { return GetValue(ContentProperty); } 
     set { SetValue(ContentProperty, value); } 
    } 

    public static readonly DependencyProperty ContentProperty = DependencyProperty.Register(
         "Content", typeof(Object), 
        typeof(CustomButton), 
         new FrameworkPropertyMetadata(new PropertyChangedCallback(ChangeContent)) 
    ); 

    private static void ChangeContent(DependencyObject source, DependencyPropertyChangedEventArgs e) 
    { 
     (source as CustomButton).UpdateContent(e.NewValue); 
    } 

    private void UpdateContent(Object sel) 
    { 
     _button.Content = sel; 
    } 
    #endregion 
} 

问题就来了后,我们暴露了MyThirdPartyButton作为属性(如果我们不这样做揭露一些东西,我们想让程序员直接使用它)。通过简单地创建属性,像这样:

public MyThirdPartyButton InternalControl 
{ 
    get { return _button; } 
    set 
    { 
     if (_button != value) 
     { 
      this.RemoveVisualChild(_button); 
      _button = value; 
      this.AddVisualChild(_button); 
     } 
    } 
} 

产生的XAML会是这样:

<my:CustomButton> 
<my:CustomButton.InternalControl> 
    <thirdparty:MyThirdPartyButton Content="ClickMe" /> 
</my:CustomButton.InternalControl> 

什么我要找的,是这样的:

<my:CustomButton> 
<my:CustomButton.InternalControl Content="ClickMe" /> 

但是(使用我的代码)不可能为InternalControl添加属性...

任何想法/建议吗?

非常感谢,

- 罗伯特

回答

2

WPF的动画系统必须设置对象的子属性的能力,但XAML分析器不会。

两种解决方法:

  1. 在InternalControl属性setter,参加传递的值,并通过其DependencyProperties循环将它们复制到您的实际InternalControl。
  2. 使用构建事件以编程方式为所有内部控件属性创建附加属性。

我会依次解释其中的每一个。使用属性setter

该解决方案将不会导致你想要的简化的语法,但它是实现简单,可能会解决的主要问题是

设置属性,如何合并设置值您的容器控件的内部控件上设置了值。

对于这个解决方案,您继续使用XAML你不喜欢:

<my:CustomButton Something="Abc"> 
    <my:CustomButton.InternalControl> 
    <thirdparty:MyThirdPartyButton Content="ClickMe" /> 
    </my:CustomButton.InternalControl> 

,但你实际上并没有最终免去您InternalControl。

要做到这一点,你InternalControl的设置方法是:

public InternalControl InternalControl 
{ 
    get { return _internalControl; } 
    set 
    { 
    var enumerator = value.GetLocalValueEnumerator(); 
    while(enumerator.MoveNext()) 
    { 
     var entry = enumerator.Current as LocalValueEntry; 
     _internalControl.SetValue(entry.Property, entry.Value); 
    } 
    } 
} 

你可能需要一些额外的逻辑,排除移民不公开可见的或默认设置。这实际上可以通过在静态构造函数中创建一个虚拟对象并制作默认具有本地值的DP列表来轻松处理。

使用生成事件创建附加属性

该解决方案允许你写的很漂亮XAML:

<my:CustomButton Something="Abc" 
       my:ThirdPartyButtonProperty.Content="ClickMe" /> 

的实现是在构建时自动创建ThirdPartyButtonProperty类。构建事件将使用CodeDOM构造ThirdPartyButton中声明的每个属性的附加属性,这些属性尚未在CustomButton中进行镜像。在每一种情况下,附加属性的PropertyChangedCallback将值复制到InternalControl的相应属性:

public class ThirdPartyButtonProperty 
{ 
    public static object GetContent(... 
    public static void SetContent(... 
    public static readonly DependencyProperty ContentProperty = DependencyProperty.RegisterAttached("Content", typeof(object), typeof(ThirdPartyButtonProperty), new PropertyMetadata 
    { 
    PropertyChangedCallback = (obj, e) => 
    { 
     ((CustomButton)obj).InternalControl.Content = (object)e.NewValue; 
    } 
    }); 
} 

实施的这部分很简单:最棘手的部分是创建MSBuild任务,从你引用它。 csproj,然后对它进行排序,以便在我的:CustomButton的预编译之后运行,以便它可以查看它需要添加的其他属性。

+0

谢谢雷。第二个解决方法就像魅力一样。是的,这将是棘手的,以产生所有这些属性... 第一个解决方法不直接在setter中工作。这可能是因为那时对象尚未初始化。无论如何,如果您在初始化(value.Initialized + = HANDLER)之后通过GetLocalValueEnumerator()迭代它就可以工作。如果我错了,请纠正我。 我仍然必须检查“InternalControl”属性上的绑定是否正常工作。其实,我要检查: -Bindings -styles 靶向性风格 -ControlTemplates -ItemTemplates -Validations - ??? – Robert 2010-05-14 12:51:12