使用MVVM在单个窗口中的多个视图-需要更多信息
本文关键字:视图 信息 使用 单个 窗口 MVVM | 更新日期: 2023-09-27 17:50:11
好的,所以我找到了WPF MVVM导航视图,Sheridan对如何在单个窗口中导航多个视图做了(大部分)很好的解释。根据代码和解释,我能够使用后面的代码获得视图切换,但是我无法获得他的XAML Command=""
版本的视图切换工作。
我不得不四处寻找与他的输入相匹配的ActionCommand()
版本,但我甚至不确定它是正确的,而且我根本找不到IsViewModelOfType<T>()
方法上的任何东西。这意味着给出的最终解是部分破碎的。
如果没有第三方库,我如何使最后一部分工作?
不幸的是,因为StackOverflow讨厌新用户参与任何事情,所以我不能在同一个线程中询问。
从上面的帖子,这是不工作的部分,因为这两个部分- ActionCommand和IsViewModelOfType -似乎不存在任何地方,没有他们的信息搜索在线
最后,我们如何从其他视图更改视图?是有的有几种可能的方法,但最简单的方法是加a控件中的iccommand直接与子视图绑定MainViewModel。我使用relaycommand的自定义版本,但您可以使用任何你喜欢的类型,我猜你会得到图片:
public ICommand DisplayPersonView { get { return new ActionCommand(action => ViewModel = new PersonViewModel(), canExecute => !IsViewModelOfType<Person>()); } }
子视图XAML:
<Button Command="{Binding DataContext.DisplayPersonView, RelativeSource= {RelativeSource AncestorType={x:Type MainViewModel}}, Mode=OneWay}" />
更新:我试着从我自己的IsViewModelOfType<T>()
(以及实际测试)中返回True
和False
,但无济于事。问题的关键似乎是ActionCommand()
方法,我没有任何基础可以继续,或者XAML本身是不正确的。
我能找到的ActionCommand()
最接近的匹配来自Microsoft.Expression.Interactivity.Core
,但它只接受一个参数,并且在这种用法中表现不正确。
我也尝试了我在网上找到的这个代码片段,但是当使用Sheridan的代码时,它没有做任何事情
class ActionCommand : ICommand
{
private readonly Action<object> _executeHandler;
private readonly Func<object, bool> _canExecuteHandler;
public ActionCommand(Action<object> execute, Func<object, bool> canExecute)
{
if (execute == null)
throw new ArgumentNullException("Execute cannot be null");
_executeHandler = execute;
_canExecuteHandler = canExecute;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_executeHandler(parameter);
}
public bool CanExecute(object parameter)
{
if (_canExecuteHandler == null)
return true;
return _canExecuteHandler(parameter);
}
}
我会说,从我在答案中看到的代码,你参考的IsViewModelOfType<Person>()
方法是使用某种类型的映射,从给定的对象类型获得视图模型类型。所以代码
public ICommand DisplayPersonView
{
get { return new ActionCommand(action => ViewModel = new PersonViewModel(),
canExecute => !IsViewModelOfType<Person>()); }
}
将引用类似于
的泛型方法public bool IsViewModelOfType<T>()
{
Type type = typeof(T);
if (!viewModelMapping.ContainsKey(type))
return false;
return viewModelMapping[type] == typeof(this);
}
上面的this
指的是当前的视图模型,你可能有视图模型类型到object
类型映射字典,如
public Dictionary<Type, Type> viewModelMapping =
new Dictionary<Type, Type>()
{
{ typeof(Person), typeof(PersonViewModel) },
...
};
Sheriden在上面的命令中所说的是,如果类型不是Person
,那么不允许显示PersonViewModel
。你可以随心所欲地强加这种逻辑,而不必遵循Sheriden的方法。
我希望这对你有帮助。
阅读你的编辑,我会首先尝试删除RelativeSource
代码,我不认为这是必要的。好了,现在进入命令类。这是我在开始使用MVVM框架之前使用的:
using System;
using System.Diagnostics;
using System.Windows.Input;
namespace ResourceStudio.Commands
{
/// <summary>
/// A command whose sole purpose is to relay its functionality to other
/// objects by invoking delegates. The default return value for the CanExecute
/// method is 'true'.
/// </summary>
public class RelayCommand : ICommand
{
readonly Action<object> execute;
readonly Predicate<object> canExecute;
#region Constructors
/// <summary>
/// Creates a new command that can always execute.
/// </summary>
/// <param name="execute">The execution logic.</param>
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
/// <summary>
/// Creates a new command.
/// </summary>
/// <param name="execute">The execution logic.</param>
/// <param name="canExecute">The execution status logic.</param>
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
this.execute = execute;
this.canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return this.canExecute == null ? true : this.canExecute(parameter);
}
/// <summary>
/// Can execute changed event handler.
/// </summary>
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
this.execute(parameter);
}
#endregion // ICommand Members
}
}
MenuItem
点击的用法如下:
<MenuItem Header="E_xit"
Command="{Binding CloseApplicationCommand}"/>
其中CloseApplicationCommand
为
private RelayCommand closeApplicationCommand;
public RelayCommand CloseApplicationCommand
{
get
{
return closeApplicationCommand ?? (closeApplicationCommand =
new RelayCommand(o => this.CloseApplication(), o => CanCloseApplication));
}
}
其中CanCloseApplication
是布尔属性。
好了,现在,如果你想将命令设置为触发一个特定的事件,比如按钮MouseOver
事件,那么你必须写一些像
<Button Content="SomeButton">
<Interactivity:Interaction.Triggers>
<Interactivity:EventTrigger EventName="MouseOver">
<Interactivity:InvokeCommandAction Command="{Binding SomeButtonMouseOverCommand}" />
</Interactivity:EventTrigger>
</Interactivity:Interaction.Triggers>
</Button>
使用xmlns:Interactivity="http://schemas.microsoft.com/expression/2010/interactivity"
命名空间。然而,一个更好的方法是使用AttachedCommandBehaviours
(如果你反对切换到MVVM框架(例如Claiburn))。微等人))。你可以在这里下载AttachedCommandBehavious
。然后你可以写
<Button Content="Select"
AttachedCommand:CommandBehavior.Event="Click" // Or what ever event you want!
AttachedCommand:CommandBehavior.Command="{Binding DoSomethingCommand}" />
我会认真考虑使用MVVM框架。然而,在开始之前理解这些东西是很好的。
问题就在这里
子视图XAML:
<Button Command="{Binding DataContext.DisplayPersonView, RelativeSource= {RelativeSource AncestorType={x:Type MainViewModel}}, Mode=OneWay}" />
这根本没有发射任何东西。根据Killercam的反馈使用
<Button Command="{Binding DisplayPersonView}" />
其中DisplayPersonView()
也触发一个事件OnViewModelChanged
,该事件包含在所有视图模型继承的BaseViewModel
中。
不幸的是,我不得不稍微打破MVVM的做法,通过在
后面的窗口代码中添加一个处理程序private void UpdateView(object sender, EventArgs e)
{
DataContext = (BaseViewModel)sender;
((BaseViewModel)DataContext).OnViewModelChange += UpdateView;
}
在窗口的构造函数中,我调用UpdateView()
来设置初始的ViewModel
无论上面描述的ActionCommand()
或RelayCommand()
都很好,有或没有canExecute()
都很好。