我有什么:
MyCommand
,它从视图中的多个位置调用DoSthInView
,在codebehind 我的目标:
无论何时执行命令,我都要调用DoSthInView
,无论哪个控件执行命令。
问题:
由于在MVVM中ViewModel不知道View,因此我无法从ViewModel调用DoSthInView
。那么如何调用此代码呢?
自己的想法:
为了不那么抽象,这是我的用例:我们有一个Button和一个TextBox。该命令获取当前位于TextBox中的文本,并将其写入模型数据中的某处。完成此写作后,我想要显示绿色复选标记的动画并逐渐淡出(这是DoSthInView
),以便用户获得数据已更新的直观确认。
有两种运行命令的方法:
对于Button我知道一种方法来调用DoSthInView
:
<Button Content="run command" Command="{Binding MyCommand}" Click={Binding DoSthInView}" />
对于TextBox,我有一个KeyBinding来接受Enter键:
<TextBox>
<TextBox.InputBindings>
<KeyBinding Command="{Binding MyCommand}" Key="Enter" />
</TextBox.InputBindings>
</TextBox>
但是InputBindings似乎不支持事件,只支持命令。所以我在这里不知道如何拨打DoSthInView
。
但即使我找到了一种从输入绑定(类似于Button)调用DoSthInView
的方法,它也不会感觉正确。我正在寻找一种说法&#34;只要执行MyCommand
,就运行DoSthInView
&#34;因此,MyCommand
的每个调用者都不必单独关心它,但只有一个地方可以处理它。也许这可以在根FrameworkElement中完成?
答案 0 :(得分:1)
你要求的是可能的。您需要实施RelayCommand。 您还可以看到我的其他SO answer,其中有一个示例。
实施RelayCommand后,您可以执行以下操作:
在ViewModel中:
public ICommand MyCommand { get; set; }
public MyViewModel()
{
MyCommand = new RelayCommand(MyCommand_Execute);
}
private void MyCommand_Execute(object sender)
{
var myView = sender as MyView;
myView?.DoSthInView();
}
在视图中
<TextBox>
<TextBox.InputBindings>
<KeyBinding Command="{Binding Path=MyCommand}" CommandParameter="{Binding}" Key="Enter"/>
</TextBox.InputBindings>
</TextBox>
虽然不建议混合使用view和viewModel,但可能会出现无法实现的情况。有时它可能是要求。但是不建议这样做。
答案 1 :(得分:0)
虽然我仍然对原始问题的答案感兴趣(在执行命令后调用codebehind-code),但Kirenenko的建议帮助我解决了关于动画的实际问题。这个答案不再符合原始问题,因为没有代码隐藏代码(动画只用XAML编写,不留任何代码隐藏代码来执行)。我仍然把它放在这里,因为它对我来说是部分有用的。
在ViewModel中,我有这个:
...
private bool _triggerBool;
public bool TriggerBool
{
get { return _triggerBool; }
set
{
if (_triggerBool != value)
{
_triggerBool = value;
NotifyPropertyChanged(nameof(TriggerBool));
}
}
}
...
public DelegateCommand MyCommand; // consists of MyCommandExecute and MyCommandCanExecute
...
public void MyCommandExecute()
{
... // actual command code
TriggerBool = true;
TriggerBool = false;
}
...
这是用XAML编写并由DataTrigger调用的动画:
<Image Source="myGreenCheckmark.png" Opacity="0">
<Image.Style>
<Style TargetType="Image">
<Style.Triggers>
<DataTrigger Binding="{Binding TriggerBool}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
From="1" To="0" Duration="0.0:0:0.750"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
将TriggerBool
设置为true
然后再设置false
看起来真的很愚蠢,但有效......
答案 2 :(得分:0)
根据Leonid Malyshev的提示,这是一个非常干净的解决方案,使用一个事件(关于View和ViewModel分离的“干净”):
ViewModel代码:
public class MyViewModel
{
...
public event Action MyCommandExecuted;
public DelegateCommand MyCommand; // consists of MyCommandExecute, MyCommandCanExecute
...
private void MyCommandExecute()
{
... // actual command code
MyCommandExecuted.Invoke();
}
...
}
查看Codebehind:
public partial class MyView : Window
{
public MyView(MyViewModel vm)
{
InitializeComponent();
DataConext = vm;
vm.MyCommandExecuted += DoSthInView();
}
...
private void DoSthInView()
{
...
}
...
}
答案 3 :(得分:-2)
通常你有更好的MVVM方法来解决这些问题,但由于我不知道你的情况,我会假设没有别的办法。 因此,您需要在视图中使用依赖项属性。布尔值很棒,你可以在更改的事件上处理它并在其中运行DoSthInView。在视图模型中,您可以设置此属性的值,并在更改后调用它。
如果你需要,我可以给你演示。还要记住,这是一个诽谤MVVM的事件驱动编码。尝试使用绑定并尽可能将DoSthInView移动到ViewModel。