简体   繁体   中英

Howto do i get correct CommandParameter Binding with dynamic menus in wpf and mvvm

i'm implementet a dynamic menu service in my WPF Application. Every Command can or should have different CommandParameters.

The Problem: With my solution to set the CommandParamter binding in xaml the CanExecute property from the command doesent update anymore.

What i have so far

I'm using mvvm-light and fody-propertychanged.

Here the menu class:

public class MyMenu : INotifyPropertyChanged
{
    private ObservableCollection<MyMenu> myChildren;

    public MyMenu()
    {
        myChildren = new ObservableCollection<MyMenu>();
    }
    public string Header { get; set; }
    public string Image { get; set; }
    public string CommandName { get; set; } //used to set the CommandParameter binding
    public ICommand Command { get; set; }
    public ObservableCollection<MyMenu> Children {
        get
        {
            return myChildren;
        }
        private set
        {
            myChildren = value;
        }
    } 


    public event PropertyChangedEventHandler PropertyChanged;
}

This class is used by the MenuService:

public sealed class MenuService : INotifyPropertyChanged
{
    private static readonly Lazy<MenuService> lazy = new Lazy<MenuService>(() => new MenuService());

    public static MenuService Instance { get { return lazy.Value; } }

    private ObservableCollection<MyMenu> myMainMenu;

    public event PropertyChangedEventHandler PropertyChanged;

    private MenuService()
    {
        myMainMenu = new ObservableCollection<MyMenu>();
    }

    public ObservableCollection<MyMenu> MainMenu
    {
        get
        {
            return myMainMenu;
        }
        private set
        {
            myMainMenu = value;
        }
    }
}

In the constructor of the viewmodel i get the instance of the MenuService and add some items:

private void AddMenuItems()
{
    MyMenu OpenUserLoginMenuItem = new MyMenu
    {
        Header = "_Login",
        Image = "./Icons/IconLogin.png",
        Command = OpenSelectTestprocedureWindowCommand,
        CommandName = "OpenUserLoginDialogCommand"
    };

    MyMenu OpenSelectTestprocedureMenuItem = new MyMenu
    {
        Header = "_Select Testprocedure",
        Image = "./Icons/IconSelectTestprocedure.png",
        Command = OpenSelectTestprocedureWindowCommand,
        CommandName = "OpenSelectTestprocedureWindowCommand"
    };

    MainMenu.Add(OpenUserLoginMenuItem);
    MainMenu.Add(OpenSelectTestprocedureMenuItem);
}

Then i have a bindable property in the viewmodel:

public ObservableCollection<MyMenu> MainMenu
{
    get
    {
        return myMenuService.MainMenu;
    }
}

Here the command implementation as RelayCommand:

//in the constructor
OpenSelectTestprocedureWindowCommand = new RelayCommand<ShowTestschrittViewParameter>(OpenSelectTestablaufWindow, CanOpenSelectTestablaufWindow);
OpenUserLoginDialogCommand = new RelayCommand<Type>(OpenUserLoginDialog);

private void OpenUserLoginDialog(Type aWindowType)
{
    myNavigationService.ShowWindowModal(aWindowType);
}

private bool CanOpenSelectTestablaufWindow(ShowTestschrittViewParameter showTestschrittViewParameter)
{
    if (myDataService.CurrentTestProcedure != null)
    {
        if (myDataService.CurrentTestProcedure.TestProcedureState == Logic.Model.GlobalTypes.TestProcedureState.Running) return false;
    }
    return new ViewModelLocator().UserLoginDialogViewModel.User.NameIsValid;
}

private void OpenSelectTestablaufWindow(ShowTestschrittViewParameter showTestschrittViewParameter)
{
    myNavigationService.ShowTestschrittView(showTestschrittViewParameter);
}

Then in the MainView i have the following xaml:

<Menu Grid.Row="2" ItemsSource="{Binding MainMenu}" Name="DynamicMenu">
    <!--<Menu.ItemTemplate>
        <DataTemplate DataType="{x:Type luih:MyMenu}">
            <StackPanel>
                    <Label Content="{Binding Header}"/>
                    <Image Source="{Binding Image}"/>
                </StackPanel>
        </DataTemplate>
    </Menu.ItemTemplate>-->
    <Menu.ItemContainerStyle>
        <Style TargetType="{x:Type MenuItem}">
            <Setter Property="Header" Value="{Binding Header}"/>
            <Setter Property="Command" Value="{Binding Command}"/>
            <Setter Property="ItemsSource" Value="{Binding Children}"/>
            <Style.Triggers>
                <DataTrigger Binding="{Binding CommandName}" Value="OpenUserLoginDialogCommand">
                    <Setter Property="CommandParameter" Value="{x:Type local:UserLoginDialog}"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding CommandName}" Value="OpenSelectTestprocedureWindowCommand">
                    <Setter Property="CommandParameter" Value="{x:Type local:UserLoginDialog}"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Menu.ItemContainerStyle>
</Menu>

Attention. The CommandParameter binding type in the xaml is currently not correct. This is another problem, i will solve by my self. But for testing purposes it should work. It will give me an exception because of wrong type.

But when i do the CommandParameter binding in the Style.Trigger with the DataTrigger, the CanExecute property doesent update anymore at runtime. When i'm comment this section out, everything works fine. But then i have no CommandParameters.

Any help and suggestions are welcome.

i'm found the problem.

RelayCommand from mvvm light evaluetes the type of the parameter for the CanExecute function. This must be the correct declared type or null.

So for testing purposes i had to set the binding like so:

 <Style TargetType="{x:Type MenuItem}">
    <Setter Property="CommandParameter" Value="{x:Null}"/>
</Style>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM