MVVM混合行为和RelayCommand无法按预期工作

时间:2014-02-14 18:55:14

标签: c# wpf xaml mvvm

我正在尝试设置一些行为来控制MVVM应用程序中的窗口。

我的想法是行为可以在XAML中进行装饰设置并存在于他们自己的类中,允许在某些情况下在许多不同的视图中重用它们。

我还有一个绑定到RelayCommand的菜单项。这两种方法都可以自行完成,但是当我尝试将这两种方法结合起来时,这些方法无法正常工作:

MainView.xaml

<Window x:Class="CaseManager.Views.MainView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:VM="clr-namespace:CaseManager.ViewModels;assembly=CaseManager.ViewModels"
        xmlns:local="clr-namespace:CaseManager.Views"
        local:ExitBehavior.ExitWhen="{Binding ExitFlag}"
        Title="Main Window" Height="350" Width="525">

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

    <Grid>

        <Menu Height="20" VerticalAlignment="Top">
            <MenuItem Header="_File">
                <MenuItem Header="_Exit" Command="{Binding ExitCommand}" />
            </MenuItem>
        </Menu>

    </Grid>
</Window>

MainViewModel.cs

namespace CaseManager.ViewModels
{
    using System;
    using System.Collections.Generic;

    using Commands;

    public class MainViewModel : ViewModelBase
    {
        # region RelayCommands
        private RelayCommand _exitCommand;
        public RelayCommand ExitCommand
        {
            get { return _exitCommand; }
            private set { _exitCommand = value; }
        }
        #endregion

        private bool _exitFlag;
        public bool ExitFlag { 
            get { return _exitFlag; } 
            private set { _exitFlag = value; } 
        }

        public MainViewModel()
        {
            ExitCommand = new RelayCommand(ExitCommand_Execute);
            ExitFlag = false;
        }

        private void ExitCommand_Execute(object obj)
        {
            System.Console.WriteLine("Command executed");
            ExitFlag = true;
        }
    }
}

ExitCommand.cs

namespace CaseManager.Views
{
    using System;
    using System.Windows;

    public static class ExitBehavior
    {
        public static bool GetExitWhen(DependencyObject obj)
        {
            return (bool)obj.GetValue(ExitWhenProperty);
        }

        public static void SetExitWhen(DependencyObject obj, bool value)
        {
            obj.SetValue(ExitWhenProperty, value);
        }

        public static readonly DependencyProperty ExitWhenProperty =
            DependencyProperty.RegisterAttached(
                "ExitWhen", typeof(bool), typeof(ExitBehavior),
                new UIPropertyMetadata(OnExitWhenChanged));

        static void OnExitWhenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            System.Console.WriteLine("Behavior executed");
            Environment.Exit(0);
        }
    }
}

当我在菜单中选择退出时,按原样运行上面的代码将在控制台中输出'Command executed'。所以我知道relay命令工作正常。但是行为不是。

如果我将MainViewModel的构造函数更改为将ExitFlag设置为true,程序将启动,将“执行行为”打印到控制台,然后退出。

ExitBehavior和RelayCommand都可以自行工作,但是当我尝试通过在relay命令中设置ExitFlag来触发ExitBehavior时,它们不能一起使用。我在这里错过了什么吗?

为简洁起见,RelayCommand.cs

namespace CaseManager.Commands
{
    using System;
    using System.Windows.Input;

    public class RelayCommand : ICommand
    {
        #region Fields

        readonly Action<object> _execute;
        readonly Predicate<object> _canExecute;

        #endregion

        #region Constructors

        public RelayCommand(Action<object> execute)
            : this(execute, null)
        {
        }

        public RelayCommand(Action<object> execute, Predicate<object> canExecute)
        {
            if (execute == null)
                throw new ArgumentNullException("execute");

            _execute = execute;
            _canExecute = canExecute;
        }
        #endregion

        #region ICommand Members

        public bool CanExecute(object parameter)
        {
            return _canExecute == null ? true : _canExecute(parameter);
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public void Execute(object parameter)
        {
            _execute(parameter);
        }

        #endregion
    }
}

2 个答案:

答案 0 :(得分:3)

您必须通知绑定引擎,您的属性ExitFlag已更改。假设ViewModelBase以某种方式实现INotifyPropertyChanged

       public bool ExitFlag 
       { 
            get { return _exitFlag; } 
            private set 
            { 
               _exitFlag = value; 
               OnPropertyChanged("ExitFlag");
            } 
        }

其中OnPropertyChanged是一种方法,可以引发INPC.PropertyChanged事件。

答案 1 :(得分:0)

正如我从你的代码中所理解的那样,在这里:

local:CloseBehavior.CloseWhen="{Binding CloseFlag}"

必须有一个属性ExitFlag

其次,此属性应从INotifyPropertyChanged接口调用OnPropertyChanged方法来更新属性,而在XAML中应该如下:

local:CloseBehavior.CloseWhen="{Binding Path=ExitFlag, Mode=TwoWay}"