Passing a command to a mainwindow from a different viewmodel - c#

I'm new to WPF and learning more about MVVM, and I've come across a problem. So I have my mainwindow.xaml with its code and implementation mostly based on the view model FileMenuCommands, now I've created another view model called AboutMenuCommands and I want to pass a command (the help command) from here to the main window, but when I try to do that the compiler doesn't find my function.
What do I need to do in order to pass a command from another view model in an XAML?
Code from mainWindow:
<Window x:Class="Notepad___.MainWindow"
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:local="clr-namespace:Notepad___.ViewModel"
mc:Ignorable="d"
Title="Notepad+++" Height="450" Width="800">
<Window.DataContext>
<local:FileMenuCommands/>
</Window.DataContext>
<Window.InputBindings>
<KeyBinding Key="O" Modifiers="Ctrl" Command="{Binding Path=Open}" />
<KeyBinding Key="N" Modifiers="Ctrl" Command="{Binding Path=New}" />
</Window.InputBindings>
<Grid>
<Menu Name="MainMenu" Height="Auto" Width="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Top">
<MenuItem Header="_File" FontSize="14" >
<MenuItem Header="_New" InputGestureText="Ctrl+N" Command="{Binding Path=New}">
<MenuItem.ToolTip>
<ToolTip>Create a new file.</ToolTip>
</MenuItem.ToolTip>
</MenuItem>
<MenuItem Header="Open" InputGestureText="Ctrl+O" Command="{Binding Path=Open}">
<MenuItem.ToolTip>
<ToolTip>Open a file.</ToolTip>
</MenuItem.ToolTip>
</MenuItem>
<MenuItem Header="_Save" InputGestureText="Ctrl+S">
<MenuItem.ToolTip>
<ToolTip>Saves a file in the current location if it already exists, if not creates a prompt.</ToolTip>
</MenuItem.ToolTip>
</MenuItem>
<MenuItem Header="_Save As" Command="{Binding Path=SaveAs}">
<MenuItem.ToolTip>
<ToolTip>Saves the file in the location prompted by the user in the format they desire.</ToolTip>
</MenuItem.ToolTip>
</MenuItem>
<MenuItem Header="_Exit" InputGestureText="Ctrl+X">
<MenuItem.ToolTip>
<ToolTip>Exits the application.</ToolTip>
</MenuItem.ToolTip>
</MenuItem>
</MenuItem>
<MenuItem Header="_Search" FontSize="14" >
<MenuItem Header="_Find" InputGestureText="Ctrl+F">
<MenuItem.ToolTip>
<ToolTip>Finds the occurences of the input word / set of characters.</ToolTip>
</MenuItem.ToolTip>
</MenuItem>
<MenuItem Header="_Replace" InputGestureText="Ctrl+R">
<MenuItem.ToolTip>
<ToolTip>Replaces the input word / set of characters.</ToolTip>
</MenuItem.ToolTip>
</MenuItem>
<MenuItem Header="_Replace All" >
<MenuItem.ToolTip>
<ToolTip>Replaces all the occurences of the input word / set of characters.</ToolTip>
</MenuItem.ToolTip>
</MenuItem>
</MenuItem>
<MenuItem Header="_Help" FontSize="14" Command="{Binding Source=Help}"> // this is where i'm trying to pass my command from another xaml
<MenuItem Header="_About" >
<MenuItem.ToolTip>
<ToolTip>A few words about the application and who made it</ToolTip>
</MenuItem.ToolTip>
</MenuItem>
</MenuItem>
</Menu>
AboutMenuCommand.cs
using Notepad___.View;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Windows.Input;
namespace Notepad___.ViewModel
{
class AboutMenuCommands
{
private ICommand m_help;
private void HelpPage(object parameter)
{
var aboutPage = new About();
aboutPage.ShowDialog();
}
public ICommand Help
{
get
{
if (m_help == null)
m_help = new RelayCommand(HelpPage);
return m_help;
}
}
}
}

Your MainWindowViewModel should get the viewmodel instance from your about page. The about command should be implemented in MainWindowViewModel and call the viewmodel from the About page.
I had such questions several month ago and came out with a mvvm framework which does really help to understand the principles of mvvm.
But i did not want to give up the control to a framework which i can not look into and so i found mvvmgen .
So take a look at the doc in mvvmgen and you will find the answer to your mvvm questions.

