2010-05-12 49 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