2012-01-22 15 views
13

Ho un pulsante con un'immagine come il suo contenuto in una barra degli strumenti. Mi piacerebbe che questo pulsante aprisse un menu sotto di esso quando si fa clic. Come?Come si apre un menu popup quando si fa clic su un pulsante?

<Toolbar> 
      <Button> 
       <Button.Content> 
        <Image Source="../Resources/help.png"></Image> 
       </Button.Content> 
      </Button> 
</Toolbar> 

Grazie !!

+5

Hai provato qualcosa? –

risposta

1

Ci sono molti modi per ottenere questo fatto e si potrebbe prendere in considerazione questo approccio ...

<ToolBar DockPanel.Dock="Top"> 
    <MenuItem IsSubmenuOpen="{Binding SomeProperty}"> 
     <MenuItem.Header> 
      <Button Height="28"> 
       <Button.Content> 
        <Image Source="---your image---"></Image> 
       </Button.Content> 
      </Button> 
     </MenuItem.Header> 
     <Menu> 
      <MenuItem Header="Do this" /> 
      <MenuItem Header="Do that"/> 
     </Menu> 
    </MenuItem> 
</ToolBar> 

Questo avvolge il pulsante in un MenuItem che ha un sottomenu. Come mostrato qui, la proprietà MenuItem denominata IsSubMenuOpen è associata a una proprietà di notifica di tipo bool nel ViewModel denominato SomeProperty.

È necessario che il ViewModel attivi questa proprietà in base a ciò che si sta tentando di fare. Si consiglia di prendere in considerazione la possibilità di fare un pulsante di attivazione/disattivazione del pulsante in modo da facilitare la chiusura del sottomenu, altrimenti si dovrà cablare un comportamento aggiuntivo nel ViewModel.

8

Ho trovato questo due soluzioni dopo la ricerca di esso:

1) Split Button in WPF

2) DropDownButtons in WPF

la seconda soluzione è il mio favorit (fonte tratto dal sito di Andrew Wilkinson)

public class DropDownButton : ToggleButton 
{ 
    // *** Dependency Properties *** 

    public static readonly DependencyProperty DropDownProperty = 
    DependencyProperty.Register("DropDown", 
           typeof(ContextMenu), 
           typeof(DropDownButton), 
           new UIPropertyMetadata(null)); 

    // *** Constructors *** 

    public DropDownButton() { 
    // Bind the ToogleButton.IsChecked property to the drop-down's IsOpen property 

    Binding binding = new Binding("DropDown.IsOpen"); 
    binding.Source = this; 
    this.SetBinding(IsCheckedProperty, binding); 
    } 

    // *** Properties *** 

    public ContextMenu DropDown { 
    get { return (ContextMenu)this.GetValue(DropDownProperty); } 
    set { this.SetValue(DropDownProperty, value); } 
    } 

    // *** Overridden Methods *** 

    protected override void OnClick() { 
    if (this.DropDown != null) { 
     // If there is a drop-down assigned to this button, then position and display it 

     this.DropDown.PlacementTarget = this; 
     this.DropDown.Placement = PlacementMode.Bottom; 

     this.DropDown.IsOpen = true; 
    } 
    } 
} 

utilizzo

<ctrl:DropDownButton Content="Drop-Down"> 
    <ctrl:DropDownButton.DropDown> 
    <ContextMenu> 
     <MenuItem Header="Item 1" /> 
     <MenuItem Header="Item 2" /> 
     <MenuItem Header="Item 3" /> 
    </ContextMenu> 
    </ctrl:DropDownButton.DropDown> 
</ctrl:DropDownButton> 

speranza che ti aiuta ...

+3

Questo approccio non è simile a WPF: deve essere utilizzata la proprietà associata, non la sottoclasse. Motivi: 1. gli stili non funzionano più 2. puoi ricavare solo da una classe ma avere molte proprietà attaccate diverse sullo stesso oggetto – Mikhail

+0

@Mikhail hai ragione, era solo un esempio – punker76

+1

Come un principiante WPF, questi sono anche incredibilmente difficile da lavorare. Tante informazioni mancanti. – Chris

30

Invece di utilizzare una sottoclasse Button, è possibile utilizzare proprietà associate o un comportamento per implementare la discesa funzionalità del pulsante, per un approccio più WPF-like e in modo da don 't impatto che il pulsante di stile:

using System.Windows.Interactivity; 

public class DropDownButtonBehavior : Behavior<Button> 
{ 
    private bool isContextMenuOpen; 

    protected override void OnAttached() 
    { 
     base.OnAttached(); 
     AssociatedObject.AddHandler(Button.ClickEvent, new RoutedEventHandler(AssociatedObject_Click), true); 
    } 

