更新对嵌套ViewModel的命令的引用? [英] Updating a reference to a command of a nested ViewModel?

查看:356
本文介绍了更新对嵌套ViewModel的命令的引用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



我试图使用MVVM模式。

p>

如何更新对链接到子视图模型的viewmodel中的命令的引用?



ve得到一个视图(MainView)绑定到一个viewmodel(MainViewModel)。
在MainView上,我有一个绑定到viewmodel(SummaryViewModel)的另一个视图(SummaryView)的实例。 SummaryViewModel包含第三个视图模型(SummaryFilterViewModel)的集合。



在SummaryView中,有一个TabControl,并且每个选项卡绑定到SummaryViewodel中的一个SummaryFilterViewModel实例



在MainView上有一个绑定到MainViewModel中的命令的按钮。



发生的是命令逻辑居住在SummaryFilterViewModel类中。



我试图做的是这样的:


  1. 存储在SummaryViewModel集合中的个别SummaryFilterViewModel对象保存ShoutCommand的实际实现。

  2. CommandReference对象MainView的XAML绑定到MainViewModel的ShoutCommand属性

  3. MainViewModel的ShoutCommand属性返回对存储在MainViewModel中的SummaryViewModel对象的ShoutCommand属性的引用。

  4. SummaryViewModel的ShoutCommand属性返回对当前选择的SummaryFilterViewModel的ShoutCommand属性的引用。

发生的是,当用户更改选项卡时,命令不会更新。



我有办法解决这个问题吗?
我需要将命令的实现移动到SummaryViewModel类中吗?



先感谢任何帮助!



我的解决方案的来源如下:



ViewModels



strong> SummaryView.xaml

 < UserControl x:Class =NestedCommands.Views.SummaryView 
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
mc:Ignorable =d
d:DesignHeight =309d:DesignWidth =476>
< Grid>
< TabControl SelectedIndex ={Binding SelectedTabIndex}>
< TabItem DataContext ={Binding Filters [0]}Header ={Binding FilterName}>
< ListBox ItemsSource ={Binding ListData}/>
< / TabItem>
< TabItem DataContext ={Binding Filters [1]}Header ={Binding FilterName}>
< ListBox ItemsSource ={Binding ListData}/>
< / TabItem>
< TabItem DataContext ={Binding Filters [2]}Header ={Binding FilterName}>
< ListBox ItemsSource ={Binding ListData}/>
< / TabItem>
< / TabControl>
< / Grid>



MainView.xaml

 < Window x:Class =NestedCommands.Views.MainView
xmlns =http://schemas.microsoft.com/winfx/2006/xaml/presentation
xmlns:x =http://schemas.microsoft.com/winfx/2006/xaml
xmlns:v =clr-namespace:NestedCommands.Views
xmlns:c =clr-namespace:NestedCommands.Commands
Title =MainViewHeight =336Width = 420>
< Window.Resources>
< c:CommandReference x:Key =ShoutCommandReferenceCommand ={Binding ShoutCommand}/>
< /Window.Resources>
< Grid>
< Grid.RowDefinitions>
< RowDefinition Height =1 */>
< RowDefinition Height =Auto/>
< /Grid.RowDefinitions>
< v:SummaryView Grid.Row =0
DataContext ={Binding SummaryViewModel}/>
< Button Content =Shout Command
Grid.Row =1
Command ={StaticResource ShoutCommandReference}/>
< / Grid>






命令类



CommandReference.cs

 使用系统; 
使用System.Windows;
using System.Windows.Input;

namespace NestedCommands.Commands
{
///< summary>
///这个类通过暴露一个Command依赖关系属性,使得XAML标记中的一个键绑定与一个View模型中定义的命令
///相关联。
///当从XAML进行数据绑定时,类派生自Freezable来解决WPF中的限制。
///< / summary>
public class CommandReference:Freezable,ICommand
{
public CommandReference()
{
// Blank
}

静态readonly DependencyProperty CommandProperty = DependencyProperty.Register(Command,typeof(ICommand),typeof(CommandReference),new PropertyMetadata(new PropertyChangedCallback(OnCommandChanged)));

public ICommand命令
{
get {return(ICommand)GetValue(CommandProperty); }
set {SetValue(CommandProperty,value); }
}

#region ICommand Members

public bool CanExecute(object parameter)
{
if(Command!= null)
return Command.CanExecute(parameter);
return false;
}

public void Execute(object parameter)
{
Command.Execute(parameter);
}

public event EventHandler CanExecuteChanged;

private static void OnCommandChanged(DependencyObject d,DependencyPropertyChangedEventArgs e)
{
CommandReference commandReference = d as CommandReference;
ICommand oldCommand = e.OldValue as ICommand;
ICommand newCommand = e.NewValue as ICommand;

if(oldCommand!= null)
{
oldCommand.CanExecuteChanged - = commandReference.CanExecuteChanged;
}
if(newCommand!= null)
{
newCommand.CanExecuteChanged + = commandReference.CanExecuteChanged;
}
}

#endregion

#region Freezable

protected override Freezable CreateInstanceCore()
{
throw new NotImplementedException();
}

#endregion
}
}


$ b b

DelegateCommand.cs

  
using System.Collections.Generic;
使用System.Windows;
using System.Windows.Input;

namespace NestedCommands.Commands
{
///< summary>
///这个类允许将命令逻辑委托给作为参数传递的方法
///,并使View能够将命令绑定到不是元素树的一部分的对象。
///< / summary>
public class DelegateCommand:ICommand
{
#region构造函数

///< summary>
///构造函数
///< / summary>
public DelegateCommand(Action executeMethod)
:this(executeMethod,null,false)
{
}

///< summary>
///构造函数
///< / summary>
public DelegateCommand(Action executeMethod,Func< bool> canExecuteMethod)
:this(executeMethod,canExecuteMethod,false)
{
}

/// < summary>
///构造函数
///< / summary>
public DelegateCommand(Action executeMethod,Func< bool> canExecuteMethod,bool isAutomaticRequeryDisabled)
{
if(executeMethod == null)
{
throw new ArgumentNullException(executeMethod );
}

_executeMethod = executeMethod;
_canExecuteMethod = canExecuteMethod;
_isAutomaticRequeryDisabled = isAutomaticRequeryDisabled;
}

#endregion

#region公共方法

///< summary>
///确定命令是否可以执行的方法
///< / summary>
public bool CanExecute()
{
if(_canExecuteMethod!= null)
{
return _canExecuteMethod();
}
return true;
}

///< summary>
///执行命令
///< / summary>
public void Execute()
{
if(_executeMethod!= null)
{
_executeMethod();
}
}

///< summary>
///对此命令启用或禁用CommandManager的自动重新查询的属性
///< / summary>
public bool IsAutomaticRequeryDisabled
{
get
{
return _isAutomaticRequeryDisabled;
}
set
{
if(_isAutomaticRequeryDisabled!= value)
{
if(value)
{
CommandManagerHelper。 RemoveHandlersFromRequerySuggested(_canExecuteChangedHandlers);
}
else
{
CommandManagerHelper.AddHandlersToRequerySuggested(_canExecuteChangedHandlers);
}
_isAutomaticRequeryDisabled = value;
}
}
}

///< summary>
///引发CanExecuteChaged事件
///< / summary>
public void RaiseCanExecuteChanged()
{
OnCanExecuteChanged();
}

///< summary>
///受保护的虚拟方法引发CanExecuteChanged事件
///< / summary>
protected virtual void OnCanExecuteChanged()
{
CommandManagerHelper.CallWeakReferenceHandlers(_canExecuteChangedHandlers);
}

#endregion

#region ICommand成员

///< summary>
/// ICommand.CanExecuteChanged实现
///< / summary>
public event EventHandler CanExecuteChanged
{
add
{
if(!_isAutomaticRequeryDisabled)
{
CommandManager.RequerySuggested + = value;
}
CommandManagerHelper.AddWeakReferenceHandler(ref _canExecuteChangedHandlers,value,2);
}
remove
{
if(!_isAutomaticRequeryDisabled)
{
CommandManager.RequerySuggested - = value;
}
CommandManagerHelper.RemoveWeakReferenceHandler(_canExecuteChangedHandlers,value);
}
}

bool ICommand.CanExecute(object parameter)
{
return CanExecute();
}

void ICommand.Execute(object parameter)
{
Execute();
}

#endregion

#region数据

private readonly Action _executeMethod = null;
private readonly Func< bool> _canExecuteMethod = null;
private bool _isAutomaticRequeryDisabled = false;
private List< WeakReference> _canExecuteChangedHandlers;

#endregion
}

///< summary>
///这个类允许将命令逻辑委托给作为参数传递的方法
///,并使View能够将命令绑定到不是元素树的一部分的对象。
///< / summary>
///< typeparam name =T>传递给委托的参数的类型< / typeparam>
public class DelegateCommand< T> :ICommand
{
#region构造函数

///< summary>
///构造函数
///< / summary>
public DelegateCommand(Action< T> executeMethod)
:this(executeMethod,null,false)
{
}

///< summary> ;
///构造函数
///< / summary>
public DelegateCommand(Action< T> executeMethod,Func< T,bool> canExecuteMethod)
:this(executeMethod,canExecuteMethod,false)
{
}

///< summary>
///构造函数
///< / summary>
public DelegateCommand(Action< T> executeMethod,Func< T,bool> canExecuteMethod,bool isAutomaticRequeryDisabled)
{
if(executeMethod == null)
{
throw new ArgumentNullException(executeMethod);
}

_executeMethod = executeMethod;
_canExecuteMethod = canExecuteMethod;
_isAutomaticRequeryDisabled = isAutomaticRequeryDisabled;
}

#endregion

#region公共方法

///< summary>
///确定命令是否可以执行的方法
///< / summary>
public bool CanExecute(T parameter)
{
if(_canExecuteMethod!= null)
{
return _canExecuteMethod(parameter);
}
return true;
}

///< summary>
///执行命令
///< / summary>
public void Execute(T parameter)
{
if(_executeMethod!= null)
{
_executeMethod(parameter);
}
}

///< summary>
///引发CanExecuteChaged事件
///< / summary>
public void RaiseCanExecuteChanged()
{
OnCanExecuteChanged();
}

///< summary>
///受保护的虚拟方法引发CanExecuteChanged事件
///< / summary>
protected virtual void OnCanExecuteChanged()
{
CommandManagerHelper.CallWeakReferenceHandlers(_canExecuteChangedHandlers);
}

///< summary>
///对此命令启用或禁用CommandManager的自动重新查询的属性
///< / summary>
public bool IsAutomaticRequeryDisabled
{
get
{
return _isAutomaticRequeryDisabled;
}
set
{
if(_isAutomaticRequeryDisabled!= value)
{
if(value)
{
CommandManagerHelper。 RemoveHandlersFromRequerySuggested(_canExecuteChangedHandlers);
}
else
{
DriverManagerHelper.AddHandlersToRequerySuggested(_canExecuteChangedHandlers);
}
_isAutomaticRequeryDisabled = value;
}
}
}

#endregion

#region ICommand Members

///<摘要>
/// ICommand.CanExecuteChanged实现
///< / summary>
public event EventHandler CanExecuteChanged
{
add
{
if(!_isAutomaticRequeryDisabled)
{
CommandManager.RequerySuggested + = value;
}
CommandManagerHelper.AddWeakReferenceHandler(ref _canExecuteChangedHandlers,value,2);
}
remove
{
if(!_isAutomaticRequeryDisabled)
{
CommandManager.RequerySuggested - = value;
}
CommandManagerHelper.RemoveWeakReferenceHandler(_canExecuteChangedHandlers,value);
}
}

bool ICommand.CanExecute(对象参数)
{
//如果T是值类型,参数不是
// set yet,then return false if CanExecute delegate
// exists,else return true
if(parameter == null&
typeof(T).IsValueType)
{
return(_canExecuteMethod == null);
}
return CanExecute((T)parameter);
}

void ICommand.Execute(object parameter)
{
Execute((T)parameter);
}

#endregion

#region数据

private readonly Action< T> _executeMethod = null;
private readonly Func< T,bool> _canExecuteMethod = null;
private bool _isAutomaticRequeryDisabled = false;
private List< WeakReference> _canExecuteChangedHandlers;

#endregion
}

///< summary>
///此类包含CommandManager的方法,有助于避免使用弱引用的
///引起的内存泄漏。
///< / summary>
内部类CommandManagerHelper
{
内部静态无效CallWeakReferenceHandlers(List< WeakReference>处理程序)
{
if(handlers!= null)
{
//在我们调用它们之前,处理程序的快照,因为处理程序
//可能导致数组在我们读取它时被修改。

EventHandler [] callees = new EventHandler [handlers.Count];
int count = 0;

for(int i = handlers.Count - 1; i> = 0; i--)
{
WeakReference reference = handlers [i];
EventHandler handler = reference.Target as EventHandler;
if(handler == null)
{
//清理已收集的旧处理程序
handlers.RemoveAt(i);
}
else
{
callees [count] = handler;
count ++;
}
}

//调用我们创建快照的处理程序
for(int i = 0; i {
EventHandler handler = callees [i];
handler(null,EventArgs.Empty);
}
}
}

内部静态无效AddHandlersToRequerySuggested(List< WeakReference>处理程序)
{
if(handlers!= null)
{
foreach(处理程序中的WeakReference handlerRef)
{
EventHandler handler = handlerRef.Target as EventHandler;
if(handler!= null)
{
CommandManager.RequerySuggested + = handler;
}
}
}
}

内部静态void RemoveHandlersFromRequerySuggested(List< WeakReference>处理程序)
{
if foreach(处理程序中的WeakReference handlerRef)
{
EventHandler handler = handlerRef.Target as EventHandler;
if(handler!= null)
{
CommandManager.RequerySuggested - = handler;
}
}
}
}

内部静态void AddWeakReferenceHandler(ref List< WeakReference>处理程序,EventHandler处理程序)
{
AddWeakReferenceHandler(ref handlers,handler,-1);
}

内部静态void AddWeakReferenceHandler(ref List< WeakReference> handler,EventHandler handler,int defaultListSize)
{
if(handlers == null)
{
handlers =(defaultListSize> 0?new List< WeakReference>(defaultListSize):new List< WeakReference>
}

handlers.Add(new WeakReference(handler));
}

内部静态void RemoveWeakReferenceHandler(List< WeakReference>处理程序,EventHandler处理程序)
{
if(handlers!= null)
{
for(int i = handlers.Count - 1; i> = 0; i--)
{
WeakReference reference = handlers [i];
EventHandler existingHandler = reference.Target as EventHandler;
if((existingHandler == null)||(existingHandler == handler))
{
//清除已收集的旧处理程序
//处理程序将被删除。
handlers.RemoveAt(i);
}
}
}
}
}
}






查看模型



ViewModelBase.cs

