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

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

Related

WPF how to set stackpanel as resources and reuse it in TabControls

I am new to C# and WPF so please give me some ideas:
I have an WPF app used to display some stack panels,all stack panels default Visibility is set to collapsed and they will switch to visible according to the received data.
Now I want to make all these stack panels to resources so I can reuse it in some new added tab controls and stack panels.
<StackPanel x:Name="ColorOption" Visibility="Collapsed">
<TextBlock Text="Line Color" Style="{StaticResource ItemNameTextBlockStyle}"/>
<Button Style="{StaticResource ColorButtonStyle}" Click="Color_Click">
<Button.Content>
<Rectangle x:Name="LineColorRect" Style="{StaticResource ColorSelectionRectangleStyle}" />
</Button.Content>
</Button>
</StackPanel>
Above is one example of stack panels I am using. In the code behind the function "Color_Click" will change this "ColorOption" stack panel state and do something.
However after I try to put this stack panel into Windows.Resources
<Window.Resources>
<StackPanel x:Name="ColorOption" Visibility="Collapsed" x:Key="ColorOption">
<TextBlock Text="Line Color" Style="{StaticResource ItemNameTextBlockStyle}"/>
<Button Style="{StaticResource ColorButtonStyle}" Click="Color_Click">
<Button.Content>
<Rectangle x:Name="LineColorRect" Style="{StaticResource ColorSelectionRectangleStyle}" />
</Button.Content>
</Button>
</StackPanel>
</Window.Resources> (I also put the style files inside)
In the tab controls I did
<TabControl>
<TabItem Header="Tab 1" Content="{StaticResource ColorOption}"/>
</TabControl>
The visual studio shows error in the code behind says "ColorOption does not exist in the current context"
How can I fix this? Is any way to set the context? thank you
You can simply wrap the StackPanel in ContentControl and make it ControlTemplate.
<ControlTemplate x:Key="ColorOptionTemplate" TargetType="{x:Type ContentControl}">
<StackPanel x:Name="ColorOption" Visibility="Collapsed">
<TextBlock Text="Line Color" Style="{StaticResource ItemNameTextBlockStyle}"/>
<Button Style="{StaticResource ColorButtonStyle}" Click="Color_Click">
<Button.Content>
<Rectangle x:Name="LineColorRect" Style="{StaticResource ColorSelectionRectangleStyle}"/>
</Button.Content>
</Button>
</StackPanel>
</ControlTemplate>
However, you will need to change properties of controls inside the ContentControl and it would be cumbersome. So the StackPanel could be wrapped in UserControl instead.
<UserControl x:Class="WpfApp1.ColorOptionControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel x:Name="ColorOption" Visibility="Collapsed">
<TextBlock Text="Line Color" Style="{StaticResource ItemNameTextBlockStyle}"/>
<Button Style="{StaticResource ColorButtonStyle}" Click="Color_Click">
<Button.Content>
<Rectangle x:Name="LineColorRect" Style="{StaticResource ColorSelectionRectangleStyle}"/>
</Button.Content>
</Button>
</StackPanel>
</UserControl>
This is a common way in WPF. The downside is that you will need to add dependency properties to UserControl and wire up them with dependency properties of internal controls so that you can set their values at the level of UserControl and bridge them with external controls and window. This could be complicated and cumbersome as well.
So I think ideally it would be better to find an existing control which has similar functionalities you want and create a custom control deriving from the existing one.

TextBlock inside MenuItem does not became gray if MenuItem is Disabled

