Laden...

Wie bekomme ich ein korrektes CommandParameter Binding mit dynamischem Menüs in Wpf und Xaml

Erstellt von return01 vor 4 Jahren Letzter Beitrag vor 4 Jahren 1.932 Views
R
return01 Themenstarter:in
5 Beiträge seit 2017
vor 4 Jahren
Wie bekomme ich ein korrektes CommandParameter Binding mit dynamischem Menüs in Wpf und Xaml

Hallo,
ich habe ein dynamischen Menü Service in meiner WPF Applikation. Dabei soll jedes Command
verschiedene CommandParameter bekommen können.

Das Problem:
Wenn ich mit meiner Lösung die CommandParameter in Xaml setze, wird die CanExecute Property
nicht mehr ausgewertet.

Was ich soweit programmiert habe:

Ich benutze mvvm-light und fody-propertychanged.

Hier die Menü Klasse:

    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;
    }

Diese Klasse wird vom Menü Service benutzt:

    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;
            }
        }
    }

Im Konstruktor vom Viewmodel hole ich mir die Instanz vom Menüservice und füge ein paar Menüpunkte hinzu:

    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);
    }

Im Viewmodel habe ich die bindable property:

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

Hier die Kommandos als 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);
    }

Und im MainView habe ich das folgende 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>

Achtung. Der CommandParameter type im Binding ist noch nicht korrekt. Diese Problem wollte ich später lösen. Aber zum testen sollte es reichen. Es gibt mir halt eine Ausnahme, weil der Parameter vom falschen Typ ist.

Wenn ich die CommandParameter im Style.Trigger mit dem DataTrigger setze, wird die CanExecute Property nicht mehr ausgewertet. Das entsprechende MenüItem bleibt grau.

Wenn ich diesen Teil im xaml auskommentiere, funktioniert alles wie erwartet. Aber dann fehlen mir die Parameter.

Jeder Tipp in die richtige Richtung ist willkommen.

R
return01 Themenstarter:in
5 Beiträge seit 2017
vor 4 Jahren

Hallo,

ich habe die Lösung gefunden. Das Problem war hausgemacht:

RelayCommand aus mvvm light wertet den Typ aus mit dem die CanExecute Funktion aufgerufen wird.
Ist dieser vom falschen Typ oder nicht null, wird diese Funktion nicht aufgerufen.

Die Lösung zum Testen, ohne den korrekten Typ zu übergeben, wäre gewesen:


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