2013-06-19 163 views
2

我有一個WPF控件來自第三方的ParentWPFControl,我想繼承它(讓我們調用Child類ChildWPFControl)。在這個過程中,我打算覆蓋一些後端邏輯和前端樣式的一部分。我可以做前者,但我在後者方面有問題。覆蓋WPF控件

我嘗試使用XAML < - > xaml.cs結構爲孩子的國家,但似乎不與VS以下警告允許:

Partial declarations of 'ChildWPFControl' must not specify different base classes 

現在,我想我可以寫一個ResourceDictionary XAML並在那裏定義前端,但如果我想添加事件處理程序到XAML(至少我找不到方法來做到這一點)會變成問題

另一個替代方案是定義覆蓋模板直接位於使用ChildWPFControl的對象中,但使設計更少模塊化。

我能想到的最後一個選擇是製作一個xaml < - > xaml.cs對,它是一個XAML樣式容器,然後強制ChildWPFControl使用通過後端事件處理程序定義的ControlTemplate。

無論如何,我正在尋找的是我的問題的優雅和模塊化解決方案。任何建議都會受到歡迎。

謝謝

回答

6

有幾個步驟需要完全覆蓋WPF控件。根據您的需求,有些是必需的。我會解釋這兩個重要的你:

創建一個新的默認樣式

每一個WPF控件都有某處包含它的視覺表現和性能覆蓋默認樣式。現在,如果從控制派生WPF仍然認爲你想用這個默認樣式,改變您更改DefaultStyle在靜態構造函數這樣

class MyButton : Button 
{ 
    static MyButton() 
    { 
     DefaultStyleKeyProperty.OverrideMetadata(typeof(MyButton), new FrameworkPropertyMetadata(typeof(MyButton))); 
    } 
} 

現在,如果你使用myButton的WPF試圖找到myButton的樣式,而不是按鈕。 OverridesDefaultStyle是一種風格的屬性,可能在某些方面也很方便。通常,這些默認樣式應放置在與xaml相關的主題中。

事件處理程序重寫類

時候它是在一個正確ControlTemplateStyle您不能使用通過事件像Click="OnClick"的語法糖。重點是,視覺表示儘可能與邏輯部分分離。使用OnApplyTemplate方法還有其他方法可以解決這個問題。通過覆蓋這個,你問模板「給我這個控制」,然後你只需在那裏添加你的事件。

override OnApplyTemplate() 
{ 
    var innerChild = Template.FindName("PART_InnerChild", this) as MyInnerControl; 
    if(innerChild != null) 
     innerChild.SomeEvent += OnSomeEvent; 
} 

注:這些控件的名稱通常與約定PART_開始,這可以在WPF中可以看出基本控制以及。它是告訴設計師的一個好方法「如果沒有這種控制,邏輯部分可能會崩潰」。還有屬性TemplatePart,但它並不重要,WPF不關心它。 AFAIK表達式混合與它做了一些,我個人用它來告訴其他人什麼樣的內部控制是絕對必要的,使這種控制工作。

個人建議

從類派生通常是試圖自定義控件的時候,我們做的最後一步。因爲需要做很多工作才能完全實現它的功能,並且可以限制重用性,所以我們儘量避免它,例如除了模板重寫和樣式外,還有一個很好的選擇。 attached behaviors

最後, 整個主題涵蓋在nice MSDN文章中。

希望有幫助

2

您可以創建您的用戶控件作爲包裝,包含基本控制。通過這種方式,你可以改變xaml中的樣式,在C#中添加一些邏輯來包裝控制。但這是一個繁瑣的過程。

編輯:將樣品(包裝爲Telerik的:radcombobox控件)

XAML:

<UserControl x:Class="Controls.SingleDictionaryValueSelector" 
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
      xmlns:CardControls="clr-namespace:Controls" 
      xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation" MinWidth="150" MinHeight="25" > 


    <Grid > 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition Width="*"></ColumnDefinition> 
      <ColumnDefinition Width="25"></ColumnDefinition> 
     </Grid.ColumnDefinitions> 
     <!-- customize visual for wrapped control --> 
     <telerik:RadComboBox x:Name="cb" 
          Grid.Column="0" 
          VerticalAlignment="Center" 
          SelectedValuePath="Key"   
          ClearSelectionButtonContent="Clear" 
          ClearSelectionButtonVisibility="Visible" 
          CanAutocompleteSelectItems="True" 
          CanKeyboardNavigationSelectItems="True" 
          SelectAllTextEvent="None" 
          OpenDropDownOnFocus="false" 
          IsFilteringEnabled="True" 
          TextSearchMode="Contains" 
          EmptyText="Select item" 
          telerik:StyleManager.Theme="Metro" 
          FontFamily="Calibri" 
          FontSize="14" 
          IsEditable="True" 
          Foreground="#666" 
          KeyDown="cb_KeyDown" 
          SelectionChanged="cb_SelectionChanged" 
          GotMouseCapture="cb_GotMouseCapture" 
          DropDownOpened="cb_DropDownOpened" 
          KeyUp="cb_KeyUp"> 

      <telerik:RadComboBox.ItemTemplate> 
       <DataTemplate> 
        <TextBlock TextWrapping="Wrap" Width="{Binding RelativeSource={RelativeSource AncestorType=telerik:RadComboBox},Path=ActualWidth}" Text="{Binding Path=Value}" /> 
       </DataTemplate> 
      </telerik:RadComboBox.ItemTemplate> 
     </telerik:RadComboBox> 

     <CardControls:ErrorInfo x:Name="errorInfoControl" Grid.Column="1" Visibility="Hidden"></CardControls:ErrorInfo> 
    </Grid> 
</UserControl> 

CS:

public partial class SingleDictionaryValueSelector : IMyCustomInterface 
{ 
    .... 

    private void cb_KeyDown(object sender, KeyEventArgs e) 
    { 
     RadComboBox senderCombo = sender as RadComboBox; 
     ... 

    } 

    private void cb_KeyUp(object sender, KeyEventArgs e) 
    { 
     SearchExecute(); 
    } 





    private void cb_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e) 
    { 
     RadComboBox senderCombo = sender as RadComboBox; 

     ... 
    } 

    private void cb_DropDownOpened(object sender, EventArgs e) 
    { 
     ... 
    } 

    ... 

} 
+0

這個想法似乎有潛力,但你可以詳細說明一個例子嗎?謝謝! – user1836155

+0

爲telerik:RadComboBox添加包裝器的簡單示例。希望有幫助 – Frank59

+0

嗯,我其實做過類似的事情。這基本上是添加一個容器對象。我對此的擔心是,如果不直接實現管道,我們不能直接訪問基本控件的基本功能,如果我們想要做的只是改變一個大的控制對象的幾個關鍵部分,這可能是相當不方便的。儘管如此,我仍然贊同這個答案,因爲它工作得很好,而且非常可行。 – user1836155

-1

它看起來像你有你的產業混合起來比它更不允許。 xaml的根元素必須與xaml.cs的基類匹配。

如果您在同一個項目中定義基類,那麼您將無法將它用作xaml中的基類,因爲它本身仍然是xaml,而不是已編譯的控件。解決這個問題的一些方法:你可以在一個單獨的項目中編譯它並引用它,你可以完全以.cs而不是部分類來編譯基類,或者你可以使用一些樣式嚮導。這裏是最後兩個示例的鏈接:http://svetoslavsavov.blogspot.ca/2009/09/user-control-inheritance-in-wpf.html

+0

不,那不是。嘗試在VS中創建Class1和Class2作爲WPF控件,並讓Class2繼承Class.xaml.cs中的Class1 – user1836155

+0

增加了一些更多詳細信息以說明類在同一項目中的情況 – Sogger