WPF button with drop down list and arrow - wpf

Can someone suggest the best way to have a button with an arrow and dropdown list like in visual studio toolbar button new item. As you can find in VS the mouse hover is highlighting both default button and arrow button and after selecting an item from list the default button is changing according your selection.
Here is a piece of code which is showing drop down menu, but not for full functionality described above:
<StackPanel Orientation="Horizontal">
<Border CornerRadius="0" BorderBrush="Black" BorderThickness="1">
<Button Name="CreateButton" Click="CreateButton_Click" Background="Transparent" BorderBrush="{x:Null}">
<Image Source="/OMS.Resources;component/Resources/Images/LibraryImages/add1.png" />
<Button.ContextMenu>
<ContextMenu HorizontalAlignment="Right">
<MenuItem Header=" doc" Click="CreateDocButton_Click">
<MenuItem.Icon>
<Image Source="/OMS.Resources;component/Resources/Images/LibraryImages/add_sheet.png" Width="24" Height="24" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header=" xls" Click="CreateXlsButton_Click">
<MenuItem.Icon>
<Image Source="/OMS.Resources;component/Resources/Images/LibraryImages/add_sheet.png" Width="24" Height="24" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header=" txt" Click="CreateTxtButton_Click">
<MenuItem.Icon>
<Image Source="/OMS.Resources;component/Resources/Images/LibraryImages/add_sheet.png" Width="24" Height="24" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</Button.ContextMenu>
</Button>
</Border>
<Border CornerRadius="0" BorderBrush="Black" BorderThickness="1">
<Button HorizontalAlignment="Left" VerticalAlignment="Stretch" Background="Transparent" BorderBrush="{x:Null}"
ContextMenuService.IsEnabled="False" Click="AddButtonContextMenu_Click">
<Image Source="/OMS.Resources;component/Resources/Images/LibraryImages/arrow_down.png" VerticalAlignment="Center" Width="9" />
</Button>
</Border>
</StackPanel>

The solution is to make use a menu item and decorate it.
XAML Code:
<MenuItem Click="AddPresetButton_Click" x:Name="AddPresetButton">
<MenuItem.Icon>
<Image Source="/MyApp.Application;component/Resources/add.png" Height="20"/>
</MenuItem.Icon>
<MenuItem.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Add Preset"/>
<Image Source="/MyApp.Application;component/Resources/arrow_down_simple.png"
Height="10" Margin="2,0,0,0"/>
</StackPanel>
</MenuItem.Header>
<MenuItem.ContextMenu>
<ContextMenu>
<MenuItem Header="Add 1"/>
<MenuItem Header="Add 2"/>
<MenuItem Header="Add 3"/>
</ContextMenu>
</MenuItem.ContextMenu>
</MenuItem>
C# Code:
When the menu is pressed the context menu is opened.
private void AddPresetButton_Click(object sender, RoutedEventArgs e)
{
var addButton = sender as FrameworkElement;
if (addButton != null)
{
addButton.ContextMenu.IsOpen = true;
}
}

It looks like you have three problems to solve:
Styling / Layout
Highlight dropdown and button OnMouseOver
Change default button according to menu's last selection
Styling / Layout
Here are a couple of examples:
http://dvoituron.wordpress.com/2011/01/06/toolbar-dropdownbutton-in-wpf/
http://blogs.msdn.com/b/llobo/archive/2006/10/25/split-button-in-wpf.aspx
I am sure there are many other ways (e.g. using a plain button and ComboBox styled appropriately)
Highlighting dropdown and button OnMouseOver
Experiment with triggers; e.g:
WPF Mouseover Trigger Effect for Child Controls
WPF - How to change children's style on mouseover of parent
Change default button according to menu's last selection
Try the MVVM approach:
The button element will be bound to a property on your ViewModel. Each menu item will call an action (ICommand) in your ViewModel. This ViewModel will know which menu item was called, and update the button's property on the ViewModel. The button will automatically update using data binding.

Related

Assign event handler to ContextMenu item defined in custom style

