Hide ContextMenu when Menu has no items - c#

I have created a ContextMenu with a Menu inside it. The Menu doesn't always have items. When there are no items I want to hide the ContextMenu.
Relevant code in my style (generic.xaml)
<Grid.ContextMenu>
<ContextMenu Margin="10,10,0,13" Name="ContextMenu" HorizontalAlignment="Left" VerticalAlignment="Top" IsOpen="False">
<Menu>
<Menu.ItemsSource>
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="ChildCommands"/>
</Menu.ItemsSource>
<Menu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="MenuItem.Header" Value="{Binding Command.Text}"/>
<Setter Property="MenuItem.IsEnabled" Value="False"/>
</Style>
</Menu.ItemContainerStyle>
<Menu.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</Menu.ItemsPanel>
</Menu>
</ContextMenu>
</Grid.ContextMenu>
How can I hide the ContextMenu when there are no menuitems inside it?
EDIT : I fixed it by changing the Menu tags to ContextMenu tags.

Use the visibility property by the context menu. Too hide the context menu bind the menuitemscount and use a converter to convert the Count to the visibility enum.

I changed the Menu tags to ContextMenu tags and it works now. The ContextMenu is only visible when there are items in it.
Code:
<Grid.ContextMenu>
<ContextMenu Margin="10,10,0,13" Name="ContextMenu" HorizontalAlignment="Left" VerticalAlignment="Top" IsOpen="False">
<ContextMenu.ItemsSource>
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="ChildCommands"/>
</ContextMenu.ItemsSource>
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="MenuItem.Header" Value="{Binding Command.Text}"/>
<Setter Property="MenuItem.IsEnabled" Value="False"/>
</Style>
</ContextMenu.ItemContainerStyle>
<ContextMenu.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ContextMenu.ItemsPanel>
</ContextMenu>
</Grid.ContextMenu>

Related

Bind Style-Resource to StackPanel items

I am trying to bind a a Style-Resource which is defined in the local ResourceDictionary of a UserControl to all items in a StackPanel.
The ItemSource of the StackPanel is bound to an ObservableCollection<Button> in the ViewModel.
Thus, the aim is to bind the Style-Resource to the Style-Property of these Buttons.
The following simplified approach results in this error:
ArgumentException: Style object is not allowed to affect the Style property of the object to which it applies.
MyViewModel.cs:
public class MyViewModel
{
public ObservableCollection<Button> MyButtons { get; private set; }
}
MyView.xaml
<UserControl x:Class="MyView"
d:DataContext="{d:DesignInstance Type=MyViewModel}">
<UserControl.Resources>
<ResourceDictionary>
<Style x:Key="StyleStackPanelButton" TargetType="{x:Type Button}"
BasedOn="{StaticResource MyDefaultStyle}">
<Setter Property="Margin" Value="15" />
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
</ResourceDictionary>
</UserControl.Resources>
<StackPanel>
<ItemsControl ItemsSource="{Binding MyButtons}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Style" Value="{StaticResource StyleStackPanelButton}" />
</Style>
</StackPanel.Resources>
</StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<Button Style="{StaticResource StyleStackPanelButton}" />
</StackPanel>
</UserControl>
I've also tried to use a Converter as suggested here
Binding for WPF Styles
as follows:
...
<ItemsControl ItemsSource="{Binding ButtonExtensions}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Style">
<Setter.Value>
<MultiBinding Converter="{StaticResource StyleConverter}">
<MultiBinding.Bindings>
<Binding RelativeSource="{RelativeSource Self}" />
<Binding Source="{StaticResource StyleStackPanelButton}" />
</MultiBinding.Bindings>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</StackPanel.Resources>
</StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
...
This results in the same error:
ArgumentException: Style object is not allowed to affect the Style property of the object to which it applies.
Is there a any other way to bind a lcoal Style-Resource to a Setter-Property of an Item in a Stackpanel-ItemSource?
Since you are adding Buttons directly to the ItemsSource collection, it is sufficient to assign the Button Style to the ItemContainerStyle property of the ItemsControl:
<ItemsControl ItemsSource="{Binding MyButtons}"
ItemContainerStyle="{StaticResource StyleStackPanelButton}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Alternatively, declare a default Button Style - without x:Key - in the StackPanel Resources:
<ItemsControl ItemsSource="{Binding MyButtons}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<Style TargetType="Button"
BasedOn="{StaticResource StyleStackPanelButton}"/>
</StackPanel.Resources>
</StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>