Related

Shortcuts not working in UserControl

I have a UserControl, which has CommandBindings:
<UserControl x:Class="DbCreator.UserControls.Menu"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:DbCreator"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.CommandBindings>
<CommandBinding Command="SelectAll" Executed="SelectAllCommand_Executed"/>
<CommandBinding Command="local:MyCommands.ToggleNavigator" Executed="ToggleNavigatorCommand_Executed"/>
<CommandBinding Command="local:MyCommands.ToggleGrid" Executed="ToggleGridCommand_Executed"/>
<CommandBinding Command="local:MyCommands.SelectNone" Executed="SelectNoneCommand_Executed"/>
</UserControl.CommandBindings>
<Menu Background="White">
<MenuItem Header="_Файл"/>
<MenuItem Header="_Правка">
<MenuItem Header="Выделить всё" Command="SelectAll"/>
<MenuItem Header="Убрать выделение" Command="local:MyCommands.SelectNone"/>
</MenuItem>
<MenuItem Header="_Вид">
<MenuItem x:Name="ToggleNavigator" Header="Скрыть навигатор" Command="local:MyCommands.ToggleNavigator"/>
<MenuItem x:Name="ToggleGrid" Header="Скрыть сетку" Command="local:MyCommands.ToggleGrid"/>
</MenuItem>
<MenuItem Header="_Диаграмма"/>
<MenuItem Header="_Справка"/>
</Menu>
When I click MenuItem, my Executed method is fired, but shortcuts not working. When this UserControl content was a part of MainWindow code, all shortcuts worked perfect. But I want to define menu in a separate file.
So the problem was that Menu was unfocused and didn't recieve input. Solution is to add InputBindings to MainWindow:
<Window.InputBindings>
<KeyBinding Command="SelectAll" CommandTarget="{Binding ElementName=Menu}" Key="A" Modifiers="Control"/>
<KeyBinding Command="local:MyCommands.SelectNone" CommandTarget="{Binding ElementName=Menu}" Key="A" Modifiers="Control+Shift"/>
<KeyBinding Command="local:MyCommands.ToggleNavigator" CommandTarget="{Binding ElementName=Menu}" Key="N" Modifiers="Alt"/>
<KeyBinding Command="local:MyCommands.ToggleGrid" CommandTarget="{Binding ElementName=Menu}" Key="G" Modifiers="Alt"/>
</Window.InputBindings>

My menu showing up at the top left of the screen