 使用系统; 
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;

命名空间NestedCommands.ViewModels
{
类ViewModelBase:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;

protected void OnPropertyChanged(object sender,string propertyName)
{
if(this.PropertyChanged!= null)
{
PropertyChanged PropertyChangedEventArgs(propertyName));
}
}
}
}

em> MainViewModel.cs

  
using System.Windows.Input;

namespace NestedCommands.ViewModels
{
class MainViewModel:ViewModelBase
{
public MainViewModel()
{
_SummaryViewModel = new SummaryViewModel();
}

private SummaryViewModel _SummaryViewModel;
public SummaryViewModel SummaryViewModel
{
get {return _SummaryViewModel; }
set
{
_SummaryViewModel = value;
OnPropertyChanged(this,SummaryViewModel);
}
}

public ICommand ShoutCommand
{
get {return _SummaryViewModel.ShoutCommand; }
}
}
}

> SummaryViewModel.cs

 使用System; 
using System.Collections.Generic;
using System.Linq;
using System.Text;
使用NestedCommands.Command;
using System.Windows.Input;

namespace NestedCommands.ViewModels
{
class SummaryViewModel:ViewModelBase
{
#region构造函数

public SummaryViewModel b $ b {
List< SummaryFilterViewModel> filters = new List< SummaryFilterViewModel>();
filters.Add(new SummaryFilterViewModel(Filter 1));
filters.Add(new SummaryFilterViewModel(Filter 2));
filters.Add(new SummaryFilterViewModel(Filter 3));
Filters = filters;
}

#endregion

#region属性

private List< SummaryFilterViewModel> _Filters;
public List< SummaryFilterViewModel>过滤器
{
get {return _Filters; }
set
{
_Filters = value;
OnPropertyChanged(this,Filters);
}
}

private int _SelectedTabIndex;
public int SelectedTabIndex
{
get {return _SelectedTabIndex; }
set
{
_SelectedTabIndex = value;
OnPropertyChanged(this,SelectedTabIndex);
}
}

#endregion

#region命令参考

public ICommand ShoutCommand
{
get {return Filters [SelectedTabIndex] .ShoutCommand; }
}

#endregion
}
}