    void AssociatedObject_Click(object sender, System.Windows.RoutedEventArgs e) 
    { 
     Button source = sender as Button; 
     if (source != null && source.ContextMenu != null) 
     { 
      if (!isContextMenuOpen) 
      { 
       // Add handler to detect when the ContextMenu closes 
       source.ContextMenu.AddHandler(ContextMenu.ClosedEvent, new RoutedEventHandler(ContextMenu_Closed), true); 
       // If there is a drop-down assigned to this button, then position and display it 
       source.ContextMenu.PlacementTarget = source; 
       source.ContextMenu.Placement = PlacementMode.Bottom; 
       source.ContextMenu.IsOpen = true; 
       isContextMenuOpen = true; 
      } 
     }    
    } 

    protected override void OnDetaching() 
    { 
     base.OnDetaching(); 
     AssociatedObject.RemoveHandler(Button.ClickEvent, new RoutedEventHandler(AssociatedObject_Click)); 
    } 

    void ContextMenu_Closed(object sender, RoutedEventArgs e) 
    { 
     isContextMenuOpen = false; 
     var contextMenu = sender as ContextMenu; 
     if (contextMenu != null) 
     { 
      contextMenu.RemoveHandler(ContextMenu.ClosedEvent, new RoutedEventHandler(ContextMenu_Closed)); 
     } 
    } 
} 

Usage:

<!-- NOTE: xmlns:i="schemas.microsoft.com/expression/2010/interactivity‌​" --> 
<Button> 
    <i:Interaction.Behaviors> 
     <local:DropDownButtonBehavior/> 
    </i:Interaction.Behaviors> 
    <Button.Content> 
     <StackPanel Orientation="Horizontal"> 
      <Image Source="/DropDownButtonExample;component/Assets/add.png" SnapsToDevicePixels="True" Height="16" Width="16" /> 
      <TextBlock Text="Add"/> 
      <Separator Margin="2,0"> 
       <Separator.LayoutTransform> 
        <TransformGroup> 
         <TransformGroup.Children> 
          <TransformCollection> 
           <RotateTransform Angle="90"/> 
          </TransformCollection> 
         </TransformGroup.Children> 
        </TransformGroup> 
       </Separator.LayoutTransform> 
      </Separator> 
      <Path Margin="2" VerticalAlignment="Center" Width="6" Fill="#FF527DB5" Stretch="Uniform" HorizontalAlignment="Right" Data="F1 M 301.14,-189.041L 311.57,-189.041L 306.355,-182.942L 301.14,-189.041 Z "/> 
     </StackPanel> 
    </Button.Content> 
    <Button.ContextMenu> 
     <ContextMenu> 
      <MenuItem Header="Attribute"/> 
      <MenuItem Header="Setting"/> 
      <Separator/> 
      <MenuItem Header="Property"/> 
     </ContextMenu> 
    </Button.ContextMenu> 
</Button> 

attuale fonte Gist e l'esempio here.

+2

Questo dovrebbe essere contrassegnato come risposta, bella soluzione pulita! –

+0

Mi piacerebbe che si aprisse al clic e si chiudesse nuovamente al clic, come i menu a discesa sui pulsanti di divisione di Visual Studio. Questa implementazione si apre ogni volta che fai clic. Ho provato a impostare IsOpen =! IsOpen, e cambiando quando l'evento si innesca (ad esempio su PreviewMouseDown) ma sembra che il menu di scelta rapida sia già chiuso prima che raggiunga un evento click. Puoi risolvere questo mistero? Non sono nemmeno sicuro che possa essere fatto all'interno del comportamento. –

+0

Questa è una buona domanda. Siccome tu dici "IsOpen =! IsOpen" non funziona, potresti collegare agli eventi ContextMenu 'Open' e' Closed' in modo da poter determinare se il ContextMenu sia effettivamente aperto (assumendo che l'istanza ContextMenu di riferimento non cambi). Per quanto riguarda il motivo per cui ciò accade, presumo che una volta premuto nuovamente DropDownButton, tecnicamente ContextMenu perde lo stato attivo e si chiude, quindi il tentativo '! IsOpen' fallisce nel momento in cui viene eseguito il codice di comportamento. Sarebbe interessante vedere come Visual Studio lo fa in realtà. Dovrebbe essere molto più facile di così. – Ryan

8

Se hai il lusso di scegliere .NET 4 o più recente, la nuova libreria Ribbon ha un numero RibbonMenuButton che può farlo. In 4.5 è facile come fare riferimento a System.Windows.Controls.Ribbon nel progetto:

<RibbonMenuButton x:Name="ExampleMenu" SmallImageSource="/Images/Example.png"> 
    <RibbonMenuItem x:Name="ExampleMenuItem" Header="Save" /> 
</RibbonMenuButton> 
+0

Ribbon Library per WPF (include un collegamento per il download per .Net 4.0): https://msdn.microsoft.com/en-us/library/ff799534.aspx – Chris

Problemi correlati