I built a menu bar using the menu control in WPF and it had been working but at some point the menu started showing up on the top left of my first monitor regardless of where in the screen I had the window. Even if I move the main window to the second monitor the menu still shows up in the first monitor.
Here is the code for the menu control:
<Menu>
<MenuItem Header="_File">
<MenuItem Header="_New" Command="New" InputGestureText="Ctrl+N"/>
<MenuItem Header="_Open" Command="Open" InputGestureText="Ctrl+O" />
<MenuItem Header="_Close" Command="Close" InputGestureText="Ctrl+W" />
<Separator/>
<MenuItem Header="_Save" Command="Save" InputGestureText="Ctrl+S" />
<MenuItem Header="Save _As" Command="SaveAs" InputGestureText="Ctrl+Shift+S" />
<Separator/>
<MenuItem Header="E_xit" Command="{StaticResource CommandBinding_Exit}" InputGestureText="Ctrl+Q" />
</MenuItem>
<MenuItem Header="_Edit">
<MenuItem Header="_Add" Command="{StaticResource CommandBinding_Add}" InputGestureText="" />
<MenuItem Header="_Edit" Command="{StaticResource CommandBinding_Edit}" InputGestureText="" />
<MenuItem Header="_Delete" Command="Delete" InputGestureText="" />
<Separator/>
<MenuItem Header="Cut" Command="Cut" InputGestureText="Ctrl+X" />
<MenuItem Header="Copy" Command="Copy" InputGestureText="Ctrl+C" />
<MenuItem Header="Paste" Command="Paste" InputGestureText="Ctrl+V" />
</MenuItem>
<MenuItem Header="_View">
<MenuItem x:Name="miShowStatusBar" Header="Show Status Bar" IsCheckable="True" IsChecked="True" Click="miShowStatusBar_Click"/>
<MenuItem x:Name="miShowFullPath" Header="Show Full Path" IsCheckable="True" IsChecked="True" Click="miShowFullPath_Click"/>
</MenuItem>
<MenuItem Header="_Help">
<MenuItem Header="_About"/>
</MenuItem>
</Menu>
The menu is not referenced any where in the code behind so I can't figure out what might be causing this odd menu placement.
I think the source of the OP's problem is in the search box under the MenuBar. It may come from the Margin or Width settings of the search box.
I faced the same problem and found the sources. Here is the boilerplate code to reproduce the same error. The problem is a combination of 5 parts, as shown in the following XAML. Remove any one of them will solve the OP's issue.
You need to bind the XAML View to a ViewModel with public property Films. If the Films collection has any element in it, it will also cause the issue (Part 5).
<Grid x:Name="MainContent">
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition />
</Grid.RowDefinitions>
<Grid >
<Menu>
<MenuItem Header="Film">
<MenuItem Header="New"/>
</MenuItem>
</Menu>
</Grid>
<Grid Grid.Row="1">
<Grid>
<!--Part 1: Remove HorizontalScrollBarVisibility="Auto"-->
<ScrollViewer HorizontalScrollBarVisibility="Auto">
<!--Part 2: Remove ItemsSource="{Binding Films}"-->
<ItemsControl ItemsSource="{Binding Films}">
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<!--Part 3: Remove the setter tag-->
<Setter Property="Margin" Value="2" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button>
<Button.Template>
<ControlTemplate TargetType="Button">
<!--Part 4: Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}, Path=ActualWidth}-->
<ContentPresenter Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}, Path=ActualWidth}"/>
</ControlTemplate>
</Button.Template>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
</Grid>
</Grid>
This has nothing to do with WPF. It's a possible corruption of the Handedness setting or driver issues if you have a touch screen monitor, tablet PC or have a tablet attached with a bad driver (such as a Wacom drawing tablet).
Type this into the Run dialog: shell:::{80F3F1D5-FECA-45F3-BC32-752C152E456E}.
Once Tablet PC Settings come up, go to the Other tab and in the Handedness section, check the Left Handed option.
I bet that this has something to do with the Xaml-Designer of your Visual Studio instance. I encounter similar strange things because of leaving the Xaml-Designer open while debugging.
Try to kill the XDesProc.exe process and check if the problem still occurs.

Implementing an Edit menu in MVVM Light application

