我如何从代码后台访问我的ViewModel

21

我不明白如何创建一个命令来创建一个可点击的MVVM矩形。这是我的代码:

<Rectangle x:Name="Color01" Fill="#FFF4F4F5" HorizontalAlignment="Left" Height="100" Margin="10,29,0,0" Stroke="Black" VerticalAlignment="Top" Width="100" MouseDown="Color_MouseDown" />
<Rectangle x:Name="Color02" Fill="#FFF4F4F5" HorizontalAlignment="Left" Height="100" Margin="115,29,0,0" Stroke="Black" VerticalAlignment="Top" Width="100"/>
<Rectangle x:Name="Color03" Fill="#FFF4F4F5" HorizontalAlignment="Left" Height="100" Margin="220,29,0,0" Stroke="Black" VerticalAlignment="Top" Width="100"/>
<Rectangle x:Name="Color04" Fill="#FFF4F4F5" HorizontalAlignment="Left" Height="100" Margin="325,29,0,0" Stroke="Black" VerticalAlignment="Top" Width="100"/>

在我的第一个矩形中,您可以看到我创建了一个代码后台事件。首先,我不知道如何从代码后台访问我的ViewModel。其次,这实际上不是MVVM。

public partial class MainWindow : Window
{
    /// <summary>
    /// Initializes a new instance of the MainWindow class.
    /// </summary>
    public MainWindow()
    {
        InitializeComponent();
        Closing += (s, e) => ViewModelLocator.Cleanup();
    }

    private void Color_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        // So what ???
    }
}

当有人点击我的矩形时,我只需要能够更改存储在viewModel中的列表中的一个简单布尔值。为什么使用MVVM会这么复杂?


var rect = sender as Rectangle 然后你可以对它做任何想做的事情,尽管这是非常糟糕的设计,你应该尽一切可能使用数据绑定并将颜色绑定到字段,而不是这样做。学习和利用给定的抽象,而不是与它们斗争。 - Benjamin Gruenbaum
我得到了矩形...然后下一步是什么?获取发送者不是问题。之后,我需要更新存储在viewModel中的列表中存储的布尔值。 - Bastien Vandamme
4个回答

39

MVVM模式下,不应该从代码后台访问您的视图模型,视图模型和视图彼此独立。相反,你可以将EventToCommand行为附加到你的控件上。这样可以将控件中的事件绑定到数据上下文中的命令。请参阅此处MSDN命令教程

如果你非常想这样做,你可以访问控件的数据上下文属性并将其强制转换为你的视图模型类型以便访问内部内容。

var vm = (ViewModelType)this.DataContext;
vm.CommandProperty.Execute(null);

你好,kidshaw,你有没有关于如何保持ViewModel和代码分离的好资源?我现在正在处理一个与ViewModel通过属性耦合的代码。我知道这不是理想的,但对于我需要的ViewModel.Method、View.Method、ViewModel.Method的执行顺序来说,它似乎是不可避免的。 - Chucky
1
@Chucky - 这是因为这是MVVM模式。如果你引入Code Behind代码,它应该只是补充视图而不是访问视图模型。也许对于你的应用程序来说这并不值得,但通常可以通过使用DataTriggers、EventToCommand和自定义行为等方式来保持模式。如果你感兴趣,请发帖讲述你的问题。 - kidshaw
6
这并不完全正确。因为在View中将 "DataContext" 属性设置为指向您的ViewModel,它们是绑定的。此外,所有的“Binding”都再次意识到在ViewModel中定义的属性。因此,View始终知道ViewModel。然而反过来是错误的:ViewModel永远不应该被绑定到View。 - Gregory Stein

19

简短回答。这可能对其他人也有帮助。

((MyViewModel)(this.DataContext)).MyProperty

2
将“DataContext”更改为“BindingContext”对我有用。 - Naveed Hematmal

14

这并不太困难。首先,在你的窗口 XAML 中创建 ViewModel 的实例:

视图 XAML:

<Window x:Class="BuildAssistantUI.BuildAssistantWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:VM="clr-namespace:MySolutiom.ViewModels">
     <Window.DataContext>
         <VM:MainViewModel />
     </Window.DataContext>
  </Window>

在此之后,您可以使用System.Windows.Interactivity.InvokeCommandAction将事件转换为命令:

XAML视图:


XAML视图:

<Grid>
 <Rectangle x:Name="Color01" Fill="#FFF4F4F5" HorizontalAlignment="Left" Height="100" Margin="10,29,0,0" Stroke="Black" VerticalAlignment="Top" Width="100" MouseDown="Color_MouseDown">
   <interactivity:Interaction.Triggers>
      <interactivity:EventTrigger EventName="MouseDown">
          <interactivity:InvokeCommandAction Command="{Binding MyCommand}"/>
      </interactivity:EventTrigger>
   </interactivity:Interaction.Triggers>
 </Rectangle>
</Grid>

现在,在您的ViewModel中设置一个ICommandDelegateCommand实现来绑定该事件:

ViewModel:

public class ViewModel
{
    public ICommand MyCommand { get; set; }

    public ViewModel()
    {
        MyCommand = new DelegateCommand(OnRectangleClicked);
    }

    public void OnRectangleClicked()
    {
        // Change boolean here
    }
}

6
在C# XAML UWP MVVM上下文中。
考虑以下示例。
模型:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FrostyTheSnowman.Models
{
    public class User
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }        

        public override string ToString() => $"{FirstName} {LastName}";

    }
}

视图模型

using FrostyTheSnowman.Models;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FrostyTheSnowman
{
    public class MainPageViewModel
    {
        public User user { get; set; }

        public MainPageViewModel()
        {
            user = new User
            {
                FirstName = "Frosty",
                LastName = "The Snowman"                
            };
        }
    }
}

查看

<Page
    x:Class="FrostyTheSnowman.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:FrostyTheSnowman"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Page.DataContext>
        <local:MainPageViewModel x:Name="ViewModel" />
    </Page.DataContext>

    <Grid>
        <StackPanel Name="sp1" DataContext="{Binding user}">

            <TextBox Name="txtFirstName"                     
                 Header="First Name"
                 Text="{Binding FirstName}" />

            <TextBox Name="txtLastName"                     
                 Header="Last Name"
                 Text="{Binding LastName}" />


        </StackPanel>

    </Grid>
</Page>

代码后台文件:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409

namespace FrostyTheSnowman
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        MainPageViewModel vm;

        public MainPage()
        {
            this.InitializeComponent();

            // Initialize the View Model Object
            vm = (MainPageViewModel)this.DataContext;

            System.Diagnostics.Debug.WriteLine(vm.user.ToString() + " was a jolly happy soul");
        }
    }
}

当您运行应用程序时,您将看到:

enter image description here

但更重要的是,调试跟踪将显示:

enter image description here

它显示代码后台确实成功访问了ViewModel...
希望这可以帮到你。

1
现在我脑海中一直哼唱着《雪人弗洛斯蒂》这首歌曲,太好了。 - Jay

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接