Wpf 是否更新对嵌套ViewModel命令的引用?
我知道我可能错过了一些简单而明显的东西,但目前我还没有意识到 我正在尝试使用MVVM模式 如何更新对链接到子viewmodel的viewmodel中的命令的引用 我已将视图(MainView)绑定到viewmodel(MainViewModel)。 在MainView上,我有一个绑定到viewmodel(SummaryViewModel)的另一个视图(SummaryView)的实例。SummaryViewModel包含第三个viewmodel(SummaryFilterViewModel)的集合 在SummaryView上,有一个TabControl,其中的每个选项卡都绑定到SummaryViewModel集合中的一个SummaryFilterServiceWModel实例 在MainView上,有一个按钮绑定到MainViewModel中的命令 我想让命令逻辑存在于SummaryFilterServiceWModel类中。因此,无论当前显示哪个选项卡,都需要连接到MainView上的按钮触发的命令 我想做的是:Wpf 是否更新对嵌套ViewModel命令的引用?,wpf,mvvm,command,viewmodel,Wpf,Mvvm,Command,Viewmodel,我知道我可能错过了一些简单而明显的东西,但目前我还没有意识到 我正在尝试使用MVVM模式 如何更新对链接到子viewmodel的viewmodel中的命令的引用 我已将视图(MainView)绑定到viewmodel(MainViewModel)。 在MainView上,我有一个绑定到viewmodel(SummaryViewModel)的另一个视图(SummaryView)的实例。SummaryViewModel包含第三个viewmodel(SummaryFilterViewModel)的集合
public MainViewModel()
{
_SummaryViewModel = new SummaryViewModel();
_SummaryViewModel.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(_SummaryViewModel_PropertyChanged);
}
void _SummaryViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "ShoutCommand":
OnPropertyChanged(this, "ShoutCommand");
break;
}
}
private SummaryViewModel _SummaryViewModel;
public SummaryViewModel SummaryViewModel {...}
public ICommand ShoutCommand
{
get { return _SummaryViewModel.ShoutCommand; }
}
private List<SummaryFilterViewModel> _Filters;
public List<SummaryFilterViewModel> Filters {...}
private int _SelectedTabIndex;
public int SelectedTabIndex
{
get { return _SelectedTabIndex; }
set
{
_SelectedTabIndex = value;
OnPropertyChanged(this, "SelectedTabIndex");
OnPropertyChanged(this, "ShoutCommand");
}
}
public ICommand ShoutCommand
{
get {
int selectedTabIndex = SelectedTabIndex;
return (selectedTabIndex == -1) ? null : Filters[SelectedTabIndex].ShoutCommand;
}
}
MainViewModel.cs
SummaryViewModel.cs
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用系统文本;
使用NestedCommands.Commands;
使用System.Windows.Input;
命名空间NestedCommands.ViewModels
{
类SummaryViewModel:ViewModelBase
{
#区域构造函数
公共摘要视图模型()
{
列表过滤器=新列表();
添加(新的SummaryFilterViewModel(“过滤器1”);
添加(新的SummaryFilterViewModel(“过滤器2”);
添加(新的SummaryFilterViewModel(“过滤器3”);
过滤器=过滤器;
}
#端区
#区域属性
私有列表过滤器;
公共列表过滤器
{
获取{return\u Filters;}
设置
{
_过滤器=值;
OnPropertyChanged(本“过滤器”);
}
}
私有int_选择的TABINDEX;
公共int-SelectedTabIndex
{
获取{return\u SelectedTabIndex;}
设置
{
_SelectedTabIndex=值;
已更改的不动产(此为“选定的数据索引”);
}
}
#端区
#区域命令引用
公共ICommand ShoutCommand
{
获取{返回筛选器[SelectedTabIndex].ShoutCommand;}
}
#端区
}
}
SummaryFilterViewModel.cs
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用系统文本;
使用NestedCommands.Commands;
使用System.Windows.Input;
命名空间NestedCommands.ViewModels
{
类SummaryFilterViewModel:ViewModelBase
{
#区域构造函数
公共摘要筛选器服务模型(字符串筛选器名称)
{
this.FilterName=FilterName;
List listData=新列表();
对于(int i=1;i<10;i++)
{
Add(string.Format(“{0}:{1}”,FilterName,i));
}
ListData=ListData;
}
#端区
#区域属性
私有字符串过滤器名称;
公共字符串筛选器名称
{
获取{return\u FilterName;}
设置
{
_过滤器名称=值;
已更改的不动产(本“过滤器名称”);
}
}
私有列表ListData;
公共列表数据
{
获取{return\u ListData;}
设置
{
_ListData=值;
OnPropertyChanged(本“列表数据”);
}
}
#端区
#区域呼叫命令
专用授权命令(u ShoutCommand);;
公共ICommand ShoutCommand
{
获取{return _ShoutCommand???(_ShoutCommand=newdelegatecommand(Shout,CanShout));}
}
私家侦探()
{
System.Windows.MessageBox.Show(string.Format(“从SummaryFilterWebModel:{0}”,FilterName调用”);
}
二等兵布尔·坎豪特()
{
返回true;
}
#端区
}
}
你的帖子很长,我承认我没有完全阅读。但是,我不明白CommandReference
的目的。为什么不直接绑定到MainViewModel.ShoutCommand
?考虑:
- 将
的TabControl
绑定到子视图模型集合ItemsSource
- 将
的TabControl
绑定到跟踪所选子视图模型的另一个属性SelectedItem
- 当上述属性更改时,也为
属性引发ShoutCommand
事件PropertyChanged
- 在
属性的getter中,只需返回所选子视图模型的ShoutCommand
ShoutCommand
- 您的采购订单
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
}
}
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);
}
}
}
}
}
}
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));
}
}
}
}
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; }
}
}
}
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
}
}
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
}
}
<v:SummaryView Grid.Row="0"
DataContext="{Binding SummaryViewModel}" />
<Button Content="Shout Command"
Grid.Row="1"
Command="{Binding ShoutCommand}" />
public MainViewModel()
{
_SummaryViewModel = new SummaryViewModel();
_SummaryViewModel.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(_SummaryViewModel_PropertyChanged);
}
void _SummaryViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "ShoutCommand":
OnPropertyChanged(this, "ShoutCommand");
break;
}
}
private SummaryViewModel _SummaryViewModel;
public SummaryViewModel SummaryViewModel {...}
public ICommand ShoutCommand
{
get { return _SummaryViewModel.ShoutCommand; }
}
<TabControl SelectedIndex="{Binding SelectedTabIndex}">
<TabItem DataContext="{Binding Filters[0]}" Header="{Binding FilterName}">
<ListBox ItemsSource="{Binding ListData}" />
</TabItem>
<!-- TabItem repeated two more times -->
</TabControl>
private List<SummaryFilterViewModel> _Filters;
public List<SummaryFilterViewModel> Filters {...}
private int _SelectedTabIndex;
public int SelectedTabIndex
{
get { return _SelectedTabIndex; }
set
{
_SelectedTabIndex = value;
OnPropertyChanged(this, "SelectedTabIndex");
OnPropertyChanged(this, "ShoutCommand");
}
}
public ICommand ShoutCommand
{
get {
int selectedTabIndex = SelectedTabIndex;
return (selectedTabIndex == -1) ? null : Filters[SelectedTabIndex].ShoutCommand;
}
}