I'm building a WPF application using MVVM Light. I've added a menu to my window that includes the standard File and Edit menus:
<Window x:Class="ParserEditor.MainWindow"
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:local="clr-namespace:ParserEditor"
xmlns:ignore="http://www.galasoft.ch/ignore"
xmlns:i="clr namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF4"
mc:Ignorable="d ignore"
Closing="Window_Closing"
DataContext="{Binding Main, Source={StaticResource Locator}}"
Height="{Binding Height, Mode=TwoWay}"
MaxHeight="{Binding MaxHeight, Mode=TwoWay}"
MinHeight="{Binding MinHeight, Mode=TwoWay}"
MinWidth="450"
Width="450"
Title="{Binding WindowTitle}">
<Window.InputBindings>
<KeyBinding Command="{Binding NewParserCommand}" Gesture="CTRL+N" />
<KeyBinding Command="{Binding OpenParserCommand}" Gesture="CTRL+O" />
<KeyBinding Command="{Binding SaveParserCommand}" Gesture="CTRL+S" />
<KeyBinding Command="{Binding SaveParserAsCommand}" Gesture="ALT+A" />
<KeyBinding Command="{Binding ExitCommand}" Gesture="ALT+F4" />
<KeyBinding Command="{Binding ApplicationCommands.Undo}" Gesture="CTRL+Z" />
<KeyBinding Command="{Binding ApplicationCommands.Redo}" Gesture="CTRL+Y" />
<KeyBinding Command="{Binding ApplicationCommands.Cut}" Gesture="CTRL+X" />
<KeyBinding Command="{Binding ApplicationCommands.Copy}" Gesture="CTRL+C" />
<KeyBinding Command="{Binding ApplicationCommands.Paste}" Gesture="CTRL+V" />
<KeyBinding Command="{Binding ApplicationCommands.Delete}" Gesture="DEL" />
<KeyBinding Command="{Binding ApplicationCommands.SelectAll}" Gesture="CTRL+A" />
</Window.InputBindings>
<DockPanel Name="Dock">
<Menu IsMainMenu="true" DockPanel.Dock="Top">
<MenuItem Header="_File">
<MenuItem Header="_New..." InputGestureText="Ctrl-N" Command="{Binding NewParserCommand}" />
<MenuItem Header="_Open..." InputGestureText="Ctrl-O" Command="{Binding OpenParserCommand}" />
<MenuItem Header="_Save..." InputGestureText="Ctrl-S" Command="{Binding SaveParserCommand}" />
<MenuItem Header="Save _As..." InputGestureText="Alt-A" Command="{Binding SaveParserAsCommand}" />
<Separator />
<MenuItem Header="E_xit" InputGestureText="Alt-F4" Command="{Binding ExitCommand}" />
</MenuItem>
<MenuItem Header="_Edit">
<MenuItem Header="Undo" InputGestureText="Ctrl-Z" />
<MenuItem Header="Redo" InputGestureText="Ctrl-Y" />
<Separator/>
<MenuItem Header="Cut" InputGestureText="Ctrl-X" />
<MenuItem Header="Copy" InputGestureText="Ctrl-C" />
<MenuItem Header="Paste" InputGestureText="Ctrl-V" />
<MenuItem Header="Delete" InputGestureText="Del" />
<MenuItem Header="Select All" InputGestureText="Ctrl-A" />
</MenuItem>
</Menu>
<Grid x:Name="LayoutRoot">
<!-- Some controls, including TextBlocks & TextBoxes here -->
</Grid>
</DockPanel>
</Window>
Now, if I run the application and make some edits in one of the TextBox controls, I can use the usual keyboard short cuts to cut, copy & paste text, undo & redo actions while in focus is the control. But if I click on my Edit menu and use one of my options there, nothing happens.
How do I get my menu items to work?
I figured this out. After posting the question & reviewing the XAML, I noticed that the Edit menu items didn't have any Commands bound to them. I added the required Command="ApplicationCommands.xxx" statements to the XAML & now the Edit menu works.

Show menu entries only for certain user roles

I'd like to get to know how I can set menu entries visible or hidden for certain user roles in XAML code? Eg I have "_MenuEntryForADMIN" which should only visible for user role "admin" and "_MenuEntryForAllUsers" should be visible to all users - role "user".
Here is my XAML code:
<Grid>
<DockPanel>
<MenuItem Header="_MenuEntryForADMIN">
<MenuItem Header="_Master1" Click="btnMaster1_Click">
<MenuItem.Icon>
<Image Source="database.png" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="_Master2" Click="btnMaster2_Click">
<MenuItem.Icon>
<Image Source="database.png" />
</MenuItem.Icon>
</MenuItem>
</MenuItem>
<MenuItem Header="_MenuEntryForAllUsers">
<MenuItem Header="_Normal1" Click="btnNormal1_Click">
<MenuItem.Icon>
<Image Source="database.png" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="_Normal2" Click="btnNormal2_Click">
<MenuItem.Icon>
<Image Source="database.png" />
</MenuItem.Icon>
</MenuItem>
</MenuItem>
</Menu>
</DockPanel>
</Grid>
The CS-Code contains the information whether a user has the role "admin" or "user" like this:
User userObject;
public MainWindow(User userObject)
{
InitializeComponent();
this.userObject = userObject; // this.userObject.Role is "admin" or "user"
}
In WPF, it is important to 'shape' your data into the correct structure before you try to display it. By this, I mean that you should filter the data, your MenuItems, in your view model or code behind before displaying them, rather than trying to filter them in the UI.
So rather than defining them all in XAML, create some custom data type to data bind to the various MenuItem properties that you are interested in. Then create a DataTemplate and bind a collection of these to the MenuItem.ItemsSource property. You can find out how to do that from my answer to the Dynamically hiding menu items question. In short, it looks something like this:
<DataTemplate x:Key="MenuItemTemplate" DataType="{x:Type MenuItem}">
<MenuItem Command="{Binding Command}" CommandParameter="{Binding CommandParameter}"
Header="{Binding Path=Header}" Visibility="{Binding Visible, Converter={
BooleanToVisibilityConverter}}" ItemsSource="{Binding ChildMenuItems}" />
</DataTemplate>
There are also many examples online of filtering data in collections, so I won't go over all of that here. You can find a short example in the “Hiding” part of List question here on Stack Overflow. Basically, you can use LinQ to select just the admin MenuItems something like this:
ChildMenuItems = new ObservableCollection<YourCustomDataType>(
FullCollection.Where(i = i.Role == "admin").ToList());

