2016-05-10 9 views
0

Da ich eine Reihe von WPF CustomControls mache und ein Standard-ContextMenu für diese Steuerelemente verwenden möchte, möchte ich wissen, ob ich dieses ContextMenu als Ressource definieren kann.Kann ich ein Standard-ContextMenu für WPF CustomControls definieren?

Und wie ist der Stil eines solchen ContextMenu zu definieren? Wenn es möglich ist, könnte ich die contextMenu Kontrolle überschreiben mit etwas wie folgt aus:

ContextMenu="{StaticResource standardcontextmenu}" 

Vielen Dank im Voraus!

Antwort

2

Definieren Sie dies in einem XAML-Ressourcenwörterbuch, das mit App.xaml zusammengeführt wurde. Es ist also in Ihrer gesamten Anwendung verfügbar. Es ist einfach, ein Kontextmenü als Ressource zu definieren, aber wenn es sich um eine Ressource handelt, müssen Sie zusätzliche Arbeit leisten, um sie über ihren Kontext zu informieren. Mit den meisten WPF-Steuerelementen würden Sie eine RelativeSource AncestorType Bindung durchführen, aber das Kontextmenü ist nicht im VisualTree, so dass es nicht funktioniert.

It says here that ContextMenu.PlacementTarget will be set to the context menu's owner wenn das Menü geöffnet wird, aber das Überwachungsfenster auf meinem Desktop sagt, dass sie nur darüber scherzen. Wenn Sie ein Kontextmenü wie folgt definieren, seine DataContext ist diese Instanz von local:Bar:

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

... aber das funktioniert nicht, wenn das Kontextmenü eine Ressource ist. In diesem Fall müssen Sie DataContext selbst einstellen. Das stellt sich als nicht zu schmerzhaft heraus. Wir machen es im ContextMenuOpening Ereignis auf dem benutzerdefinierten Steuerelement. Wir definieren zwei benutzerdefinierte Steuerelemente. In einem werden wir ContextMenuOpening über eine EventSetter in der Style einrichten, und in der anderen werden wir ContextMenuOpening mit einem Lambda im Konstruktor behandeln. Ich mag die EventSetter Version, weil, während es ein wenig mehr Arbeit ist, Sie diesen Handler auf absolut alles werfen können, können Sie eine Style auf setzen. Sie könnten eine angehängte Eigenschaft/ein angehängtes Verhalten schreiben, um einen solchen Handler zu setzen, der noch einfacher zu verwenden wäre.

Themen/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> 

Themen/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

Hallo Ed! Ich bin mir nicht sicher, ob ich dich richtig verstanden habe. Bedeutet das, dass ich es für jedes CustomControl definieren muss? –

+0

Oh, gotcha, wenn Sie das gleiche Menü für eine Reihe von verschiedenen benutzerdefinierten Steuerelementtypen möchten, die auch machbar sind - definieren Sie es als eigene Ressource. Zurück in ein bisschen damit bin ich auf meinem Telefon atm. –

+0

@PatrickPirzer OK, ich bin jetzt wach. Es gibt einen Haken: Es ist einfach, ein gemeinsames Menü zu definieren. Es ist jedoch ein absoluter Alptraum, irgendetwas auf den Menüpunkten an irgendetwas auf der Steuerung zu binden, zu der das Menü gehört. Es ist einer der wenigen Teile von WPF, die sie einfach schrecklich vermasselt haben. WPF funktioniert wirklich sehr, wirklich zu verhindern, dass Kontextmenüs herausfinden, was ihr Kontext ist. –

Verwandte Themen