SummaryFilterViewModel.cs

  
using System.Collections.Generic;
using System.Linq;
using System.Text;
使用NestedCommands.Command;
using System.Windows.Input;

命名空间NestedCommands.ViewModels
{
类SummaryFilterViewModel:ViewModelBase
{
#region构造函数

public SummaryFilterViewModel )
{
this.FilterName = FilterName;

List< string> listData = new List< string>();
for(int i = 1; i <10; i ++)
{
listData.Add(string.Format({0}:{1},FilterName,i)) ;
}
ListData = listData;
}

#endregion

#region属性

private string _FilterName;
public string FilterName
{
get {return _FilterName; }
set
{
_FilterName = value;
OnPropertyChanged(this,FilterName);
}
}

private List< string> _ListData;
public List< string> ListData
{
get {return _ListData; }
set
{
_ListData = value;
OnPropertyChanged(this,ListData);
}
}

#endregion

#region喊命令

private DelegateCommand _ShoutCommand;
public ICommand ShoutCommand
{
get {return _ShoutCommand? (_ShoutCommand = new DelegateCommand(Shout,CanShout)); }
}

private void Shout()
{
System.Windows.MessageBox.Show(string.Format(Called from SummaryFilterViewModel:{0} FilterName));
}

private bool CanShout()
{
return true;
}

#endregion
}
}


解决方案

我认为你要走的路径很快就会变得复杂,紧密耦合。您应该可以查看使用中介模式,以便于在您的SummaryFilterViewModel中传达更改到您的MainViewModel。



使用中介模式,您可以实现订阅和发布消息的方法,允许一个视图模型与另一个视图模型通信,



基本上,当选项卡选择更改时,摘要视图模型将使用包含引用对象或其他数据的消息有效内容发布更改。



在Mediator Pattern上的一些资源,你可以看看:




I know I'm probably missing something simple and obvious, but at the moment it eludes me.

I'm attempting to use the MVVM pattern.

How do you update a reference to a command in a viewmodel that is linked to a child viewmodel?

I've got a view (MainView) bound to a viewmodel (MainViewModel). On MainView, I've got an instance of another view (SummaryView) bound to a viewmodel (SummaryViewModel). SummaryViewModel contains a collection of a third viewmodel (SummaryFilterViewModel).

