How can I access my ViewModel from code behind
Asked Answered
B

4

22

I don't understand how I can create a command to create a MVVM clickable rectangle. Here is my code:

<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"/>

On my first rectangle you can see I created a code behind event. First I don't know how to access my ViewModel from the code behind. Two it's not really 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 ???
    }
}

I just need to be able to change a simple boolean value stored in a list stored in my viewModel when someone click on my rectangle. Why it is so complicate to do with MVVM?

Bracknell answered 20/7, 2014 at 3:30 Comment(2)
var rect = sender as Rectangle and then do whatever you want to it, although this is very bad design and you should by all means use data binding and bind the color to a field rather than doing this. Learn and utilize the abstractions you are given rather than fight them.Geddes
And i get the rectangle... then what next? Getting the sender is not the problem. After that I need to update my boolean value stored in a list stored in my viewModel.Bracknell
R
14

This isn't too difficult. First, create an instance of your ViewModel inside your Window XAML:

View 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>

After that, you can System.Windows.Interactivity.InvokeCommandAction to translate your event to a command:

View 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>

Now, in your ViewModel, set up an ICommand and the DelegateCommand implementation to bind to that event:

ViewModel:

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

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

    public void OnRectangleClicked()
    {
        // Change boolean here
    }
}
Repetitious answered 20/7, 2014 at 3:52 Comment(0)
W
39

In MVVM you shouldn't be accessing your view model from code behind, the view model and view are ignorant of each other a here endeth the lecture :)

Instead you can attach the EventToCommand behaviour to your control. This lets you bind an event in the control to a command in the data context. See msdn commands tutorial here.

If you are desperate to do it, you can access the controls data context property and cast it to your view model type to give access to the internals.

var vm = (ViewModelType)this.DataContext;
vm.CommandProperty.Execute(null);
West answered 20/7, 2014 at 9:24 Comment(3)
Hi kidshaw, do you have any good resources for keeping the ViewModel and code-behind separate? I am working on a code-behind now which is coupled to the ViewModel via a property. I know this isn't ideal but it feels unavoidable for the execution order of ViewModel.Method, View.Method, ViewModel.Method that I need.Singleness
@Chucky - simply because that is the MVVM pattern. If you introduce code behind it should only supplement the view and not access the view model. It may not be worth it for your application, but you can normally find a way to keep the pattern using DataTriggers, EventToCommand and maybe a custom behaviour. If that interests you, post your problem.West
This isn't entirely correct. Since you set DataContext property to point to your ViewModel within View, they are bound. Moreover, all the Binding-s are again aware of the properties defined in ViewModel. So View always aware of ViewModel. However the opposite is not correct: ViewModel should never be bound to View.Hindsight
U
20

Quick answer. This might help others as well

((MyViewModel)(this.DataContext)).MyProperty
Unimposing answered 17/4, 2018 at 4:6 Comment(1)
Changing DataContext to BindingContext works for me.Akim
R
14

This isn't too difficult. First, create an instance of your ViewModel inside your Window XAML:

View 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>

After that, you can System.Windows.Interactivity.InvokeCommandAction to translate your event to a command:

View 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>

Now, in your ViewModel, set up an ICommand and the DelegateCommand implementation to bind to that event:

ViewModel:

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

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

    public void OnRectangleClicked()
    {
        // Change boolean here
    }
}
Repetitious answered 20/7, 2014 at 3:52 Comment(0)
P
8

In a C# XAML UWP MVVM Context.

Consider the following example

Model:

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}";

    }
}

ViewModel

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"                
            };
        }
    }
}

View

<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>

code-behind file:

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");
        }
    }
}

When u run the app you'll see:

enter image description here

But more importantly, the debug trace will show:

enter image description here

It shows that the code-behind has indeed successfully accessed the ViewModel...

Hope this helps

Pappas answered 23/3, 2019 at 20:23 Comment(1)
And now I have "Frosty The Snowman" song stuck in my head. Great.Hoekstra

© 2022 - 2024 — McMap. All rights reserved.