Binding cannot find source when trying to access the datacontext of a custom listview contextmenu

I got a very similar problem with ContextMenu for ListViewItem only but the solution doesn't seem to work in my case.
<GroupBox Header="PlayOffs" Grid.Row="2" Grid.Column="0">
<StackPanel Orientation="Vertical" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ListView Grid.Column="0" ItemsSource="{Binding PlayoffSeries}" ItemTemplate="{StaticResource SeriesTemplate}">
<ListView.Resources>
<ContextMenu x:Key="cm">
<MenuItem Header="SetLive" Click="SetSeriesLive"/>
<MenuItem Header="Start" IsEnabled="{Binding Path=DataContext.CanStart, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}" Click="StartSeries"/>
</ContextMenu>
</ListView.Resources>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="ContextMenu" Value="{StaticResource cm}"/>
<EventSetter Event="MouseDoubleClick" Handler="HandleDoubleClick" />
</Style>
</ListView.ItemContainerStyle>
</ListView>
</ScrollViewer>
</StackPanel>
</GroupBox>
I get an error for the IsEnabled binding of the MenuItem Start saying Cannot find source:RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ListView',AncestorLevel='1'
The click command works like a charm, essentially what I want to do is to bind the IsEnabled property of the menuitem to the CanStart property of the underlying datacontext provided from the listviewItem.
I'm a dumdum. I just tried with AncestorType=ContextMenu and it seems to be working like a charm. Still not entirely sure why the first solution worked for the other question though. I suppose its related to the datamodel bound to the listview.

Context Menu Item for ListBox in WPF