I have created Context menu style with predefined MenuItems in it:
<Style TargetType="{x:Type ContextMenu}" x:Key="DataGridColumnFilterContextMenu">
<Setter Property="ContextMenu.Template">
<Setter.Value>
<ControlTemplate>
<Border BorderBrush="#868686"
BorderThickness="1"
Background="#FAFAFA">
<Grid MaxHeight="500">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel>
<MenuItem Header="Filtrēt" StaysOpenOnClick="True" Name="DynSearch">
<MenuItem.Icon>
<Image RenderOptions.BitmapScalingMode="NearestNeighbor"
RenderOptions.EdgeMode="Aliased"
Source="/Furniture;component/Resources/search4.png" />
</MenuItem.Icon>
</MenuItem>
<Separator Margin="20 5 20 0" Height="2" Width="Auto" />
<MenuItem Margin="5 5 0 0" StaysOpenOnClick="True" >
<MenuItem.Template>
<ControlTemplate >
<CheckBox Padding="15 0 0 0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >Iezīmēt/Atzīmēt visus</CheckBox>
</ControlTemplate>
</MenuItem.Template>
<MenuItem.Icon>
<Image RenderOptions.BitmapScalingMode="NearestNeighbor"
RenderOptions.EdgeMode="Aliased"
Source="/Furniture;component/Resources/search4.png" />
</MenuItem.Icon>
</MenuItem>
<Separator Margin="20 5 20 0" Height="2" Width="Auto" />
<RadioButton Margin="5 5 0 0" Padding="15 0 0 0" GroupName="Sorting" Content="Augoša secībā" IsChecked="True"/>
<RadioButton Margin="5 5 0 0" Padding="15 0 0 0" GroupName="Sorting" Content="Dilstoša secībā" />
<Separator Margin="20 5 20 5" Height="2" Width="Auto" />
</StackPanel>
<ScrollViewer Grid.Row="1"
Margin="1,0,1,0"
CanContentScroll="True"
VerticalScrollBarVisibility="Auto"
Grid.ColumnSpan="2">
<ItemsPresenter KeyboardNavigation.DirectionalNavigation="Cycle" />
</ScrollViewer>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Now I assigned style to new ContextMenu and wanted to assign Click event handler to MenuItem defined in style with header "Filtrēt". I tried do it like following:
ContextMenu cm = new ContextMenu();
cm.Style = Application.Current.Resources["DataGridColumnFilterContextMenu"] as Style;
var controlList = ((((cm.Template.LoadContent() as Border).Child as Grid).Children)[0] as StackPanel).Children;
MenuItem filterItem = (controlList[0] as MenuItem);
filterItem.Click += MiFiltre_Click;
And when I clicked debugger It didn't come to MiFiltre_Click method. I tried different events like MouseDown and PreviewMouseDown. Also I tried to bound ICommand and this also didn't work.
Then I searched this question and realized that cm.Template.LoadContent() may actually create new instance of my ContextMenu template and I am trying to bound my event handler to different control instance. Then I tried to get ContextMenu node controls with VisualTreeHelper and LogicalTreeHelper and this also didn't work.
So here are questions:
How to achieve Click event handler binding?
If 1st question is too
hard, then how to get ContextMenu childrens current instance defined
in custom style?
I'm going to assume this menu Style is defined in a resource dictionary named "MyResources.xaml".
Create a Class file in the same project folder, and call it "MyResources.xaml.cs".
Make it a partial class like this:
namespace WhateverNamespace
{
public partial class MyResources
{
private void FilterMenuItem_Click(object sender, RoutedEventArgs e)
{
MenuItem mi = (MenuItem)sender;
// Below, PlacementTarget is the control the user right-clicked on.
// Use that directly if you want that instead of its viewmodel.
// This works with the conventional ContextMenu defined below.
//ViewModel viewModel = ((FrameworkElement)((ContextMenu)mi.Parent).PlacementTarget).DataContext as ViewModel;
// This works with your template design. The only difference is
// TemplatedParent instead of Parent.
ViewModel viewModel =
((FrameworkElement)((ContextMenu)mi.TemplatedParent).PlacementTarget)
.DataContext as ViewModel;
MessageBox.Show("FilterMenuItem_Click");
}
}
}
Give the resource dictionary an x:Class attribute:
<ResourceDictionary
x:Class="WhateverNamespace.MyResources"
Wire up the handler:
<MenuItem
Click="FilterMenuItem_Click"
Header="Filtrēt"
StaysOpenOnClick="True"
Name="DynSearch">
By the way, you don't need to mess with styles and templates to create a ContextMenu resource:
<ContextMenu x:Key="DataGridColumnFilterContextMenu">
<MenuItem
Click="FilterMenuItem_Click"
Header="Filtrēt"
StaysOpenOnClick="True"
Name="DynSearch">
<MenuItem.Icon>
<Image
RenderOptions.BitmapScalingMode="NearestNeighbor"
RenderOptions.EdgeMode="Aliased"
Source="/Furniture;component/Resources/search4.png"
/>
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem StaysOpenOnClick="True">
<MenuItem.Header>
<CheckBox>Iezīmēt/Atzīmēt visus</CheckBox>
</MenuItem.Header>
<MenuItem.Icon>
<Image
RenderOptions.BitmapScalingMode="NearestNeighbor"
RenderOptions.EdgeMode="Aliased"
Source="/Furniture;component/Resources/search4.png"
/>
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem>
<MenuItem.Header>
<RadioButton GroupName="Sorting" Content="Augoša secībā" IsChecked="True"/>
</MenuItem.Header>
</MenuItem>
<MenuItem>
<MenuItem.Header>
<RadioButton GroupName="Sorting" Content="Dilstoša secībā" IsChecked="True"/>
</MenuItem.Header>
</MenuItem>
</ContextMenu>

Should I use a ContextMenu to show a couple of buttons?

