mvvm with prism: setting view from menu item - wpf

I am new to wpf world. I have a context menu in the shell as below:
<ContextMenu>
<MenuItem Header="Login"
Command="{Binding WorkSpaceViewSetter}" CommandParameter="DemoApplication.View.LoginView">
<MenuItem.Icon>
<Image Height="16" Width="16" Stretch="Uniform" Source="/Images/login.png"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Modules" ItemsSource="{Binding AppModules}">
<MenuItem.Icon>
<Image Source="/Images/modules.png"/>
</MenuItem.Icon>
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding ModuleName}"/>
<Setter Property="Command" Value="{Binding ElementName=win, Path=DataContext.WorkSpaceViewFromType}"/>
<Setter Property="CommandParameter" Value="{Binding MainViewType}"/>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</ContextMenu>
Each element in the itemssource AppModules of the Modules menuitem has a property named MainViewType of type System.Type. I want to change the view of a region when a menuitem gets clicked and am thinking of using a single ICommad in the shellviewmodel and passing the MainViewType as command parameter. However, the above code is not working.
I was wondering why then the Modules menuitem gets populated from the itemssource as expected.
I have noticed that the command binding on the Login menuitem is also not working even though it should have, since the itemssource property of Modules gets properly bounded. Can anybody please suggest how to make it work?

Context menus aren't on the same visual tree as the rest of your window, so using ElementName in the binding won't work. You'll need to using PlacementTarget instead. Without knowing how your viewmodels are structured it's difficult to give a definitive answer but your solution will look something like:
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding ModuleName}"/>
<Setter Property="Command" Value="{Binding PlacementTarget.DataContext.WorkSpaceViewFromType}"/>
<Setter Property="CommandParameter" Value="{Binding MainViewType}"/>
</Style>
</MenuItem.ItemContainerStyle>

Related

Set scroll to one of the MenuItem on a contextmenu in WPF

I have a context menu, and it had a few items.
Like always if scroll over one of the items I can see the sub-items in that menu.
But its size is very huge I can't able to see the full lists or scroll it.
Since the sub-items are loaded dynamically I use like :
<ContextMenu >
<MenuItem Header="Mobiles" ItemSource={Binding...}>
<MenuItem.ItemContainerStyle>
<Stryle targeyType="MenuItem">
<Setter property="HeaderTemplate">
<Setter.Value>
<DataTemplate> <TextBlock Text ="{Binding MobilName}"/> </DataTemplate>
</Setter.Value>
<Setter Property="Command" Value="{Binding ViewMobileCommand}"/>
</Setter >
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</ContextMenu >
Can anyone suggest a better way to tackle this?

WPF Design Time Data in Menu Subitem Style

I have a WPF project with a menu. One of the menus has subitems dynamically generated by binding to a collection in code behind. Here is the XAML for this item:
<MenuItem Header="Open Files" Name="MiInsertOpen"
d:DataContext="{d:DesignInstance Type={x:Type core:DBInterface}}"
ItemsSource="{Binding InsertableDBs}">
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding DisplayName}" />
<Setter Property="ToolTip" Value="{Binding FilePath}" />
<Setter Property="Command" Value="{Binding DataContext.CmdInsert,
RelativeSource={RelativeSource FindAncestor,
AncestorType=MenuItem, AncestorLevel=2}}" />
<Setter Property="CommandParameter" Value="{Binding FilePath}" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
This all works fine at run time and I get what I would expect, etc. The data context of each subitem is the proper DB that it represents.
At design time, however, the parent MenuItem has it's design time data context properly set and recognizes 'IsertableDBs', but in the style it's complaining that it can't find 'DisplayName' and the other properties on type 'DBInterface' (the DataContext type for the parent menu). How do I tell it that the items in that style should have DataContext of the DB type?
Ok after googling some more I found the answer here. This seems to be working; the only thing I have to add is that the d: tag comes up empty in Intellisense when entering it; just type in 'Style.DataContext' manually and it works...

Change MenuItem header and icon based on viewmodel property binding