On SummaryView, there is a TabControl and each tab on it is bound to one of the SummaryFilterViewModel instances in the SummaryViewodel collection.

On MainView there is a button that is bound to a command in MainViewModel.

What I want to happen is for the command logic to live within the SummaryFilterViewModel class. So, whichever tab is currently displayed needs to be wired up to the command that the button on MainView fires.

What I tried to do was this:

  1. The individual SummaryFilterViewModel objects stored in the collection in SummaryViewModel hold the actual implementations of the ShoutCommand.
  2. A CommandReference object in the XAML of MainView binds to a ShoutCommand property of the MainViewModel
  3. The ShoutCommand property of the MainViewModel returns a reference to the ShoutCommand property of the SummaryViewModel object stored in MainViewModel.
  4. The ShoutCommand property of the SummaryViewModel returns a reference to the ShoutCommand property of whichever is the currently selected SummaryFilterViewModel.

What happens, is that the command does not get updated when the user changes tabs.

Am I way off base in how to implement this? Do I need to move the implementation of the command into the SummaryViewModel class?

Thanks in advance for any help!

The source to my solution is listed below:

ViewModels

SummaryView.xaml

<UserControl x:Class="NestedCommands.Views.SummaryView"
         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" 
         mc:Ignorable="d" 
         d:DesignHeight="309" d:DesignWidth="476">
<Grid>
    <TabControl SelectedIndex="{Binding SelectedTabIndex}">
        <TabItem DataContext="{Binding Filters[0]}" Header="{Binding FilterName}">
            <ListBox ItemsSource="{Binding ListData}" />
        </TabItem>
        <TabItem DataContext="{Binding Filters[1]}" Header="{Binding FilterName}">
            <ListBox ItemsSource="{Binding ListData}" />
        </TabItem>
        <TabItem DataContext="{Binding Filters[2]}" Header="{Binding FilterName}">
            <ListBox ItemsSource="{Binding ListData}" />
        </TabItem>
    </TabControl>
</Grid>

MainView.xaml

<Window x:Class="NestedCommands.Views.MainView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:v="clr-namespace:NestedCommands.Views"
    xmlns:c="clr-namespace:NestedCommands.Commands"
    Title="MainView" Height="336" Width="420">
<Window.Resources>
    <c:CommandReference x:Key="ShoutCommandReference" Command="{Binding ShoutCommand}" />
</Window.Resources>
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="1*" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <v:SummaryView Grid.Row="0"
                   DataContext="{Binding SummaryViewModel}" />
    <Button Content="Shout Command"
            Grid.Row="1"
            Command="{StaticResource ShoutCommandReference}" />
</Grid>


Command Classes

CommandReference.cs

using System;
using System.Windows;
using System.Windows.Input;

namespace NestedCommands.Commands
{
    /// <summary>
    /// This class facilitates associating a key binding in XAML markup to a command
    /// defined in a View Model by exposing a Command dependency property.
    /// The class derives from Freezable to work around a limitation in WPF when data-binding from XAML.
    /// </summary>
    public class CommandReference : Freezable, ICommand
    {
        public CommandReference()
        {
            // Blank
        }

        public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(CommandReference), new PropertyMetadata(new PropertyChangedCallback(OnCommandChanged)));

        public ICommand Command
        {
            get { return (ICommand)GetValue(CommandProperty); }
            set { SetValue(CommandProperty, value); }
        }

        #region ICommand Members

        public bool CanExecute(object parameter)
        {
            if (Command != null)
                return Command.CanExecute(parameter);
            return false;
        }

        public void Execute(object parameter)
        {
            Command.Execute(parameter);
        }

        public event EventHandler CanExecuteChanged;

        private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            CommandReference commandReference = d as CommandReference;
            ICommand oldCommand = e.OldValue as ICommand;
            ICommand newCommand = e.NewValue as ICommand;

            if (oldCommand != null)
            {
                oldCommand.CanExecuteChanged -= commandReference.CanExecuteChanged;
            }
            if (newCommand != null)
            {
                newCommand.CanExecuteChanged += commandReference.CanExecuteChanged;
            }
        }

        #endregion

        #region Freezable

        protected override Freezable CreateInstanceCore()
        {
            throw new NotImplementedException();
        }

        #endregion
    }
}

DelegateCommand.cs

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Input;

namespace NestedCommands.Commands
{
    /// <summary>
    ///     This class allows delegating the commanding logic to methods passed as parameters,
    ///     and enables a View to bind commands to objects that are not part of the element tree.
    /// </summary>
    public class DelegateCommand : ICommand
    {
        #region Constructors

        /// <summary>
        ///     Constructor
        /// </summary>
        public DelegateCommand(Action executeMethod)
            : this(executeMethod, null, false)
        {
        }

