How do I create a "Window" menu in XAML? - c#

I am creating an MVVM Wpf client application. I want create menu in the main View for the application that his a menu item called "Window" on it. That menu item will dynamically update itself with a submenu of menuitems who are made up of the list of active windows running in the application. I created a ViewManager whom each View registers itself with to compile a list of active windows.
I am trying to do this in XAML but getting an error when I click on "Window"
<MenuItem Header="Window">
<ItemsControl ItemsSource="{Binding ViewMgr.Views}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding DataContext.OpenWindowCmd ,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"/>
<Setter Property="CommandParameter" Value="{Binding}"/>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</MenuItem>
How do I create a dynamically updated list of menuitems on my menu in XAML using a MVVM style of data bindings and commands?

You are adding a new ItemsControl as a single child of the menu item, instead of adding each view as one child of the menu item itself. You probably get the error because the styles TargetType doesn't match. MenuItem inherits from ItemsControl itself and exposes a property ItemsSource. Try the following:
<MenuItem ItemsSource="{Binding ViewMgr.Views}" DisplayMemberPath="Title">
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding DataContext.OpenWindowCmd, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"/>
<Setter Property="CommandParameter" Value="{Binding}"/>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>

Related

Two way binding of IsChecked within a nested MenuItem

I have a nested context menu like this:
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Thing Count" ItemsSource="{Binding ThingsProvider}">
NB the Thing Count parent MenuItem with children from ThingsProvider.
ThingsProvider provides a List of ThingViewModel which contains properties Thing and IsChecked. I want to be able to control IsChecked from my main view model and from the user via the MenuItem but I have hit a problem; If I use an ItemContainerStyle like this.
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Path=Thing, Mode=OneWay}"/>
<Setter Property="IsChecked" Value="{Binding Path=IsChecked, UpdateSourceTrigger=PropertyChanged}"/>
</Style>
</MenuItem.ItemContainerStyle>
then (predictably I suppose as it's a style) it will observe the ViewModel's IsChecked but will not set it.
If I use an ItemTemplate like so
<MenuItem.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Thing, Mode=OneWay}"/>
</DataTemplate>
</MenuItem.ItemTemplate>
I can't get to the MenuItem's IsChecked property as I've only got the TextBlock. If I put a MenuItem in the ItemTemplate then I end up with nested MenuItems in my UI and it looks bad.
There must be a way to do this but I'm stumped at the moment, can anyone help?
Cheers
Rich

WPF - Context menu on a single item in a treeview?

I am not very skilled (if at all) with WPF, and I am faced with the following problem.
I have a treeview control (XAMtreeView) where I am binding a contextmenuitem on. Now, I want the contextmenu to only display for one of the 5 tree node items (e.g. right clicking on the 4th item should pop it on, but right click should do nothing for the other items). I tried using databinding but something tells me that is the wrong way to go - nevertheless, here is what I have so far:
<DataTemplate x:Key="CategoryTreeNodeTemplate">
<TextBlock Tag="{Binding DataContext, ElementName=ScenarioOptions}"
Text="{Binding Data.Name}" Style="{StaticResource ScreenNodeTitleStyle}" VerticalAlignment="Center">
<TextBlock.ContextMenu>
<ContextMenu Visibility="Hidden" cal:Action.TargetWithoutContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<ContextMenu.Style>
<Style TargetType="ContextMenu">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Text, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TextBlock}}}" Value="CTS Charts">
<Setter Property="Visibility" Value="Visible"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContextMenu.Style>
<MenuItem Header="{StaticResource ExternalChartOpenFile}" cal:Message.Attach="OpenExternalFile()">
</MenuItem>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</DataTemplate>
As you can see I am trying to play with the visibility tag - and failing miserably while at it :). How do I make the context menu apply on a single treenode item?
Thanks :/

Command & command parameter Tree in Xaml

Trying to change one button to other, I have stuck in a problem with commands handling. It is difficult to describe its source, because nearly all are the same, but when I pass the command via setter it is not working.
My previous code (it is working like a charm):
it is just a split button that passes its menu item header as parameter to executing command.
<xctk:SplitButton.DropDownContent>
<ItemsControl ItemsSource="{Binding Instance.InstanceVM.Names}" x:Name="NamesCtr">
<ItemsControl.ItemTemplate>
<DataTemplate>
<MenuItem Header="{Binding}" Command="{Binding ElementName=NamesCtr, Path=DataContext.Instance.InstanceVM.Load}" CommandParameter="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</xctk:SplitButton.DropDownContent>
And here is for my dropdown button:
<controls:DropDownButton.DropDownContextMenu>
<ContextMenu x:Name="NamesCtr" ItemsSource="{Binding Instance.InstanceVM.Names}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Template" Value="{StaticResource DropDownMenuItemTemplate}"/>
<Setter Property="Header" Value="{Binding}"/>
<Setter Property="Command" Value="{Binding ElementName=Namestr, Path=DataContext.Instance.InstanceVM.Load}"/>
<Setter Property="CommandParameter" Value="{Binding}"/>
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</controls:DropDownButton.DropDownContextMenu>
This shows a button, a dropdown with correct menu items (so the source, template and header were bound correctly, but the command does not hit its function)
Found a solution here :
How do you bind a command to a MenuItem (WPF)?
As you are binding through a static singleton, you can modify the Binding like this:
<Setter Property="Command" Value="{Binding Source={x:Static <xmlnamespace>:<yourType>.Instance}, Path=InstanceVM.Load}"/
Don't forget to replace the placeholders with valid values, i.e. with an xml namespace prefix that corresponds to the appropriate CLR namespace and the type declaring the Instance property.
Please note that ElementName bindings and RelativeSource bindings are less trustworthy if placed in DataTemplates / Styles and also in case of using Popups in your visual tree.