I cannot figure out how to do this, and one would think it would be pretty simple.
I have a MenuItem that's part of a ContextMenu. I have a binding to a boolean property on a viewmodel. Depending on the state of this property, I want the MenuItem's header text and icon to change.
I could certainly use an IValueConverter for this, but I'm sure that there is a more elegant solution using a DataTemplate and triggers. I just can't figure out the proper markup.
The code I worked up (snipped below) has two problems: one the HeaderTemplate doesn't appear to contain the icon, so what would be the MenuItems's text contains the icon as well (normally the icon appears on in left hand section - see image and compare with the Copy and Clear menuitems). Additionally, clicking the MenuItem doesn't trigger the DataTemplate changes (note the command works, the viewmodel binding does in fact toggle the true/false state).
<ContextMenu>
<MenuItem Command="{Binding Source={x:Static cmd:Commands.PauseCommand}}"
CommandParameter="{Binding}">
<MenuItem.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image x:Name="img" Source="../Icons/pause.png"/>
<TextBlock x:Name="txt" Text="Pause"/>
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsPaused}" Value="True" >
<Setter Property="Image.Source" Value="../Icons/play.png" TargetName="img"/>
<Setter Property="Text" Value="Play" TargetName="txt"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</MenuItem.HeaderTemplate>
</MenuItem>
You mean like this? Dont make things harder than they are ;)
<ContextMenu>
<MenuItem Command="{Binding Source={x:Static cmd:Commands.PauseCommand}}"
CommandParameter="{Binding}">
<MenuItem.Style>
<Style TargetType="MenuItem">
<Setter Property="Icon" Value="../Icons/play.png>"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding IsPaused, Mode=OneWay}" Value="True">
<Setter Property="Icon" Value="../Icons/pause.png"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</MenuItem.Style>
</MenuItem>
</ContextMenu>

Change context menu in xaml

I've got a view with two large context menus defined as resouces. They look something like this (only larger) :
<ContextMenu x:Key="ContextMenu1">
<MenuItem Header="Append" Command="{Binding AppendCommand}" />
<MenuItem Header="Edit" Command="{Binding AppendCommand}" />
</ContextMenu>
<ContextMenu x:Key="ContextMenu2">
<MenuItem Header="Delete" Command="{Binding DeleteCommand}" />
<MenuItem Header="Verify" Command="{Binding VerifyCommand}" />
</ContextMenu>
I know that I can dynamically show / hide items with the canExecute method of the command. But since this is two completely different modes I would like to just Bind to a bool property that decides which context menu to display. Something like this:
<ListView ContextMenu={binding ContextMenuSelector}>
Does anyone know how I can do something like that ?
Consider using a DataTemplateSelector.
The DataTemplateSelector enables you to display specific presentations based on the context of each datacontext item within your itemscontrol.
I have used it for context menu items that may differin behavior.
I got a few good ideas from the DataTemplateSelector, but I ended up with a style with data trigger:
<Style TargetType="{x:Type StackPanel}" x:Key="stackPanelStyle">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=AppendMode}" Value="True">
<Setter Property="ContextMenu" Value="{DynamicResource PanelContextMenuAttachFile}"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Path=AppendMode}" Value="False">
<Setter Property="ContextMenu" Value="{DynamicResource PanelContextMenu}"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>

WPF: Add a command to auto-generated by binding menu items

MVVM is used. I created separate menu 'Recent files' which gets its items from binding. It looks like that:
<MenuItem Header="_Recent files" ItemsSource="{Binding RecentFiles, Converter={StaticResource RecentFilesToListOfStringsConverter}, Mode=OneWay}" >
</MenuItem>
Now, I would like to add Command to each of the those auto-generated items, which should get the path as command parameter and execute import file action by click.
Could you please suggest how can it be done in MVVM way?
Again, found the solution by myself. I tried to put the command in wrong way like below, and it doesn't work:
<MenuItem Header="_Recent files" ItemsSource="{Binding RecentFiles, Converter={StaticResource RecentFilesToListOfStringsConverter}, Mode=OneWay}" >
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding ImportRecentItemCommand}" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
Here is the right approach. Still don't understand how it works, have to learn WPF deeply!
<MenuItem Header="_Recent files" ItemsSource="{Binding RecentFiles, Converter={StaticResource RecentFilesToListOfStringsConverter}, Mode=OneWay}" >
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding DataContext.ImportRecentItemCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type MenuItem}, AncestorLevel=1}}" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
EDIT: The final version
XAML:
<MenuItem Header="_Recent files" ItemsSource="{Binding RecentFiles, Converter={StaticResource RecentFilesToListOfStringsConverter}, Mode=OneWay}" >
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding DataContext.ImportRecentItemCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type MenuItem}, AncestorLevel=1}}" />
<Setter Property="CommandParameter" Value="{Binding}" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
ViewModel: MVVM Light Toolkit is used, RelayCommand goes from there:
private ICommand _importRecentItemCommand;
public ICommand ImportRecentItemCommand
{
get { return _importRecentItemCommand ?? (_importRecentItemCommand = new RelayCommand<object>(ImportRecentItemCommandExecuted)); }
}
private void ImportRecentItemCommandExecuted(object parameter)
{
MessageBox.Show(parameter.ToString());
}
Enjoy

Resources