        /// <summary>
        ///     Constructor
        /// </summary>
        public DelegateCommand(Action executeMethod, Func<bool> canExecuteMethod)
            : this(executeMethod, canExecuteMethod, false)
        {
        }

        /// <summary>
        ///     Constructor
        /// </summary>
        public DelegateCommand(Action executeMethod, Func<bool> canExecuteMethod, bool isAutomaticRequeryDisabled)
        {
            if (executeMethod == null)
            {
                throw new ArgumentNullException("executeMethod");
            }

            _executeMethod = executeMethod;
            _canExecuteMethod = canExecuteMethod;
            _isAutomaticRequeryDisabled = isAutomaticRequeryDisabled;
        }

        #endregion

        #region Public Methods

        /// <summary>
        ///     Method to determine if the command can be executed
        /// </summary>
        public bool CanExecute()
        {
            if (_canExecuteMethod != null)
            {
                return _canExecuteMethod();
            }
            return true;
        }

        /// <summary>
        ///     Execution of the command
        /// </summary>
        public void Execute()
        {
            if (_executeMethod != null)
            {
                _executeMethod();
            }
        }

        /// <summary>
        ///     Property to enable or disable CommandManager's automatic requery on this command
        /// </summary>
        public bool IsAutomaticRequeryDisabled
        {
            get
            {
                return _isAutomaticRequeryDisabled;
            }
            set
            {
                if (_isAutomaticRequeryDisabled != value)
                {
                    if (value)
                    {
                        CommandManagerHelper.RemoveHandlersFromRequerySuggested(_canExecuteChangedHandlers);
                    }
                    else
                    {
                        CommandManagerHelper.AddHandlersToRequerySuggested(_canExecuteChangedHandlers);
                    }
                    _isAutomaticRequeryDisabled = value;
                }
            }
        }

        /// <summary>
        ///     Raises the CanExecuteChaged event
        /// </summary>
        public void RaiseCanExecuteChanged()
        {
            OnCanExecuteChanged();
        }

        /// <summary>
        ///     Protected virtual method to raise CanExecuteChanged event
        /// </summary>
        protected virtual void OnCanExecuteChanged()
        {
            CommandManagerHelper.CallWeakReferenceHandlers(_canExecuteChangedHandlers);
        }

        #endregion

        #region ICommand Members

        /// <summary>
        ///     ICommand.CanExecuteChanged implementation
        /// </summary>
        public event EventHandler CanExecuteChanged
        {
            add
            {
                if (!_isAutomaticRequeryDisabled)
                {
                    CommandManager.RequerySuggested += value;
                }
                CommandManagerHelper.AddWeakReferenceHandler(ref _canExecuteChangedHandlers, value, 2);
            }
            remove
            {
                if (!_isAutomaticRequeryDisabled)
                {
                    CommandManager.RequerySuggested -= value;
                }
                CommandManagerHelper.RemoveWeakReferenceHandler(_canExecuteChangedHandlers, value);
            }
        }

        bool ICommand.CanExecute(object parameter)
        {
            return CanExecute();
        }

        void ICommand.Execute(object parameter)
        {
            Execute();
        }

        #endregion

        #region Data

        private readonly Action _executeMethod = null;
        private readonly Func<bool> _canExecuteMethod = null;
        private bool _isAutomaticRequeryDisabled = false;
        private List<WeakReference> _canExecuteChangedHandlers;

        #endregion
    }

    /// <summary>
    ///     This class allows delegating the commanding logic to methods passed as parameters,
    ///     and enables a View to bind commands to objects that are not part of the element tree.
    /// </summary>
    /// <typeparam name="T">Type of the parameter passed to the delegates</typeparam>
    public class DelegateCommand<T> : ICommand
    {
        #region Constructors

        /// <summary>
        ///     Constructor
        /// </summary>
        public DelegateCommand(Action<T> executeMethod)
            : this(executeMethod, null, false)
        {
        }

        /// <summary>
        ///     Constructor
        /// </summary>
        public DelegateCommand(Action<T> executeMethod, Func<T, bool> canExecuteMethod)
            : this(executeMethod, canExecuteMethod, false)
        {
        }

        /// <summary>
        ///     Constructor
        /// </summary>
        public DelegateCommand(Action<T> executeMethod, Func<T, bool> canExecuteMethod, bool isAutomaticRequeryDisabled)
        {
            if (executeMethod == null)
            {
                throw new ArgumentNullException("executeMethod");
            }

            _executeMethod = executeMethod;
            _canExecuteMethod = canExecuteMethod;
            _isAutomaticRequeryDisabled = isAutomaticRequeryDisabled;
        }

        #endregion

        #region Public Methods

        /// <summary>
        ///     Method to determine if the command can be executed
        /// </summary>
        public bool CanExecute(T parameter)
        {
            if (_canExecuteMethod != null)
            {
                return _canExecuteMethod(parameter);
            }
            return true;
        }