I am developing a WPF application and I want the following functionality: If a user right clicks on a progress bar a small context menu should popup at the clicked position. This menu should just contain a couple of buttons which are lined up horizontally. Should I use the ContextMenu for this or are there better suitable WPF elements?
I tried a ContextMenu and this is how it looks like:
This is the XAML:
<ProgressBar x:Name="PgF" Height="10" Value="{Binding Path=FileCurrentMs}" Maximum="{Binding Path=FileLengthMs}">
<ProgressBar.ContextMenu>
<ContextMenu>
<StackPanel Orientation="Horizontal">
<Button Content="A"/>
<Button Content="B"/>
<Button Content="C"/>
</StackPanel>
</ContextMenu>
</ProgressBar.ContextMenu>
</ProgressBar>
In the ContextMenu I have the space to the left and to the right which I don’t want and I read in other posts that it is not simple just to remove this space. Any ideas?
Try like this :
<ProgressBar x:Name="PgF" Height="10" Value="{Binding Path=FileCurrentMs}" Maximum="{Binding Path=FileLengthMs}">
<ProgressBar.ContextMenu>
<ContextMenu>
<MenuItem>
<MenuItem.Template>
<ControlTemplate>
<StackPanel Orientation="Horizontal">
<Button Content="A" Margin="2"/>
<Button Content="B" Margin="2"/>
<Button Content="C" Margin="2"/>
</StackPanel>
</ControlTemplate>
</MenuItem.Template>
</MenuItem>
</ContextMenu>
</ProgressBar.ContextMenu>
</ProgressBar>
You need to put all buttons in a single menu item :) good luck

C# WPF -> context menu bounded command won't be called

I have a little problem with wpf.
I have a listbox and in it a context menu,
but the bounded command won't be called if I am click on a menu item.
I think there is an error with the scope, but I dont have an idia to solve this problem, so I hope, that some guy here can help me.
Here is my code from the ui:
<ListBox Name="CompanyListBox" Margin="5" ItemsSource="{Binding Path=Companys}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border CornerRadius="10" Background="LightGray" Margin="5">
<Border.ContextMenu>
<ContextMenu>
<MenuItem Header="Akt. Item in einem neuen Fenster öffnen" Command="{Binding Path=OpenInNewWindowCommand}">
<MenuItem.Icon>
<Image Width="24" Height="24" Source="path/to/icon/icon.ico"></Image>
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</Border.ContextMenu>
<StackPanel Orientation="Vertical" Margin="5">
//Here is the layout of the item
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And here the code from the viewmodel:
public ICommand OpenInNewWindowCommand
{
get
{
return new RelayCommand(OpenInNewWindow);
}
}
Yes, the DataContext are be setted to the view model, everything works excpet the context menu.
The method "OpenInNewWindow" will be not called from the ui if I clicked on the menu item, why?
(Sorry for bad englisch ;) I´m not from there ^^)

How to remove the default border around a group of subitems?

I'm trying to find a way to get rid of the border around the container of the MenuItems of a menu, in Expression Blend 4.
Here is an image of what I mean, I'd like to make the big white border around Item2 and Item3 disappear.
http://i.stack.imgur.com/dhOwY.png
And here is the XAML of this menu:
<Menu Background="{DynamicResource MenuGradient}" Margin="8,3,0,0" Height="26" VerticalAlignment="Top" ItemTemplate="{DynamicResource GeneratedMenuItem}">
<MenuItem x:Name="itm1" Header="Item1" FontSize="16" Foreground="White" Template="{DynamicResource CustomMenuItemStyle}">
<MenuItem x:Name="itm2" Header="Item2" Style="{DynamicResource CustomSubMenuItemStyle}"/>
<MenuItem x:Name="itm3" Header="Item3" Style="{DynamicResource CustomSubMenuItemStyle}"/>
</MenuItem>
<MenuItem x:Name="itmOptions" Header="Options" Foreground="White" FontSize="16" Template="{DynamicResource CustomMenuItemStyle}"/>
</Menu>
I think you need to edit the popup part inside the template of the menu item.

WPF menu item with image

How to define MenuItem.Icon so that the MenuItemHeader text would be placed below the menu item image?Thanks for help!
How something along the lines of:
<ContextMenu>
<MenuItem Header="Reports">
<MenuItem.Icon>
<Image Source="/XSoftArt.WPFengine;component/Images/export32x32xp.png"/>
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
The easy way way is to not use the Icon property but to instead put the icon in the Header:
<Menu>
<MenuItem>
<MenuItem.Header>
<StackPanel>
<Image Width="20" Height="20" Source="/XSoftArt.WPFengine;component/Images/export32x32xp.png" />
<ContentPresenter Content="Reports" />
</StackPanel>
</MenuItem.Header>
</MenuItem>
<MenuItem Header="Export" />
<MenuItem Header="New record" />
</Menu>
For this simple case the <ContentPresenter Content="Reports" /> can be replaced with a <TextBlock Text="Reports" /> because that's what ContentPresenter would use to present the string anyway. For more complex Header=, you could use the ContentPresenter as shown.
In the case of StackPanel use Label and not the TextBlock since only Label will allow you to have the mnemonics on the menu, like _Reports.

Resources