If I use this code:
<MenuItem x:Name="MenuSave" Header="Save" IsEnabled="False"/>
when MenuSave is disabled (in real code by a RoutedUICommand programmatically), the Header is disabled and text is gray.
But if I need more text like here:
<MenuItem x:Name="MenuSaveAs" IsEnabled="False">
<MenuItem.Header >
<StackPanel Orientation="Horizontal">
<TextBlock Text="Save as"/>
<TextBlock> ...</TextBlock>
</StackPanel>
</MenuItem.Header>
</MenuItem>
In this case, the Header is disabled but text is not gray.
How can I obtain text gray with composite text?
This is just simplified code to explain the problem, the real code is combination of translated terms.
If you add the TextBlock thorough a HeaderTemplate, the color will be applied for the disabled state. By the way, you can use multiple Runs instead, so the same TextBlock is populated. If you bind a data item as Header, you can bind its properties in the template to the Runs.
<MenuItem x:Name="MenuSaveAs" IsEnabled="False">
<MenuItem.HeaderTemplate>
<DataTemplate>
<TextBlock>
<Run Text="Save as"/>
<Run> ...</Run>
</TextBlock>
</DataTemplate>
</MenuItem.HeaderTemplate>
</MenuItem>
Alternatively, if you need to format a string with a bound property, use HeaderStringFormat.
<MenuItem x:Name="MenuSaveAs"
IsEnabled="False"
Header="{Binding NameOfTheSavedItem}"
HeaderStringFormat="Save as {0}...">
If you really insist on setting the header directly, a workaround would be to bind the Foreground of TextBlock explicitly to the TextElement.Foreground of the ContentPresenter in the MenuItem control template. You can bind it on each TextBlock or add an implicit style that applies to all TextBlocks in scope automatically. Please note the word all.
<Menu>
<Menu.Resources>
<Style TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}">
<Setter Property="Foreground" Value="{Binding (TextElement.Foreground), RelativeSource={RelativeSource AncestorType=ContentPresenter}}"/>
</Style>
</Menu.Resources>
<MenuItem x:Name="MenuSaveAs"
IsEnabled="True">
<MenuItem.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Padding="0" Text="Save as"/>
<TextBlock Padding="0" Text="..."/>
</StackPanel>
</MenuItem.Header>
</MenuItem>
</Menu>
Please be aware that I suggest to use a single TextBlock with Runs for sentences or paragraphs in general, because panels with multiple TextBlocks result in incorrect spacing and alignment that do not match the typesetting that TextBlock and other document related types provide. It usually looks odd and disjointed and does not take into account the characteristics of a font.
I found an other easy way using <Label Padding="0"..>, instead af <TextBlock ..>:
<MenuItem x:Name="MenuSaveAs" IsEnabled="False">
<MenuItem.Header >
<StackPanel Orientation="Horizontal">
<Label Padding="0" Content="Save as"/>
<Label Padding="0" Content="..."/>
</StackPanel>
</MenuItem.Header>

How to fix "materialDesign" is not defined in wpf/xaml

I recently added material design to my wpf project. Everything worked fine so far, untill I tried to add a dialog.
https://gyazo.com/13e65442cdd241108fa397bd1c5695e6
I googled the whole internet off but could not find a solution
<StackPanel
VerticalAlignment="Center">
<!--the request to open the dialog will bubble up to the top-most DialogHost, but we can used the attached property based event to handle the response -->
<Button
Command="{x:Static materialDesign:DialogHost.OpenDialogCommand}"
materialDesign:DialogHost.DialogClosingAttached="Sample2_DialogHost_OnDialogClosing"
Width="128">
<Button.CommandParameter>
<StackPanel
Margin="16">
<ProgressBar
Style="{DynamicResource MaterialDesignCircularProgressBar}"
HorizontalAlignment="Center"
Margin="16"
IsIndeterminate="True"
Value="0" />
<Button
Style="{StaticResource MaterialDesignFlatButton}"
IsCancel="True"
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
CommandParameter="Sample2Cancel"
HorizontalAlignment="Center">
CANCEL
</Button>
</StackPanel>
</Button.CommandParameter>
PASS VIEW
</Button>
<Button
Command="{x:Static materialDesign:DialogHost.OpenDialogCommand}"
Width="128"
Margin="0 32 0 0">
<Button.CommandParameter>
<!-- the simplest view model of all, a DateTime. the view can be found in the resources of MainWindow.xaml -->
<system:DateTime xmlns:system="clr-namespace:System;assembly=mscorlib">
1966-JUL-30
</system:DateTime>
</Button.CommandParameter>
PASS MODEL
</Button>
</StackPanel>
Getting the following error:
https://gyazo.com/c3038b28c90ebd81c94bea5a7bc9b671
I expected to have a dialog like in the demo of material design toolkit.
https://gyazo.com/435b4136a05678455386848548c9c6de
I had to add: xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
at the top of the xaml file

How to get menu items command CanExecute to apply when using images and text

If I convert my menu code from:
<MenuItem Header="Remove Special Event"
Command="{Binding RemoveSpecialEventCommand, Mode=OneWay}" />
To:
<MenuItem Command="{Binding RemoveSpecialEventCommand, Mode=OneWay}">
<MenuItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="Images/RemoveSpecialEvent.png" Stretch="None" Margin="0,0,5,0"/>
<TextBlock>Remove Special Event</TextBlock>
</StackPanel>
</MenuItem.Header>
</MenuItem>
Now the Command CanExecute is not being applied to the menu item. They are all enabled.
Update
I don't particularly expect this to work.
It worked!
I can't reproduce your issue, and I'm going to take you at your word that it's absolutely disabled, but yet does not gray out the text. I suppose it might be a theme thing.
Here's one thing you can try: Instead of giving the Header property a visual tree fragment, try templating the plain string header content instead.
<MenuItem
Header="Remove Special Event"
Command="{Binding RemoveSpecialEventCommand, Mode=OneWay}"
>
<MenuItem.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image
Source="Images/RemoveSpecialEvent.png"
Stretch="None"
Margin="0,0,5,0"
/>
<ContentControl
Content="{Binding}"
/>
</StackPanel>
</DataTemplate>
</MenuItem.HeaderTemplate>
</MenuItem>
I don't particularly expect this to work.

WPF button with drop down list and arrow

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.

Resources