MenuItem and IcollectionView - wpf

I want to display a list of available languages in a menu.
The languages are available as an ICollectionView.
This is the code:
<Menu DockPanel.Dock="Top">
<Menu.Resources>
<Style x:Key="LanguageMenuStyle" TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Name}"></Setter>
<Setter Property="IsCheckable" Value="True"/>
</Style>
</Menu.Resources>
<MenuItem Header="Language" ItemsSource="{Binding Languages}"
ItemContainerStyle="{StaticResource LanguageMenuStyle}">
</MenuItem>
</Menu>
Languages is an ICollectionView created as default view from a list of cultures.
The menu is displayed correctly.
Now I would like to bind to the CurrentChanged event when the selection in the menu changes, but since there is no IsSynchronizedWithCurrentItem property, how could I do that?
And is there a way to allow only one item to be checked at a time?

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?

How can I add context menu item to a textBox inside of the FlowDocumentScrollViewer

I have my PDFDocument bound to the FlowDocumentScrollViewer.
<FlowDocumentScrollViewer
Document="{Binding Path=PDFDocument}"
/>
How can I add a new context menu item to a text box inside of the viewing area
Eventually I found how to do it
You can attach the context menu to each of TextBox Elements using a style property setter like this:
<Window.Resources>
<ContextMenu x:Key="contextMenu" >
<MenuItem Name="mnuOpen" Header="_Open Link" Command="{Binding TextBoxContextMenuCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Self}}"/>
<MenuItem Name="mnuView" Header="_View Properties" Command="{Binding TextBoxContextMenuCommand}"/>
</ContextMenu>
<Style TargetType="TextBox">
<Setter Property="ContextMenu" Value="{DynamicResource contextMenu}" />
</Style>
</Window.Resources>

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...

mvvm with prism: setting view from menu item

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>

How can I bind an ObservableCollection of ViewModels to a MenuItem?

