i have an observable collection as ItemSource for my menuitems.
i want a simple button (see commented part) at end of menu item list that can add new item to this collection.
it seem simple But it throw a System.Windows.Markup.XamlParseException in my code telling the collection is already in use.
what is the correct way to achieve this?
<Menu Background="Transparent">
<MenuItem Header="WorkSpace" Background="Transparent" ItemsSource="{Binding NosWorkSpaces}">
<ItemsControl.ItemTemplate>
<DataTemplate >
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Original.Title}"></Label>
<Button Content="Select" Tag="WorkSpace_Load" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=mah:MetroWindow}, Path=DataContext.SelectWorkspaceCommand}" CommandParameter="{Binding }" />
<Button Content="Load" Tag="WorkSpace_Load" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=mah:MetroWindow}, Path=DataContext.LoadBinaryWorkspace}" CommandParameter="{Binding }" />
<Button Content="Save" Tag="WorkSpace_Save" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=mah:MetroWindow}, Path=DataContext.SaveWorkspaceCommand}" CommandParameter="{Binding }" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
<!-- <Button Content="New " /> this wont work -->
</MenuItem>
</Menu>
Well i found a solution mixing some answer, even i find it not so easy for a simple menu.
<Menu Height="24" VerticalAlignment="Top">
<Menu.Resources>
<CollectionViewSource Source="{Binding NosWorkSpaces}" x:Key="YourMenuItems"/>
</Menu.Resources>
<MenuItem Header="WorkSpaces" >
<MenuItem.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection="{Binding Source={StaticResource YourMenuItems}}" />
<Separator></Separator>
<MenuItem Header="Add Worspace" />
</CompositeCollection>
</MenuItem.ItemsSource>
<MenuItem.ItemTemplate>
<DataTemplate >
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Original.Title}"></Label>
<Button Content="Select" Tag="WorkSpace_Load" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=mah:MetroWindow}, Path=DataContext.SelectWorkspaceCommand}" CommandParameter="{Binding }" />
<Button Content="Load" Tag="WorkSpace_Load" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=mah:MetroWindow}, Path=DataContext.LoadBinaryWorkspace}" CommandParameter="{Binding }" />
<Button Content="Save" Tag="WorkSpace_Save" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=mah:MetroWindow}, Path=DataContext.SaveWorkspaceCommand}" CommandParameter="{Binding }" />
</StackPanel>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
</Menu>
giving a mixed menu with itemSource and datatemplate.
In my code I have:
<ListBox Name="Playlists_ListBox">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0,2" >
<Grid.ContextMenu>
<ContextMenu Name="cm" StaysOpen="true" >
<MenuItem Header="Delete"/>
</ContextMenu>
</Grid.ContextMenu>
<TextBlock Name="Name" Text="{Binding Title}" Foreground="White"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
When I want now open context menu, I must click only on TextBlock. How can I do it to open context menu by click on any part of listbox item - one line in listbox?
Add Context menu for your ListBox so that you'll get context menu wherever you click on Listbox
<ListBox Name="Playlists_ListBox">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0,2" >
<TextBlock Name="Name" Text="{Binding Title}" Foreground="White"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ContextMenu>
<ContextMenu StaysOpen="True">
<MenuItem Header="Delete"/>
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
My Application consists of a MainWindow with a ContentControl and I change the ViewModel depending on the selected menu.
One of the UserControls I display as content contains the following WrapPanel:
<UserControl ...>
<Grid>
<WrapPanel>
<ItemsControl ItemsSource="{Binding Connections}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Command="{Binding DataContext.ConnectionSelectCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding}"
FocusManager.FocusedElement="{Binding ElementName=InstanceName}"
Style="{DynamicResource DashboardButton}">
<TextBlock TextWrapping="Wrap" HorizontalAlignment="Center" Text="{Binding Name}" />
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Delete"
Command="{Binding ConnectionRemoveCommand}"
CommandParameter="{Binding}" />
</ContextMenu>
</Button.ContextMenu>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</WrapPanel>
</Grid>
</UserControl>
The Command on the ContextMenu doesn't work because it tries to call ConnectionRemoveCommand on the Connection object instead of the ConnectionViewModel which is the DataContext of the UserControl.
How do I bind the Command to the ConnectionViewModel with the CommandParameter being the Connection object?
If you bind the Tag property of the Button to the DataContext of the ItemsControl, you could then bind to it using the PlacementTarget of the ContextMenu:
<Button Command="{Binding DataContext.ConnectionSelectCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding}"
FocusManager.FocusedElement="{Binding ElementName=InstanceName}"
Style="{DynamicResource DashboardButton}"
Tag="{Binding DataContext, RelativeSource={RelativeSource AncestorType=ItemsControl}}">
<TextBlock TextWrapping="Wrap" HorizontalAlignment="Center" Text="{Binding Name}" />
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Delete"
Command="{Binding PlacementTarget.Tag.ConnectionRemoveCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
CommandParameter="{Binding}" />
</ContextMenu>
</Button.ContextMenu>
</Button>
I'm learning WPF/MVVM and got stuck on following...
The code below is working
<ListBox x:Name="listbox" DockPanel.Dock="Top" ItemsSource="{Binding Items}" DisplayMemberPath="Name" >
<ListBox.ContextMenu>
<ContextMenu DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Show Selected"
Command="{Binding Path=DataContext.ShowSelectedCommand}"
CommandParameter="{Binding Path=SelectedItems}"/>
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
but when I've replaced listbox on treeview, such as
<TreeView x:Name="tview" DockPanel.Dock="Top" DisplayMemberPath="Name" Tag="{Binding DataContext, RelativeSource={RelativeSource Self}}" ItemsSource="{Binding Items}" >
<TreeView.ContextMenu>
<ContextMenu DataContext="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource Self}}" StaysOpen="true">
<MenuItem Header="Show Selected" Command="{Binding DataContext.ShowSelectedCommand}"
CommandParameter="{Binding Path=SelectedItems}">
</MenuItem>
</ContextMenu>
</TreeView.ContextMenu>
</TreeView>
The ShowSelectedCommand doesn't invoked. Could you please explain what's wrong there and how can I make it work. Thanks a lot.
I've searched and read anything I can about ContextMenus and binding, and how it's not in the tree... etc. So searching feels like I've exhausted it and just don't understand it.
I'm trying to get my ContextMenu AddTournamentCommand to work, but I simply can't get it to command. I recently found out the easy way through Data Sources to bind to objects, so if there's an easy way other than coding it by hand to wire it up, please let me know. This is what I have so far:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:Models="clr-namespace:FumbblApiClient.Models" mc:Ignorable="d" x:Name="FumbblMainWindow" x:Class="FumbblApiClient.MainWindow"
Title="MainWindow" Height="499.45" Width="639" Loaded="Window_Loaded">
<Window.Resources>
<CollectionViewSource x:Key="groupViewSource" d:DesignSource="{d:DesignInstance {x:Type Models:Group}, CreateList=True}"/>
<CollectionViewSource x:Key="groupTournamentsViewSource" Source="{Binding Tournaments, Source={StaticResource groupViewSource}}"/>
</Window.Resources>
<Grid Margin="0,0,2,0">
<TabControl Margin="10">
<TabItem Header="Groups">
<Grid Background="#FFE5E5E5" DataContext="{StaticResource groupViewSource}">
<TextBox x:Name="GroupIdTextBox" HorizontalAlignment="Left" Height="23" Margin="10,10,0,0" TextWrapping="Wrap" Text="Group ID" VerticalAlignment="Top" Width="100" Grid.Column="1"/>
<Button Content="Fetch" HorizontalAlignment="Left" Margin="115,11,0,0" VerticalAlignment="Top" Width="61" Click="GroupFetch_Click" Grid.Column="1" Height="22"/>
<ListBox x:Name="groupListView" ItemsSource="{Binding}" Margin="10,38,0,10" SelectionMode="Single" HorizontalAlignment="Left" Width="166" SelectionChanged="GroupList_SelectionChanged">
</ListBox>
<Grid x:Name="grid1" Margin="181,38,10,0" VerticalAlignment="Top" Height="369">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Content="Id:" Grid.Column="0" HorizontalAlignment="Left" Margin="3" Grid.Row="0" VerticalAlignment="Center"/>
<TextBox x:Name="idTextBox" Grid.Column="1" HorizontalAlignment="Left" Height="23" Margin="3" Grid.Row="0" Text="{Binding Id, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}" VerticalAlignment="Center" Width="120"/>
<Label Content="Name:" Grid.Column="0" HorizontalAlignment="Left" Margin="3" Grid.Row="1" VerticalAlignment="Center"/>
<TextBox x:Name="nameTextBox" Grid.Column="1" HorizontalAlignment="Left" Height="23" Margin="3" Grid.Row="1" Text="{Binding Name, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}" VerticalAlignment="Center" Width="120"/>
<Label Content="Tournaments:" HorizontalAlignment="Left" Margin="3" Grid.Row="2" VerticalAlignment="Center"/>
<ListBox x:Name="tournamentsListView" ItemsSource="{Binding Source={StaticResource groupTournamentsViewSource}}" Margin="3,3,-182,-260" SelectionMode="Multiple" Grid.Row="2" Grid.Column="1">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<EventSetter Event="UIElement.PreviewMouseRightButtonDown" Handler="EmptyHandler"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Add To Selected Tournaments" Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=PlacementTarget.DataContext.AddTournamentCommand}"/>
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
</Grid>
</Grid>
</TabItem>
<TabItem Header="Tournaments">
<Grid Background="#FFE5E5E5" Margin="0,0,0,-2">
<ListBox HorizontalAlignment="Left" Margin="10,10,0,10" Width="166"/>
</Grid>
</TabItem>
<TabItem Header="Teams">
</TabItem>
<Grid Margin="0,0,-10,10"/>
</TabControl>
</Grid>
</Window>
and in the Code Behind:
public partial class MainWindow : Window
{
[removed]
private ICommand addTournamentCommand;
public ICommand AddTournamentCommand
{
get
{
if(addTournamentCommand == null)
{
addTournamentCommand = new RelayCommand(OnTournamentAdded);
}
return addTournamentCommand;
}
}
private void OnTournamentAdded(object state)
{
}
}
PlacementTarget property is on Context Menu and not on window. Travel to ContextMenu and not to Window. Window anyhow doesn't lies in Visual Tree of ContextMenu so you can't reach to it using RelativeSource.
<ContextMenu>
<MenuItem Header="Add To Selected Tournaments"
Command="{Binding RelativeSource={RelativeSource
AncestorType={x:Type ContextMenu}},
Path=PlacementTarget.DataContext.AddTournamentCommand}"/>
</ContextMenu>
With above code you will get PlacementTarget's dataContext which will be ListBox's DataContext and if you haven't set explicitly DataContext on ListBox, it will inherit it from Window and your code will work fine.
UPDATE
You can store the Window DataContext in Tag of ListBox and bind with it.
<ListBox Tag="{Binding DataContext,
RelativeSource={RealtiveSource Mode=FindAncestor,
AncestorType=Window}}"/>
and in ContextMenu bind using Tag:
<ContextMenu>
<MenuItem Header="Add To Selected Tournaments"
Command="{Binding RelativeSource={RelativeSource
AncestorType={x:Type ContextMenu}},
Path=PlacementTarget.Tag.AddTournamentCommand}"/>
</ContextMenu>
Change like this,
<ContextMenu>
<MenuItem Header="Add To Selected Tournaments" Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=AddTournamentCommand}"/>
</ContextMenu>