        /// <summary>
        ///     Execution of the command
        /// </summary>
        public void Execute(T parameter)
        {
            if (_executeMethod != null)
            {
                _executeMethod(parameter);
            }
        }

        /// <summary>
        ///     Raises the CanExecuteChaged event
        /// </summary>
        public void RaiseCanExecuteChanged()
        {
            OnCanExecuteChanged();
        }

        /// <summary>
        ///     Protected virtual method to raise CanExecuteChanged event
        /// </summary>
        protected virtual void OnCanExecuteChanged()
        {
            CommandManagerHelper.CallWeakReferenceHandlers(_canExecuteChangedHandlers);
        }

        /// <summary>
        ///     Property to enable or disable CommandManager's automatic requery on this command
        /// </summary>
        public bool IsAutomaticRequeryDisabled
        {
            get
            {
                return _isAutomaticRequeryDisabled;
            }
            set
            {
                if (_isAutomaticRequeryDisabled != value)
                {
                    if (value)
                    {
                        CommandManagerHelper.RemoveHandlersFromRequerySuggested(_canExecuteChangedHandlers);
                    }
                    else
                    {
                        CommandManagerHelper.AddHandlersToRequerySuggested(_canExecuteChangedHandlers);
                    }
                    _isAutomaticRequeryDisabled = value;
                }
            }
        }

        #endregion

        #region ICommand Members

        /// <summary>
        ///     ICommand.CanExecuteChanged implementation
        /// </summary>
        public event EventHandler CanExecuteChanged
        {
            add
            {
                if (!_isAutomaticRequeryDisabled)
                {
                    CommandManager.RequerySuggested += value;
                }
                CommandManagerHelper.AddWeakReferenceHandler(ref _canExecuteChangedHandlers, value, 2);
            }
            remove
            {
                if (!_isAutomaticRequeryDisabled)
                {
                    CommandManager.RequerySuggested -= value;
                }
                CommandManagerHelper.RemoveWeakReferenceHandler(_canExecuteChangedHandlers, value);
            }
        }

        bool ICommand.CanExecute(object parameter)
        {
            // if T is of value type and the parameter is not
            // set yet, then return false if CanExecute delegate
            // exists, else return true
            if (parameter == null &&
                typeof(T).IsValueType)
            {
                return (_canExecuteMethod == null);
            }
            return CanExecute((T)parameter);
        }

        void ICommand.Execute(object parameter)
        {
            Execute((T)parameter);
        }

        #endregion

        #region Data

        private readonly Action<T> _executeMethod = null;
        private readonly Func<T, bool> _canExecuteMethod = null;
        private bool _isAutomaticRequeryDisabled = false;
        private List<WeakReference> _canExecuteChangedHandlers;

        #endregion
    }

    /// <summary>
    ///     This class contains methods for the CommandManager that help avoid memory leaks by
    ///     using weak references.
    /// </summary>
    internal class CommandManagerHelper
    {
        internal static void CallWeakReferenceHandlers(List<WeakReference> handlers)
        {
            if (handlers != null)
            {
                // Take a snapshot of the handlers before we call out to them since the handlers
                // could cause the array to me modified while we are reading it.

                EventHandler[] callees = new EventHandler[handlers.Count];
                int count = 0;

                for (int i = handlers.Count - 1; i >= 0; i--)
                {
                    WeakReference reference = handlers[i];
                    EventHandler handler = reference.Target as EventHandler;
                    if (handler == null)
                    {
                        // Clean up old handlers that have been collected
                        handlers.RemoveAt(i);
                    }
                    else
                    {
                        callees[count] = handler;
                        count++;
                    }
                }

                // Call the handlers that we snapshotted
                for (int i = 0; i < count; i++)
                {
                    EventHandler handler = callees[i];
                    handler(null, EventArgs.Empty);
                }
            }
        }

        internal static void AddHandlersToRequerySuggested(List<WeakReference> handlers)
        {
            if (handlers != null)
            {
                foreach (WeakReference handlerRef in handlers)
                {
                    EventHandler handler = handlerRef.Target as EventHandler;
                    if (handler != null)
                    {
                        CommandManager.RequerySuggested += handler;
                    }
                }
            }
        }

        internal static void RemoveHandlersFromRequerySuggested(List<WeakReference> handlers)
        {
            if (handlers != null)
            {
                foreach (WeakReference handlerRef in handlers)
                {
                    EventHandler handler = handlerRef.Target as EventHandler;
                    if (handler != null)
                    {
                        CommandManager.RequerySuggested -= handler;
                    }
                }
            }
        }

        internal static void AddWeakReferenceHandler(ref List<WeakReference> handlers, EventHandler handler)
        {
            AddWeakReferenceHandler(ref handlers, handler, -1);
        }