When I bind Menu Items with an ObservableCollection, only the "inner" area of the MenuItem is clickable:
alt text http://tanguay.info/web/external/mvvmMenuItems.png
In my View I have this menu:
<Menu>
<MenuItem
Header="Options" ItemsSource="{Binding ManageMenuPageItemViewModels}"
ItemTemplate="{StaticResource MainMenuTemplate}"/>
</Menu>
Then I bind it with this DataTemplate:
<DataTemplate x:Key="MainMenuTemplate">
<MenuItem
Header="{Binding Title}"
Command="{Binding DataContext.SwitchPageCommand,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Menu}}}"
Background="Red"
CommandParameter="{Binding IdCode}"/>
</DataTemplate>
Since each ViewModel in the ObservableCollection ManageMenuPageItemViewModels has a property Title and IdCode, the above code works fine at first sight.
HOWEVER, the problem is that the MenuItem in the DataTemplate is actually inside another MenuItem (as if it is being bound twice) so that in the above DataTemplate with Background="Red" there is a Red box inside each menu item and only this area can be clicked, not the whole menu item area itself (e.g. if the user clicks on the area where the checkmark is or to the right or left of the inner clickable area, then nothing happens, which, if you don't have a separate color is very confusing.)
What is the correct way to bind MenuItems to an ObservableCollection of ViewModels so that the whole area inside each MenuItem is clickable?
UPDATE:
So I made the following changes based on advice below and now have this:
alt text http://tanguay.info/web/external/mvvmMenuItemsYellow.png
I have only a TextBlock inside my DataTemplate, but I still can't "color the whole MenuItem" but just the TextBlock:
<DataTemplate x:Key="MainMenuTemplate">
<TextBlock Text="{Binding Title}"/>
</DataTemplate>
And I put the Command binding into Menu.ItemContainerStyle but they don't fire now:
<Menu DockPanel.Dock="Top">
<Menu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Background" Value="Yellow"/>
<Setter Property="Command" Value="{Binding DataContext.SwitchPageCommand,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Menu}}}"/>
<Setter Property="CommandParameter" Value="{Binding IdCode}"/>
</Style>
</Menu.ItemContainerStyle>
<MenuItem
Header="MVVM" ItemsSource="{Binding MvvmMenuPageItemViewModels}"
ItemTemplate="{StaticResource MainMenuTemplate}"/>
<MenuItem
Header="Application" ItemsSource="{Binding ApplicationMenuPageItemViewModels}"
ItemTemplate="{StaticResource MainMenuTemplate}"/>
<MenuItem
Header="Manage" ItemsSource="{Binding ManageMenuPageItemViewModels}"
ItemTemplate="{StaticResource MainMenuTemplate}"/>
</Menu>
I found using MVVM with MenuItems to be very challenging. The rest of my application uses DataTemplates to pair the View with the ViewModel, but that just doesn't seem to work with Menus because of exactly the reasons you've described. Here's how I eventually solved it. My View looks like this:
<DockPanel>
<Menu DockPanel.Dock="Top" ItemsSource="{Binding Path=(local:MainViewModel.MainMenu)}">
<Menu.ItemContainerStyle>
<Style>
<Setter Property="MenuItem.Header" Value="{Binding Path=(contracts:IMenuItem.Header)}"/>
<Setter Property="MenuItem.ItemsSource" Value="{Binding Path=(contracts:IMenuItem.Items)}"/>
<Setter Property="MenuItem.Icon" Value="{Binding Path=(contracts:IMenuItem.Icon)}"/>
<Setter Property="MenuItem.IsCheckable" Value="{Binding Path=(contracts:IMenuItem.IsCheckable)}"/>
<Setter Property="MenuItem.IsChecked" Value="{Binding Path=(contracts:IMenuItem.IsChecked)}"/>
<Setter Property="MenuItem.Command" Value="{Binding}"/>
<Setter Property="MenuItem.Visibility" Value="{Binding Path=(contracts:IMenuItem.Visible),
Converter={StaticResource BooleanToVisibilityConverter}}"/>
<Setter Property="MenuItem.ToolTip" Value="{Binding Path=(contracts:IMenuItem.ToolTip)}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=(contracts:IMenuItem.IsSeparator)}" Value="true">
<Setter Property="MenuItem.Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type MenuItem}">
<Separator Style="{DynamicResource {x:Static MenuItem.SeparatorStyleKey}}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Menu.ItemContainerStyle>
</Menu>
</DockPanel>
If you notice, I defined an interface called IMenuItem, which is the ViewModel for a MenuItem. Here's the code for that:
public interface IMenuItem : ICommand
{
string Header { get; }
IEnumerable<IMenuItem> Items { get; }
object Icon { get; }
bool IsCheckable { get; }
bool IsChecked { get; set; }
bool Visible { get; }
bool IsSeparator { get; }
string ToolTip { get; }
}
Notice that the IMenuItem defines IEnumerable Items, which is how you get sub-menus. Also, the IsSeparator is a way to define separators in the menu (another tough little trick). You can see in the xaml how it uses a DataTrigger to change the style to the existing separator style if IsSeparator is true. Here's how MainViewModel defines the MainMenu property (that the view binds to):
public IEnumerable<IMenuItem> MainMenu { get; set; }
This seems to work well. I assume you could use an ObservableCollection for the MainMenu. I'm actually using MEF to compose the menu out of parts, but after that the items themselves are static (even though the properties of each menu item are not). I also use an AbstractMenuItem class that implements IMenuItem and is a helper class to instantiate menu items in the various parts.
UPDATE:
Regarding your color problem, does this thread help?
Don't put the MenuItem in the DataTemplate. The DataTemplate defines the content of the MenuItem. Instead, specify extraneous properties for the MenuItem via the ItemContainerStyle:
<Menu>
<Menu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Title}"/>
...
</Style>
</Menu.ItemContainerStyle>
<MenuItem
Header="Options" ItemsSource="{Binding ManageMenuPageItemViewModels}"
ItemTemplate="{StaticResource MainMenuTemplate}"/>
</Menu>
Also, take a look at HierarchicalDataTemplates.
Here is how I have done my menus. It may not be precisely what you need, but I think it is pretty close.
<Style x:Key="SubmenuItemStyle" TargetType="MenuItem">
<Setter Property="Header" Value="{Binding MenuName}"></Setter>
<Setter Property="Command" Value="{Binding Path=MenuCommand}"/>
<Setter Property="ItemsSource" Value="{Binding SubmenuItems}"></Setter>
</Style>
<DataTemplate DataType="{x:Type systemVM:TopMenuViewModel}" >
<Menu>
<MenuItem Header="{Binding MenuName}"
ItemsSource="{Binding SubmenuItems}"
ItemContainerStyle="{DynamicResource SubmenuItemStyle}" />
</Menu>
</DataTemplate>
<Menu DockPanel.Dock="Top" ItemsSource="{Binding Menus}" />
TopMenuViewModel is a collection of the menus that will appear on the menu bar. They each contain the MenuName that will be displayed and a collection called SubMenuItems that I set to be the ItemsSource.
I control the way the SubMenuItems are displayed by way of the style SumMenuItemStyle. Each SubMenuItem has its own MenuName property, Command property of type ICommand, and possibly another collection of SubMenuItems.
The result is that I am able to store all my menu information in a database and dynamically switch what menus are displayed at runtime. The entire menuitem area is clickable and displays correctly.
Hope this helps.
Just make your DataTemplate to be a TextBlock (or maybe a stack panel with an icon and a TextBlock).

Resources