WPF DataGrid Remove Column

I have created a DataGrid in WPF from a DataTable with AutoGenerateColumns = true. In the VM I have a command property for adding and deleting columns which manipulates the underlying DataTable.
When I call AddColumn from the main XAML window via Command="{Binding AddColumn}" it works as expected but when I call the RemoveColumn from a resource file with a context menu it calls the command property (im able to step through the code) but does not update the Grid.
<MenuItem Header="Delete" Command="{Binding Source={StaticResource AppViewModel}, Path=DeleteColumn}" CommandParameter="{Binding}" />
I now updated both commands to only set the DataTable to null, ie Dt = null and for the AddColumn this works as expected and removed the Grid and columns but for the RemoveColumn it does nothing... I also see no errors in the output window in regard to binding and when stepping through the code the property is called. I also tried to set the column to invisible which also did not work.
UPDATE
I call the DeleteColumn from the following (simplified) code in a resources file.
<Style x:Key="ColumnHeaderStyle" TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="VerticalContentAlignment" Value="Center" />
<Style.Triggers>
<DataTrigger Binding="{Binding Content, ConverterParameter=*, Converter={StaticResource AfterDashConverter}, RelativeSource={RelativeSource self}}" Value="Green">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
<Grid>
<Grid.ContextMenu>
<ContextMenu>
<MenuItem Header="Delete" Command="{Binding Source={StaticResource AppViewModel}, Path=DeleteColumn}" />
</ContextMenu>
</Grid.ContextMenu>
<DataGridColumnHeader x:Name="PART_FillerColumnHeader" IsHitTestVisible="False"/>
<ItemsPresenter/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
The commands are very simple, the AddColumn works as its directly in my main view and the DeleteColumn does not as its in the above resource file. I have checked the commands fire properly.
AddColumn = new RelayCommand(_ =>
{
Dt = null;
}, true);
DeleteColumn = new RelayCommand(column =>
{
Dt = null;
}, true);
the main issue was my resource was creating a new viewmodel so it was not calling / impacting the VM instance that my view was dependent on. To fix this I needed to set the DataContext of my ContextMenu parent control (which was a grid) as follows:
DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type MyType}}}
If you just call the following it will create a new instance of your ViewModel and use that:
<MenuItem Header="Delete" Command="{Binding Source={StaticResource AppViewModel}, Path=DeleteColumn}" />

WPF: MenuItem icon in the wrong place when using data template

I have the following code:
<Menu>
<MenuItem ItemsSource="{Binding SomethingMenuItems}" Header="Something"/>
</Menu>
Where MenuItems is a collection of objects of type SomethingMenuItem.
I also have:
<DataTemplate DataType="{x:Type SomethingMenuItem}">
<MenuItem Header="{Binding OrderTypeName}">
<MenuItem.Icon>
<Image Source="{Binding IconName}"/>
</MenuItem.Icon>
</MenuItem>
</DataTemplate>
I'd expect to get (I get something like this when I hardcode the menu items):
What I get instead is:
What am I doing wrong?
You might want to use ItemContainerStyle instead of DataTemplate. You have to style the container of the data item than just providing a template for data item.
With your DataTemplate, you basically displaying another nested MenuItem as content for each MenuItem generated for your Menu Something, and your inner MenuItem has the image in the correct place. I am attaching VisualTree from Snoop here for your reference.
Below is the Style for the container of the data item (in this case a MenuItem):
<MenuItem ItemsSource="{Binding SomethingMenuItems}"
Header="Something">
<MenuItem.Resources>
<Image Source="{Binding IconPath}" x:Key="IconImage" x:Shared="False"/>
<Style TargetType="MenuItem" >
<Setter Property="Header" Value="{Binding Name}" />
<Setter Property="Icon" Value="{StaticResource IconImage}" />
</Style>
</MenuItem.Resources>
</MenuItem>
You can see no nested MenuItems when you apply the above style, have added image here
With the above style applied, this is how the Menu looks:
Refer to this MSDN page to know more about ItemContainerStyle.
So, sthotakura's answer set me on the right track, but the code he posted didn't quite work because the style got applied to the parent menuitem as well.
In case someone else has a similar problem and stumbles on this question, here's the code that works:
<MenuItem ItemsSource="{Binding SomethingMenuItems}"
Header="Something">
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem" >
<Style.Resources>
<Image Source="{Binding IconPath}" x:Key="IconImage" x:Shared="False"/>
</Style.Resources>
<Setter Property="Header" Value="{Binding Name}" />
<Setter Property="Icon" Value="{StaticResource IconImage}" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>

Categories