Nightmare Games
Nightmare Games

Reputation: 2435

Accessing a property from both the View and the View Model in WPF

I'm writing a 3D modelling program in WPF and Prism using the MVVM format.

I'm using canvases to show the different views (top, front, side) with grid lines. I want the user to be able to adjust the spacing between lines, and I have a TextBox that holds the value, which is bound to a property in the view-model. This parts works fine.

To draw the grid lines using the user's spacing, however, I need to access that property from the View, where the lines are being drawn (in MainWindow.xaml.cs). I still need it to exist in the view-model as well, though, for certain functionality to work (like snap-to-grid).

I foresee that there will be many properties I'll need to bounce back and forth, since the UI and the functionality will be working closely together in a program like this.

The way I've gotten around this problem in the past is by first creating an invisible label in the UI. Then I use a function to set the label's binding dynamically and grab the value from the label.

public int TempIntBind(string bind)
{
    DummyLabel.SetBinding(Label.ContentProperty, new Binding(bind));

    int newInt;
    if (DummyLabel.Content != null && int.TryParse(DummyLabel.Content.ToString(), out newInt))
    {
        return newInt;
    }
    else
    {
        return -1;
    }
}

This works, but it seems rather hacky and also circumvents the MVVM pattern.

Is there a better way to achieve this result?

Since:

this.DataContext = new View_Model ();

I was hoping I could just say something like:

x = this.DataContext.gridsize;

But apparently that's not how that works. Too JavaScript-y, I suppose.

Here are the relevant bits of my main window xaml:

<Window x:Class="Brick.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Brick"
        mc:Ignorable="d"
        Title="Brick" Width="800" Height="600">

    <WrapPanel Grid.Column="0" Grid.ColumnSpan="3" Grid.Row="1" Margin="5">
      <Label>Grid Size</Label>
      <TextBox Width="50" Height="20" Text="{Binding Path=grid_size, UpdateSourceTrigger=PropertyChanged}" />
    </WrapPanel>

    <Canvas Grid.Column="1" Grid.Row="2" x:Name="top_view" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,5,5" Background="Black"></Canvas>

  </Grid>
</Window>

The View:

namespace Brick
  {
  public partial class MainWindow : Window
    {
    public MainWindow ()
      {
      InitializeComponent ();
      this.DataContext = new View_Model ();
      draw_grid_lines ();
      }

    void draw_grid_lines ()
      {
      int spaces = grid_size;  // <-- Right here is the problem spot
      }
    }
  }

And the View-Model:

namespace Brick
  {
  class View_Model : Prism.Mvvm.BindableBase
    {
    public event PropertyChangedEventHandler property_changed_event;

    private int grid_size_private;
    public int grid_size
      {
      get {return grid_size_private;}
      set
        {
        if (grid_size_private != value)
          {
          grid_size_private = value;
          RaisePropertyChanged ("grid_size");
          }
        }
      }

    public View_Model ()
      {
      grid_size = 8;
      }
    }
  }

Upvotes: 1

Views: 3664

Answers (2)

na th
na th

Reputation: 145

did you try casting the DataContext to View_Model in code behind?

namespace Brick
{
   public partial class MainWindow : Window
   {
      public MainWindow ()
      {
         InitializeComponent ();
         this.DataContext = new View_Model ();
         draw_grid_lines ();
      }

      void draw_grid_lines ()
      {
         var vm = (View_Model)this.DataContext;
         int spaces = vm.grid_size;  // <-- Right here is the problem spot
      }
   }
}

Upvotes: 2

aybe
aybe

Reputation: 16652

You can assign a property to DataContext and just access members through it:

namespace WpfApp1
{
    public partial class MainWindow
    {
        public MainWindow()
        {
            InitializeComponent();

            Model = new Model();

            DataContext = Model;
        }

        private Model Model { get; }

        private void Whatever()
        {
            var value = Model.Value;
        }
    }

    internal class Model
    {
        public int Value { get; set; }
    }
}

For the syntax issue you were running into you would have done:

var x = ((Model) DataContext).Value;

Upvotes: 0

Related Questions