Member 12814959 Ответов: 1

Как выполнить команду, которая находится в другом классе, и привязать ее к кнопке в другом окне?


Я новичок в WPF и работаю над приложением WPF. Я построил навигационную систему, глядя на какой-то образец. У меня есть кнопки в главном окне, которые привязываются к NavigationViewModel

<Button Content="Items List" Command="{Binding ItemsListCmd}"/>
<Button Content="Items" Command="{Binding ItemsEditCmd}"/>


В MainWinodw.язык XAML.в CS
public MainWindow()
{
    InitializeComponent();
    this.DataContext = new NavigationViewModel();
}

В MainWindow.xaml у меня есть привязки, как показано ниже

<ContentControl x:Name="Pages" DockPanel.Dock="Right" Content="{Binding SelectedViewModel}"/>

Вот класс NavigationViewModel, имеющий команды ItemsListCmd и ItemsEditCmd.

namespace CC
{
	class NavigationViewModel : INotifyPropertyChanged
	{
		public event PropertyChangedEventHandler PropertyChanged;

		public ICommand ItemsListCmd { get; set; }
		public ICommand ItemsEditCmd { get; set; }

		private object selectedViewModel;

		public object SelectedViewModel
		{
			get {
				return selectedViewModel;
			}
			set {
				selectedViewModel = value;
				OnPropertyChanged("SelectedViewModel");
			}
		}

		public NavigationViewModel()
		{
			ItemsListCmd = new BaseCommand(OpenItemsList);
			ItemsEditCmd = new BaseCommand(OpenItemsEdit);
		}

		private void OpenItemsList(object obj)
		{
			SelectedViewModel = new ItemsListView();
		}

		private void OpenItemsEdit(object obj)
		{
			SelectedViewModel = new ItemsEditViewModel();
		}

		private void OnPropertyChanged(string propName)
		{
			if (PropertyChanged != null)
			{
				PropertyChanged(this, new PropertyChangedEventArgs(propName));
			}
		}
	}
}

Я хочу знать, как я могу выполнить команды ItemsListCmd или ItemsEditCmd из кода позади какого-то пользовательского события/действия, например, когда пользователь нажимает на кнопку отмены или в любом другом представлении/ViewModel?

Что я уже пробовал:

Я попытался вызвать класс" NavigationViewModel", но так как он используется в качестве привязки, то не получаю прямого доступа к этому классу.
Я также попытался поискать решение в google, но ничего не нашел.

Member 12814959

@Graeme_Grant, спасибо за ваш ответ, но я бы предпочел решение без использования моих сторонних фреймворков. Как и на данном этапе, мне нужно какое-то быстрое решение без необходимости изучать другой фреймворк поверх WPF.

Graeme_Grant

MVVMLight-это не фреймворк, это библиотека. Если вы не хотите добавлять их DLL в свой проект, их исходный код библиотеки является открытым исходным кодом, и вы можете добавить его в свое собственное приложение.

Graeme_Grant

Вот статья CodeProject, в которой объясняется, как реализовать команду для использования с кнопками: Базовый пример использования MVVM и ICommand[^] а вот статья о реализации паттерна посредника [Messaging] : Шаблон медиатора MVVM[^]

Я по-прежнему рекомендую библиотеку MVVMLight как лучшее решение. ;)

1 Ответов

Рейтинг:
0

Graeme_Grant

Взгляните на MVVM Light Toolkit[^]- Эта библиотека упрощает ряд концепций MVVM, включая команды.

Например, я использую элемент управления кадром для размещения двух моих тестовых навигационных представлений:

<Window 

    x:Class="WpfApp2.MainWindow"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"



    mc:Ignorable="d"

    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"



    xmlns:local="clr-namespace:WpfApp2"



    Title="WPF Navigation Example" Height="350" Width="525">

    <Window.Resources>
        <Style x:Key="FrameStyle1" TargetType="{x:Type Frame}">
            <Setter Property="BorderThickness" Value="0" />
            <Setter Property="NavigationUIVisibility" Value="Hidden" />
            <Setter Property="Margin" Value="0" />
            <Setter Property="SnapsToDevicePixels" Value="True" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Frame}">
                        <ContentPresenter x:Name="PART_FrameCP"/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>

    <Window.DataContext>
        <local:MainViewModel/>
    </Window.DataContext>

    <DockPanel>
        <Border DockPanel.Dock="top" Background="AliceBlue" Padding="10">
            <StackPanel Orientation="Horizontal" >
                <Button Content="Page1" Padding="10 5" Margin="0 0 10 0"

                        Command="{Binding MenuCommand}" CommandParameter="Page1"/>
                <Button Content="Page2" Padding="10 5" Margin="0 0 10 0"

                        Command="{Binding MenuCommand}" CommandParameter="Page2"/>
            </StackPanel>
        </Border>
        <Frame Source="{Binding NavigateTo}" Style="{StaticResource FrameStyle1}" />
    </DockPanel>

</Window>

ViewModel для главного окна
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.CommandWpf;

namespace WpfApp2
{
    public class MainViewModel : ViewModelBase
    {
        public MainViewModel()
        {
            MenuCommand = new RelayCommand<string>(msg => NavigateTo = $"{msg}.xaml");
        }