I want to display ContextMenu on right click of ListBoxItems in WPF. I tried below code, but ContextMenu is displaying where ListBoxItems are not there also. What I want is to display ContextMenu only on the right click of only ListBoxItems.
<Grid>
<ListBox x:Name="listofConnectedItems" Grid.Column="0" Grid.Row="0" ItemsSource="{Binding MyItems}" MouseRightButtonDown="listofConnectedItems_MouseRightButtonDown" ContextMenuOpening="listofConnectedItems_ContextMenuOpening" >
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Padding" Value="10">
</Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="_Start" Click="MenuItemStart_Click" />
<MenuItem Header="Sto_p" Click="MenuItemStop_Click" />
<MenuItem Header="_Clear" Click="MenuItemClear_Click" />
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
</Grid>
`
Here I attached the screenshot.
Can anybody Please get me through this. Thanks in advance
It should be in IntemContainerStyle:
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="Open" />
<MenuItem Header="Edit" Command="Binding
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
I think the problem is that You didn't specified height and width of list box so it automaticaly scales to the window size. So technically Your code is working correctly but listbox is everywhere.
try with something like
<ListBox x:Name="listofConnectedItems" Grid.Column="0" Grid.Row="0" BorderThickness="5" Width="200" Height="200"...
and then rightclick outside the listbox

WPF "static" binding in a List

I have a problem with a binding in a List.
I have a List of objects. This List is bound to a ListBox. Of every object in my List I can open a ContextMenu:
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="First" IsEnabled="{Binding FirstEnabled}"/>
<MenuItem Header="Second" IsEnabled="{Binding SecondEnable}"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
In the code like this my objects in the list having the two booleans to bind. Now I want to bind this two booleans not to the objects. I want to bind it "static" to my DataContext. This is not working like this and I have no idea how to realize it.
I googled a lot but found nothing helpful ...
Thanks for helping!
Since, ContextMenu applies to ListBoxItem it will have its DataContext value and ListBoxItem will be its PlacementTarget. So if you want to bind to property of ListBox.DataContext you need to pass current ListBox.DataContext as, for example, Tag to ListBoxItem and then you need to refer to it from ContextMenu via its PlacementTarget. It's all because ContextMenu uses Popup which creates its own visual tree so simple RelativeSource/ElementName binding won't work
<Style TargetType="ListBoxItem">
<Setter Property="Tag" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBox}}, Path=DataContext}"/>
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="First" IsEnabled="{Binding RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}, Path=PlacementTarget.Tag.FirstEnabled}"/>
<MenuItem Header="Second" IsEnabled="{Binding RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}, Path=PlacementTarget.Tag.SecondEnable}"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
You can reference every datacontext with the ElementName Syntax:
<ListBox x:Name="myListBox">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="First" IsEnabled="{Binding Path=DataContext.FirstEnabled, ElementName=myListBox}"/>
<MenuItem Header="Second" IsEnabled="{Binding Path=DataContext.SecondEnable, ElementName=myListBox}"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
With this syntax you use the DataContext of your ListBox and not from the ListItem.

MultiBinding using DisplayMemeberBinding for MenuItem

I have the following XAML
<MenuItem Header="_Recent Studies"
Height="22"
ItemsSource="{Binding RecentFiles}">
<MenuItem.Resources>
<Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource {x:Type MenuItem}}">
<Setter Property="Header" Value="{Binding FullFileName}"/>
</Style>
</MenuItem.Resources>
</MenuItem>
Which displayed my recent files like
However, I want to display the item number of the MenuItem next to the file name like VS2012 does.
FileNameA.f
FileNameB.x
FileNameC.j
etc. To do this I decided to use a converter Where if I were just getting the number without the file name I could do this. But I want to incorporate this with multi-binding so I can write something like
<MultiBinding StringFormat="{}{0}. {1}">
<Binding Path="??"/>
<Binding Path="FullFileName"/>
</MultiBinding>
I don't know what to write in the above. How can I prefix my files names with the number that the file is in the list without adding an index property to my FullFileNames which would make things more complex?
Thanks for your time.
Edit. This is how I use the below answer in my code
<MenuItem Header="_FILE">
...
<MenuItem Header="_Recent Studies"
ItemsSource="{Binding RecentFiles}"
AlternationCount="{Binding RecentFiles.Count}"
HeaderTemplate="{x:Null}">
<MenuItem.Resources>
<Style TargetType="{x:Type MenuItem}"
BasedOn="{StaticResource {x:Type MenuItem}}">
<Setter Property="HeaderTemplate" >
<Setter.Value>
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}. {1}">
<Binding Path="(ItemsControl.AlternationIndex)"
RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type MenuItem}}"
Converter="{StaticResource IntPlusNConverter}"/>
<Binding Path="FullFileName"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</MenuItem.Resources>
</MenuItem>
<Separator/>
<MenuItem Header="E_xit"
Height="22"
Icon="{Binding Source={StaticResource Close},
Converter={StaticResource drawingBrushToImageConverter}}"
Command="{Binding ExitCommand}" />
</MenuItem>
This works! However, all my XAML for the FILE MenuItem block is being highlighted and I get a compile-time error (the code runs and works though!), saying
An object of the type "System.Windows.StaticResourceExtension" cannot be applied to a property that expects the type "System.Windows.Style".
Why is this happening and how can I resolve it?
Thanks for your time.
RESULT!
You should be able to do it with AlternationIndex like in this answer
Finding Listbox Index of ListBoxItem (ItemsTemplate to specify Visual COntent)
You may have to override the HeaderTemplate because the StringFormat may not work correctly because Header is an object not a string
Example:
<MenuItem Header="_Recent Studies" ItemsSource="{Binding RecentFiles}" AlternationCount="{Binding RecentFiles.Count}" HeaderTemplate="{x:Null}">
<MenuItem.Resources>
<Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource {x:Type MenuItem}}">
<Setter Property="HeaderTemplate" >
<Setter.Value>
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}. {1}">
<Binding Path="(ItemsControl.AlternationIndex)" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type MenuItem}}" />
<Binding Path="FullFileName"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</MenuItem.Resources>
</MenuItem>
Result:

Categories