        internal static void AddWeakReferenceHandler(ref List<WeakReference> handlers, EventHandler handler, int defaultListSize)
        {
            if (handlers == null)
            {
                handlers = (defaultListSize > 0 ? new List<WeakReference>(defaultListSize) : new List<WeakReference>());
            }

            handlers.Add(new WeakReference(handler));
        }

        internal static void RemoveWeakReferenceHandler(List<WeakReference> handlers, EventHandler handler)
        {
            if (handlers != null)
            {
                for (int i = handlers.Count - 1; i >= 0; i--)
                {
                    WeakReference reference = handlers[i];
                    EventHandler existingHandler = reference.Target as EventHandler;
                    if ((existingHandler == null) || (existingHandler == handler))
                    {
                        // Clean up old handlers that have been collected
                        // in addition to the handler that is to be removed.
                        handlers.RemoveAt(i);
                    }
                }
            }
        }
    }
}


View Models

ViewModelBase.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;

namespace NestedCommands.ViewModels
{
    class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(object sender, string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                PropertyChanged(sender, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

MainViewModel.cs

using System;
using System.Windows.Input;

namespace NestedCommands.ViewModels
{
    class MainViewModel : ViewModelBase
    {
        public MainViewModel()
        {
            _SummaryViewModel = new SummaryViewModel();
        }

        private SummaryViewModel _SummaryViewModel;
        public SummaryViewModel SummaryViewModel
        {
            get { return _SummaryViewModel; }
            set
            {
                _SummaryViewModel = value;
                OnPropertyChanged(this, "SummaryViewModel");
            }
        }

        public ICommand ShoutCommand
        {
            get { return _SummaryViewModel.ShoutCommand; }
        }
    }
}

SummaryViewModel.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NestedCommands.Commands;
using System.Windows.Input;

namespace NestedCommands.ViewModels
{
    class SummaryViewModel : ViewModelBase
    {
        #region Constructor

        public SummaryViewModel()
        {
            List<SummaryFilterViewModel> filters = new List<SummaryFilterViewModel>();
            filters.Add(new SummaryFilterViewModel("Filter 1"));
            filters.Add(new SummaryFilterViewModel("Filter 2"));
            filters.Add(new SummaryFilterViewModel("Filter 3"));
            Filters = filters;
        }

        #endregion

        #region Properties

        private List<SummaryFilterViewModel> _Filters;
        public List<SummaryFilterViewModel> Filters
        {
            get { return _Filters; }
            set
            {
                _Filters = value;
                OnPropertyChanged(this, "Filters");
            }
        }

        private int _SelectedTabIndex;
        public int SelectedTabIndex
        {
            get { return _SelectedTabIndex; }
            set
            {
                _SelectedTabIndex = value;
                OnPropertyChanged(this, "SelectedTabIndex");
            }
        }

        #endregion

        #region Command References

        public ICommand ShoutCommand
        {
            get { return Filters[SelectedTabIndex].ShoutCommand; }
        }

        #endregion
    }
}

SummaryFilterViewModel.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NestedCommands.Commands;
using System.Windows.Input;

namespace NestedCommands.ViewModels
{
    class SummaryFilterViewModel : ViewModelBase
    {
        #region Constructor

        public SummaryFilterViewModel(string FilterName)
        {
            this.FilterName = FilterName;

            List<string> listData = new List<string>();
            for (int i = 1; i < 10; i++)
            {
                listData.Add(string.Format("{0}: {1}", FilterName, i));
            }
            ListData = listData;
        }

        #endregion

        #region Properties

        private string _FilterName;
        public string FilterName
        {
            get { return _FilterName; }
            set
            {
                _FilterName = value;
                OnPropertyChanged(this, "FilterName");
            }
        }

        private List<string> _ListData;
        public List<string> ListData
        {
            get { return _ListData; }
            set
            {
                _ListData = value;
                OnPropertyChanged(this, "ListData");
            }
        }

        #endregion

        #region Shout Command

        private DelegateCommand _ShoutCommand;
        public ICommand ShoutCommand
        {
            get { return _ShoutCommand ?? (_ShoutCommand = new DelegateCommand(Shout, CanShout)); }
        }

        private void Shout()
        {
            System.Windows.MessageBox.Show(string.Format("Called from SummaryFilterViewModel: {0}", FilterName));
        }

        private bool CanShout()
        {
            return true;
        }

        #endregion
    }
}

解决方案

I think the path you are going down is quickly going to end up complex and tightly coupled. You should probably take a look at using the Mediator Pattern to facilitate communication of changes in your SummaryFilterViewModel to your MainViewModel.

Using the mediator pattern, you can implement a means of subscribing to and publishing messages that allows one view model to communicate with another view model without ending up with tightly coupled view models.

Basically, when your tab selection changes, the summary view model would publish the change with the message payload containing the reference object or other data. The main view model would be subscribed to publication of this message and modify its state accordingly.

Some resources on the Mediator Pattern you can take a look at:

这篇关于更新对嵌套ViewModel的命令的引用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