        public RelayCommand<string> MenuCommand { get; }

        private string navigateTo = "Page1.xaml";
        public string NavigateTo
        {
            get => navigateTo;
            set => Set(ref navigateTo, value);
        }
    }
}

И две страницы (навигационные представления)
<Page x:Class="WpfApp2.Page1"

      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:WpfApp2"

      mc:Ignorable="d" 

      d:DesignHeight="300" d:DesignWidth="300"

      Title="Page1">

    <Grid>
        <Viewbox>
            <TextBlock Text="Page 1"/>
        </Viewbox>
    </Grid>
</Page>

<Page x:Class="WpfApp2.Page2"

      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:WpfApp2"

      mc:Ignorable="d" 

      d:DesignHeight="300" d:DesignWidth="300"

      Title="Page2">

    <Grid>
        <Viewbox>
            <TextBlock Text="Page 2"/>
        </Viewbox>
    </Grid>
</Page>

Теперь я могу нажать на кнопки, и вид изменится.

Обновление

Прочитав еще раз ваш вопрос, я понимаю, что вам могут понадобиться только навигационные кнопки на дочерних представлениях. Итак, вот еще одно решение, использующее Mvvmlight Messaging. Система обмена сообщениями обеспечивает простую связь между представлениями.

Главное окно:
<Window

    x:Class="WpfApp1.MainWindow"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"



    mc:Ignorable="d"

    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"



    xmlns:local="clr-namespace:WpfApp1"



    Title="WPF Navigation Example 2" Height="350" Width="525">

    <Window.Resources>
        <Style x:Key="FrameStyle1" TargetType="{x:Type Frame}">
            <Setter Property="BorderThickness" Value="0" />
            <Setter Property="NavigationUIVisibility" Value="Hidden" />
            <Setter Property="Margin" Value="0" />
            <Setter Property="SnapsToDevicePixels" Value="True" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Frame}">
                        <ContentPresenter x:Name="PART_FrameCP"/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>

    <Window.DataContext>
        <local:MainViewModel/>
    </Window.DataContext>

    <DockPanel>
        <Frame Source="{Binding NavigateTo}" Style="{StaticResource FrameStyle1}" />
    </DockPanel>

</Window>

ViewModel для главного окна
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Messaging;

namespace WpfApp1
{
    public class MainViewModel : ViewModelBase
    {
        public MainViewModel()
        {
            Messenger.Default.Register<MenuNavigationMessage>
                (
                     this,
                     (navMsg) => NavigateTo = $"{navMsg.Message}.xaml"
                );
        }

        private string navigateTo = "Page1.xaml";
        public string NavigateTo
        {
            get => navigateTo;
            set => Set(ref navigateTo, value);
        }
    }
}

Класс обмена сообщениями, используемый для передачи типизированных сообщений между моделями представления
namespace WpfApp1
{
    public class MenuNavigationMessage
    {
        public MenuNavigationMessage(string msg)
        {
            Message = msg;
        }
        public string Message { get; }
    }
}

Базовая модель представления для навигационных представлений с общей функциональностью-сообщениями и кнопками:
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;

namespace WpfApp1
{
    public abstract class NavigationViewModelBase : ViewModelBase
    {
        public NavigationViewModelBase()
        {
            MenuCommand = new RelayCommand<string>(msg => Messenger.Default.Send(new MenuNavigationMessage(msg)));
        }

        public RelayCommand<string> MenuCommand { get; }
    }
}

И две страницы (навигационные представления)
<Page x:Class="WpfApp1.Page1"

      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:WpfApp1"

      mc:Ignorable="d"  d:DesignHeight="300" d:DesignWidth="300"

      Title="Page1">

    <Page.DataContext>
        <local:Page1ViewModel/>
    </Page.DataContext>

    <Grid>
        <Button Content="Go to Page2" Padding="10 5" Margin="10"

                HorizontalAlignment="left" VerticalAlignment="Top"

                        Command="{Binding MenuCommand}" CommandParameter="Page2"/>
        <Viewbox>
            <TextBlock Text="Page 1"/>
        </Viewbox>
    </Grid>
</Page>

<Page x:Class="WpfApp1.Page2"

      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:WpfApp1"

      mc:Ignorable="d" 

      d:DesignHeight="300" d:DesignWidth="300"

      Title="Page2">

    <Page.DataContext>
        <local:Page2ViewModel/>
    </Page.DataContext>

    <Grid>
        <Button Content="Go to Page1" Padding="10 5" Margin="10"

                HorizontalAlignment="left" VerticalAlignment="Top"

                        Command="{Binding MenuCommand}" CommandParameter="Page1"/>
        <Viewbox>
            <TextBlock Text="Page 2"/>
        </Viewbox>
    </Grid>
</Page>

И viewmodels для страниц
namespace WpfApp1
{
    public class Page1ViewModel : NavigationViewModelBase
    {
    }

    public class Page2ViewModel : NavigationViewModelBase
    {
    }
}

Теперь кнопки навигации находятся на каждой странице. Щелчок по ним переключит вид.