WPF - 如何处理从MainWindow中的UserControl引发的事件并更新ViewModel

时间:2017-11-21 06:14:05

标签: wpf

我正在尝试在UserControl1中引发一个事件,但我无法弄清楚如何处理该事件以在MainWindow中显示UserControl2。

MainWindow.xaml

<Window x:Class="WpfApp1.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:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">

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

    <Window.Resources>
        <DataTemplate x:Key="Template1" DataType="{x:Type local:ViewModel1}">
            <local:UserControl1 />
        </DataTemplate>
        <DataTemplate x:Key="Template2" DataType="{x:Type local:ViewModel1}">
            <local:UserControl2 />
        </DataTemplate>
    </Window.Resources>
    <Grid>
        <ContentControl Content="{Binding }">
            <ContentControl.Style>
                <Style TargetType="{x:Type ContentControl}">
                    <Setter Property="ContentTemplate" Value="{StaticResource Template1}" />
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding UserControlId}" Value="2">
                            <Setter Property="ContentTemplate" Value="{StaticResource Template2}" />
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </ContentControl.Style>
        </ContentControl>
    </Grid>
</Window>

MainWindow.cs

using System.Windows;

namespace WpfApp1
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}

UserControl1.xaml

<UserControl x:Class="WpfApp1.UserControl1"
             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">
    <Grid>
        <Label Content="User Control 1" />
        <Button x:Name="btnNext" Content="Next" Click="btnNext_Click"></Button>
    </Grid>
</UserControl>

UserControl1.cs

using System.Windows;
using System.Windows.Controls;

namespace WpfApp1
{
    public partial class UserControl1 : UserControl
    {
        public static readonly RoutedEvent MyEvent = EventManager.RegisterRoutedEvent(
            "MyEventName",
            RoutingStrategy.Bubble,
            typeof(RoutedEventHandler),
            typeof(UserControl1)
        );

        public event RoutedEventHandler LoginEventHandler
        {
            add { AddHandler(MyEvent, value); }
            remove { RemoveHandler(MyEvent, value); }

        }
        public UserControl1()
        {
            InitializeComponent();
        }

        private void btnNext_Click(object sender, RoutedEventArgs e)
        {
            var eventArgs = new RoutedEventArgs(MyEvent);
            RaiseEvent(eventArgs);
        }
    }
}

UserControl2.xaml

<UserControl x:Class="WpfApp1.UserControl2"
             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">
    <Grid>
        <Label Content="User Control 2" />
    </Grid>
</UserControl>

UserControl2.cs

using System.Windows.Controls;

namespace WpfApp1
{
    public partial class UserControl2 : UserControl
    {
        public UserControl2()
        {
            InitializeComponent();
        }
    }
}

ViewModel1.cs

using System.ComponentModel;

namespace WpfApp1
{
    public class ViewModel1 : INotifyPropertyChanged
    {
        public int UserControlId { get; set; }

        public ViewModel1()
        {
            UserControlId = 2;
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}

我认为我必须更新ViewModel1中的UserControlId属性来更改用户控件但是在哪里处理以及如何让ViewModel1实例更改属性?

编辑1 对于ViewModel1构造函数中的拼写错误,我打算将UserControlId属性初始化为1,而不是2。

当我运行它时,我看到主窗口中加载了UserControl1的窗口。 UserControl1有一个按钮“下一步”。 单击UserControl1中的按钮,我想显示UserControl2。 所以我在按钮点击事件处理程序中提升MyEvent路由事件。 现在,在哪里附加和编写MyEvent的事件处理程序以及如何让ViewModel1实例更改它的UserControlId属性?

3 个答案:

答案 0 :(得分:1)

我终于开始工作了。在ViewModel1中使用属性名称更改了引发的事件,在MainWindow中添加了事件侦听器,

以下是更改的文件

ViewModel1.cs

using System.ComponentModel;

namespace WpfApp1
{
    public class ViewModel1 : INotifyPropertyChanged
    {
        private int _userControlId;

        public int UserControlId { 
            get { return _userControlId; }

            set 
            {
                _userControlId = value;
                RaisePropertyChanged("UserControlId");
            }
        }

        private void RaisePropertyChanged(string v = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(v));
        }

        public ViewModel1()
        {
            UserControlId = 1;
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}

MainWindow.cs

using System.Windows;

namespace WpfApp1
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            AddHandler(UserControl1.MyEvent, new RoutedEventHandler(OnMyEvent));
        }

        private void OnMyEvent(object sender, RoutedEventArgs e)
        {
            var vm = (ViewModel1)DataContext;
            vm.UserControlId = "2";
        }
    }
}

答案 1 :(得分:0)

不要以为这里需要路由事件。将ViewModel分配给MainWindow并添加Property&#34; UserControlId&#34;在MainViewModel中。然后,当您在MainWindow的DataContext级别上编写UserControlId值的触发器时,您的XAML代码将正常工作。

答案 2 :(得分:0)

为了做到这一点,你需要在两个视图模型之间进行调解。调解的一种方法是将主窗口作为调解器:它侦听来自子节点的事件,然后将它们路由到所需的目的地。另一种方式是通过事件聚合器。大多数MVVM框架都有一个。我在WPF世界中所知道的最好的开源是Caliburn Micro中的那个。您在一个视图模型中订阅了某种消息,然后从其他视图模型发送事件,事件聚合器确保事件被路由到其目标。有关详细信息,请参阅其文档。