2016-05-10 38 views
0

因爲我製作了一堆WPF CustomControls,並且希望爲這些控件使用標準的ContextMenu,所以我想知道,如果我可以將ContextMenu定義爲資源。我可以爲WPF CustomControls定義標準的ContextMenu嗎?

這樣的ContextMenu的風格如何定義? 如果可能的話,我可以用類似的東西覆蓋控件的文本菜單:

ContextMenu="{StaticResource standardcontextmenu}" 

提前感謝!

回答

2

在一個合併到App.xaml中的XAML資源字典中定義它,所以它在整個應用程序中都可用。將上下文菜單定義爲資源很容易,但如果它是資源,則需要做額外的工作以使其知道其上下文是什麼。對於大多數WPF控件,您會執行RelativeSource AncestorType綁定,但上下文菜單不在VisualTree中,因此不起作用。

It says here that ContextMenu.PlacementTarget will be set to the context menu's owner菜單打開時,但桌面上的觀察窗口說他們只是在開玩笑。如果你像這樣定義一個上下文菜單,其DataContextlocal:Bar這種情況下:

<local:Bar > 
    <local:Bar.ContextMenu> 
     <ContextMenu> 
      <MenuItem 
       Header="{Binding ArbitraryProperty}" 
       /> 
     </ContextMenu> 
    </local:Bar.ContextMenu> 
</local:Bar> 

...但是當上下文菜單是一種資源,不工作。在這種情況下,您需要自己設置DataContext。結果不是太痛苦。我們將在自定義控件上的ContextMenuOpening事件中執行此操作。我們將定義兩個自定義控件。在一箇中,我們將通過Style中的EventSetter設置ContextMenuOpening,另一個我們將在構造函數中使用lambda處理ContextMenuOpening。我喜歡EventSetter版本,因爲雖然這是一個更多的工作,但您可以拋棄該處理程序,完全可以將任何東西放在Style上。你可以編寫一個附加的屬性/行爲來設置一個類似的處理程序,這將更容易使用。

主題/ Generic.xaml

<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:local="clr-namespace:SharedContextMenuTest" 
    x:Class="SharedContextMenuTest.Themes.Generic" 
    > 
    <ContextMenu x:Key="SharedContextMenu"> 
     <MenuItem Header="{Binding ArbitraryProperty}" /> 
    </ContextMenu> 

    <Style TargetType="{x:Type local:Foo}"> 
     <!-- IMPORTANT --> 
     <Setter Property="ContextMenu" Value="{StaticResource SharedContextMenu}" /> 
     <EventSetter Event="ContextMenuOpening" Handler="FooBar_ContextMenuOpening" /> 
     <!-- !IMPORTANT --> 

     <Setter Property="Template"> 
      <Setter.Value> 
       <ControlTemplate TargetType="{x:Type local:Foo}"> 
        <Border 
         Background="GhostWhite" 
         BorderBrush="DodgerBlue" 
         BorderThickness="1" 
         Margin="1" 
         > 
         <Label 
          Content="{TemplateBinding ArbitraryProperty}" 
          Padding="20" 
          /> 
        </Border> 
       </ControlTemplate> 
      </Setter.Value> 
     </Setter> 
    </Style> 

    <Style TargetType="{x:Type local:Bar}"> 
     <!-- IMPORTANT --> 
     <!-- Bar sets up the ContextMenuOpening handler in its constructor --> 
     <Setter Property="ContextMenu" Value="{StaticResource SharedContextMenu}" /> 
     <!-- !IMPORTANT --> 

     <Setter Property="Template"> 
      <Setter.Value> 
       <ControlTemplate TargetType="{x:Type local:Bar}"> 
        <Border 
         Background="GhostWhite" 
         BorderBrush="ForestGreen" 
         BorderThickness="1" 
         Margin="1" 
         > 
         <Label 
          Content="{TemplateBinding ArbitraryProperty}" 
          Padding="20" 
          /> 
        </Border> 
       </ControlTemplate> 
      </Setter.Value> 
     </Setter> 
    </Style> 
</ResourceDictionary> 

主題/ Generic.xaml.cs

namespace SharedContextMenuTest.Themes 
{ 
    public partial class Generic 
    { 
     private void FooBar_ContextMenuOpening(object sender, ContextMenuEventArgs e) 
     { 
      (e.Source as FrameworkElement).ContextMenu.DataContext = e.Source; 
     } 
    } 
} 

MyCustomControls.cs

namespace SharedContextMenuTest 
{ 
    public class Foo : Control 
    { 
     public static readonly DependencyProperty ArbitraryPropertyProperty = 
      DependencyProperty.Register("ArbitraryProperty", typeof(String), typeof(Foo), 
       new PropertyMetadata(nameof(Foo))); 
    } 

    public class Bar : Control 
    { 
     public Bar() 
     { 
      // Foo has an EventSetter in its Style; here we illustrate a quicker way. 
      ContextMenuOpening += (s, e) => ContextMenu.DataContext = this; 
     } 

     public static readonly DependencyProperty ArbitraryPropertyProperty = 
      DependencyProperty.Register("ArbitraryProperty", typeof(String), typeof(Bar), 
       new PropertyMetadata(nameof(Bar))); 
    } 
} 

MainWindow.xaml

<StackPanel Orientation="Vertical"> 
    <local:Foo /> 
    <local:Bar /> 
</StackPanel> 
+0

嗨!我不知道我是否理解你是對的。這是否意味着我必須爲每個CustomControl定義它? –

+0

噢,如果你想爲一堆不同的自定義控件類型提供相同的菜單,那麼可以將其定義爲自己的資源。回到這一點,我在我的手機atm。 –

+0

@PatrickPirzer好的,我現在醒了。有一個問題:定義共享菜單很容易。但是,將菜單項上的任何內容綁定到菜單所屬控件上的任何內容都是絕對的噩夢。這是WPF的幾個部分之一,他們只是簡單地搞砸了。 WPF真的很有效,很難阻止上下文菜單找到它們的上下文。 –

相關問題