Wednesday, September 26, 2012

How to Workaround Referencing View's Controls

How to Workaround Referencing View's Controls

I'm using Galasoft's Light MVVM for my Siverlight project.

I have setup everything as instructed: the ViewModel is bound to View's DataContext;

I have a canvas named inkCanvas in the View.

When the ViewModel gets the updated project data, I need to reference inkCanvas to create a CanvasRender instance public CanvasRender(Canvas canvas, ProjectData pdata).

The problem is in MVVM, the ViewModel knows nothing about View, so how can I reference a control (inkCanvas) in View?

P.S. (Edited): The workaround I made is: when I pass the project data to the ViewModel, I also pass the inkCanvas from View's code-behind. hmmm, now my code-behind is not clean.

Answers & Comments...

Answer: 1

In MVVM Pattern, you won't reference a Control directly in ViewModel. In MVVM, all is "binding". You inkCanvas will be binding to a property in your ViewModel.

public class MyViewModel : INotifyPropertyChanged {     private readonly StrokeCollection _mystrokes;      public MyViewModel ()     {         _mystrokes= new StrokeCollection();         (_mystrokesas INotifyCollectionChanged).CollectionChanged += delegate         {             //the strokes have changed         };     }      public event PropertyChangedEventHandler PropertyChanged;      public StrokeCollection MyStrokes     {         get         {             return _mystrokes;         }     }      private void OnPropertyChanged(string propertyName)     {         var handler = PropertyChanged;          if (handler != null)         {             handler(this, new PropertyChangedEventArgs(propertyName));         }     } } 

And XAML:

<InkCanvas Strokes="{Binding MyStrokes}"/> 

Edit :

Maybe the workaround for your case is to use EventToCommand : this allow tobind an UI event to an ICommand directly in XAML ( and use Args to pass a ref to the inkCancas)

<i:Interaction.Triggers>     <i:EventTrigger EventName="Loaded">         <cmd:EventToCommand Command="{Binding Mode=OneWay, Path=LoadedCommand}"                             PassEventArgsToCommand="True" />     </i:EventTrigger> </i:Interaction.Triggers> 
by : Cybermaxshttp://stackoverflow.com/users/1554201

Answer: 2

Per the comments above, one way to do this is to extend Canvas and keep the reference to CanvasRender inside that class.

public class MyCanvas : Canvas {     private CanvasRender _canvasRender;     private ProjectData _data;      public ProjectData Data     {         get { return _data; }                set         {             _data = value;             _canvasRender = new CanvasRender(this, _data);         }     }      public MyCanvas() : base()     {     } } 

You'd probably want to also make ProjectData a Dependency Property so that it's bindable.

This allows you to maintain the MVVM pattern, because now you can write in XAML:

<local:MyCanvas ProjectData="{Binding ViewModel.ProjectData}" /> 
by : dbasemanhttp://stackoverflow.com/users/1001985

Answer: 3

If your going to use the EventToCommand approach (which you tried in another answer), then instead of using the PassEventArgsToCommand property use the CommandParameter property and bind it to your Canvas.

<i:Interaction.Triggers>     <i:EventTrigger EventName="Loaded">         <cmd:EventToCommand Command="{Binding Path=CanvasLoadedCommand}"                             CommandParameter="{Binding ElementName=inkCanvas}" />     </i:EventTrigger> </i:Interaction.Triggers> 

Then in your ViewModel:

public class ViewModel {     private Canvas m_canvas;      public RelayCommand<Canvas> CanvasLoadedCommand { get; private set; }      public ViewModel()      {          CanvasLoadedCommand = new RelayCommand<Canvas>(canvas =>           {              m_canvas = canvas;         });      } } 

So as soon as your canvas is loaded, you should then have a reference to it saved in your view model.

by : bugged87http://stackoverflow.com/users/1575237




No comments:

Post a Comment

Send us your comment related to the topic mentioned on the blog