WPF find a nested menuitem in code

I have an image that has a nested context menu defined in the XAML (shown below).
I am trying to use a for loop to find a menu and uncheck it using IsChecked=false.
my code is as follows:
for (int i = 1; i <= 16; i++)
{
MenuItem theMenu = (MenuItem)this.FindName("beat" + i.ToString());
theMenu.IsChecked = false;
}
the above does not work and returns a null.
what am I doing wrong!
excerpt from the XAML
<Image x:Name="Options" Height="35" Source="Images/pad-options-button.png" Stretch="Fill" Width="47" Canvas.Left="740" Canvas.Top="293"
MouseUp="Options_MouseUp">
<Image.ContextMenu>
<ContextMenu>
<MenuItem Header="Beats">
<MenuItem Name="beat1" Header="1" Click="MenuBeats_Click"/>
<MenuItem Name="beat2" Header="2" Click="MenuBeats_Click"/>
<MenuItem Name="beat3" Header="3" Click="MenuBeats_Click"/>
<MenuItem Name="beat4" Header="4" Click="MenuBeats_Click"/>
<MenuItem Name="beat5" Header="5" Click="MenuBeats_Click"/>
<MenuItem Name="beat6" Header="6" Click="MenuBeats_Click"/>
<MenuItem Name="beat7" Header="7" Click="MenuBeats_Click"/>
<MenuItem Name="beat8" Header="8" Click="MenuBeats_Click"/>/>
<MenuItem Name="beat9" Header="9" Click="MenuBeats_Click"/>
<MenuItem Name="beat10" Header="10" Click="MenuBeats_Click"/>
<MenuItem Name="beat11" Header="11" Click="MenuBeats_Click"/>
<MenuItem Name="beat12" Header="12" Click="MenuBeats_Click"/>
<MenuItem Name="beat13" Header="13" Click="MenuBeats_Click"/>
<MenuItem Name="beat14" Header="14" Click="MenuBeats_Click"/>
<MenuItem Name="beat15" Header="15" Click="MenuBeats_Click"/>
<MenuItem Name="beat16" Header="16" Click="MenuBeats_Click"/>
</MenuItem>
<MenuItem Header="Beat Type">
<MenuItem Name="Whole" Header="Whole" Click="MenuBeatType_Click"/>
<MenuItem Name ="Half" Header="Half" Click="MenuBeatType_Click"/>
<MenuItem Name ="Quarter" Header="Quarter" Click="MenuBeatType_Click"/>
<MenuItem Name ="Eighth" Header="Eighth" Click="MenuBeatType_Click"/>
<MenuItem Name ="Sixteenth" Header="Sixteenth" Click="MenuBeatType_Click"/>
</MenuItem>
</ContextMenu>
</Image.ContextMenu>
</Image>
</Canvas>
An other option is to get the context menu from the image.
var items = Options.ContextMenu.Items
foreach(MenuItem item in items)
{
// do your work with the item
}
Be arware that you will have to handle the sub items!
Items will have only 2 items in your case
You have incorrect markup. Last two symbols shouldn't be there:
<MenuItem Name="beat8" Header="8" Click="MenuBeats_Click"/>/>
However, you example perfectly works for me, but with accidental item:
"/>"
Just in the constructor of the class in code behind write NameScope.SetNameScope(contextMenu, NameScope.GetNameScope(this)); contextMenu is the name given to context menu of image. Actually ContextMenu is not the part of Visual tree so it cant find the names . but the above line of code will set context menu in the scope of visual tree and hence will find the names. Hope this will help.

Categories

Resources