WPF "static" binding in a List - c#

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.

Related

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.

DataContext binding on ContextMenu

I have a DataGrid with a ContextMenu, I'm trying to figure out how to properly bind the context.
So far I have read that the context menu its ouside the visual tree, and therefore the DataContext is different. With that in mind the provided solution is to use the property Tag, but I still cannot make it work:
<UserControl>
<!--#region DataGrid-->
<DataGrid ItemsSource="{Binding Model.Collection}"
Tag="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}, Path=DataContext}">
<!--#region Resources-->
<DataGrid.Resources>
<!--#region DataGridCell-->
<Style TargetType="DataGridCell">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Open Details"
Command="{Binding DataContext.OpenRowDetailsCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
CommandParameter="{Binding DataContext.SelectedIndex, RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
<!--#endregion DataGridCell-->
</DataGrid.Resources>
<!--#endregion Resources-->
<!--#region DataGridColumns-->
<DataGrid.Columns>
<DataGridTextColumn Header="Filename"
Binding="{Binding FileInfo.Name}"
Width="Auto" />
</DataGrid.Columns>
<!--#endregion DataGridColumns-->
</DataGrid>
<!--#endregion DataGrid-->
The DataContext of the UserControl is working fine as I have other commands which uses the DataContext that way.
Anyone sees any error or has any other approach?
Thanks in advance.
This should work provided that the OpenRowDetailsCommand and SelectedIndex properties are defined in the view model class of the parent UserControl:
<Style TargetType="DataGridCell">
<Setter Property="Tag" Value="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="Open Details"
Command="{Binding PlacementTarget.Tag.DataContext.OpenRowDetailsCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
CommandParameter="{Binding PlacementTarget.Tag.DataContext.SelectedIndex, RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
I like to use a BindingProxy for this (as described in this SO answer)
public class BindingProxy : Freezable
{
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
public object Data
{
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object),typeof(BindingProxy));
}
Use it like this:
<DataGrid ItemsSource="{Binding Model.Collection}">
<DataGrid.Resources>
<local:BindingProxy x:Key="VMProxy" Data="{Binding}" />
<local:BindingProxy x:Key="DataGridProxy" Data="{Binding RelativeSource={RelativeSource Self}}" />
<Style TargetType="DataGridCell">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="Open Details"
Command="{Binding Data.OpenRowDetailsCommand, Source={StaticResource VMProxy}}"
CommandParameter="{Binding Data.SelectedIndex, Source={StaticResource DataGridProxy}}"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</DataGrid.Resources>

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

Connect handler to ContextMenu in "Style" section

I need to create structure of ContextMenu in runtime, because existence of MenuItem's of the menu is based on many factors. And I try add handler to ContextMenuOpening event this way:
XAML
<TreeView x:Name="ArticlesTreeView" Grid.Column="0" AllowDrop="True">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="ContextMenuService.ShowOnDisabled" Value="True" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu Opened="ContextMenu_OnOpened">
<MenuItem Header="First item"></MenuItem>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
This code throws XamlParseException on <ContextMenu Opened="ContextMenu_OnOpened"> line. Can I solve issue in another way?
UPD I find that to me it is necessary not ContextMenuOpening and Opened event. I corrected upper code.
Try creating your ContextMenu on your ItemTemplate of the treeview. Try the below code (it is not complete but i hope you get the idea)
<TreeView .....>
<TreeView.ItemTemplate>
<DataTemplate>
<ContentControl>
<ContentControl.ContextMenu>
<ContextMenu ContextMenuOpening="ContextMenu_OnContextMenuOpening">
<MenuItem Header="First item"></MenuItem>
</ContextMenu>
</ContentControl.ContextMenu>
---------Your Item Template here
</ContentControl>
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Your question is already answered here.
Placing event handlers in event setter did the work.
<TreeView x:Name="ArticlesTreeView" AllowDrop="True">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="ContextMenuService.ShowOnDisabled" Value="True" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="First item">
<MenuItem.Style>
<Style TargetType="MenuItem">
<EventSetter Event="Click" Handler="ContextMenu_ContextMenuOpening_1"></EventSetter>
</Style>
</MenuItem.Style>
</MenuItem>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</TreeView.ItemContainerStyle>
<TreeViewItem>
</TreeViewItem>
</TreeView>
Also change your code behind event handler as
private void ContextMenu_ContextMenuOpening_1(object sender, RoutedEventArgs e)
{
}

Menu item won't show context menu

I have a button that when clicked, displays a ContextMenu. In this ContextMenu, I have MenuItems. If the MenuItem is left clicked, it should execute a command. All this behavior works properly as of now with the following code:
<Button.ContextMenu>
<ContextMenu>
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding StartContextMenuCommand}" />
<Setter Property="CommandParameter" Value="{Binding RelativeSource={RelativeSource Self}, Path=Header}" />
</Style>
</ContextMenu.ItemContainerStyle>
<MenuItem Header="{x:Static Name:ContextMenuStartNames.1}"/>
<MenuItem Header="{x:Static Name:ContextMenuStartNames.2}"/>
<MenuItem Header="{x:Static Name:ContextMenuStartNames.3}"/>
<MenuItem Header="{x:Static Name:ContextMenuStartNames.4}"/>
</ContextMenu>
</Button.ContextMenu>
Now I want each menu item to have a context menu with one MenuItem. Since left clicking invokes a Command on a MenuItem, I want the right click behavior to display the following ContextMenu:
<ContextMenu>
<MenuItem Header="Set Default"></MenuItem>
</ContextMenu>
I tried putting this as a setter in the ItemContainerStyle. I also tried putting it as a ContextMenu of a MenuItem, but neither of my attempts, among others have worked. I'm sure I could come up with a hacky way to do this, but I want to keep it clean and simple.
Maybe i do not understand your question (my english is not... anything special):D But this should work if you just want multilevel contextmenu:
<ContextMenu>
<MenuItem Header="Top Level 1">
<MenuItem Header="Sub Level" />
<MenuItem Header="Sub Level" />
</MenuItem>
<MenuItem Header="Top Level 2">
<MenuItem Header="Sub Level" />
<MenuItem Header="Sub Level" />
</MenuItem>
</ContextMenu>
Here's my current fix to this issue, but I don't like it at all. I would like to avoid code behind as much as possible.
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding StartCommand}" />
<Setter Property="CommandParameter" Value="{Binding RelativeSource={RelativeSource Self}, Path=Header}" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu StaysOpen="True">
<MenuItem Header="Set As Default"/>
</ContextMenu>
</Setter.Value>
</Setter>
<EventSetter Event="PreviewMouseRightButtonUp" Handler="MenuItem_Click"/>
</Style>
</ContextMenu.ItemContainerStyle>
And the code behind:
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
ButtonContextMenu.StaysOpen = true;
(sender as MenuItem).ContextMenu.IsOpen = true;
}

Categories