How to Bind a Grid to Viewmodel

Topics: Conventions
Aug 8, 2011 at 4:30 AM

I originally tried having a Grid bound to a Grid property on my Viewmodel, but I'm gradually understanding more about mvvm, and best practices etc, so I moved it and the associated methods to there own class, and Import a reference to the new Editor object as a property. I can reference the grid by using the x:Name="Editor_EditorGrid" syntax, but there is no convention for Grid, so it treats it as a FrameworkElement, which doesn't work.  

So, what should I be looking at to bind and show my grid?

Aug 9, 2011 at 7:01 PM

If you want to show/hide the grid itself, you can bind the grid Visibility property in XAML to a boolean property of the VM (bool GridVisible {get; set;}), using a BooleanToVisiblityConverter.
Make sure when to change to property in the VM, to call NotifyOfPropertyChange(() => GridVisible);

CM does a lot of auto-binding, but you can always write your own bindings next to that. 

With MVVM, the idea is that the VM does not know about the view (most of the time). So you don't need properties on your VM of type "Grid" or even type "Visibility".

Hope this helps.




Aug 9, 2011 at 8:59 PM
Edited Aug 9, 2011 at 8:59 PM

I can't understand what does the "Grid" property in your VM contain. Is it a collection of object to be bound to DataGrid.ItemsSource?

In that case, you just have to provide a convention for the DataGrid (in addition to builtin ones).
This is done using ConventionManager.AddElementConvention<> method.

Aug 9, 2011 at 9:42 PM

hmm I have to stop posting things when I'm tired.

The Grid in my view is a standard layout Grid panel, filled with shapes, see for some details.

I started with having the Grid created in my vm, as well as all the methods to manipulate it etc, but as I understand more about MVVM and CM, I moved it all into its own class Editor. and use IoC to import it into my VM. The VM shouldn't need to know anything about controls...

Now I'm trying to get the Grid that EditorGrid creates to show in the VM.

I first tried

<Grid x:Name="Editor_EditorGrid />

but it gives me an databinding error, about converting from Grid to Visibility, I assume because the ConventionManager is treating the Grid as a FrameworkElement. 

I tried changing <Grid> to >ContentControl> but that gives me a 'Cannot find view for System.Windows.Controls.Grid'

Of course, I might just be going about this in the entirely wrong way. but I'm hitting the limit of my knowledge here :D


Aug 9, 2011 at 9:49 PM
Edited Aug 9, 2011 at 9:50 PM

You need


<Grid x:Name="Editor_EditorGrid Visibility={Binding IsEditorGridVisible, Converter={StaticResource BooleanToVisibilityConverter}} />


And make the static resource converter of course.

Then in your VM:


public bool IsEditorGridVisible {get; set;} 
// make sure to call NotifyOfPropertyChange(() => IsEditorGridVisible);


Edit: this is no convention or CM magic, just regular binding.

Aug 10, 2011 at 12:09 AM

Hmm, after examining my original code, I haf

<UserControl Content="{Binding EditorGrid />

bound to a Grid property in my viewmodel.

I changed it to

<UserControl Content={Binding Editor.EditorGrid />

which now works. I'm not sure If it is better as my vm is dependent on an Editor object instead of a Grid, but its getting there...

Aug 10, 2011 at 12:33 AM

@alwinuz: I may be wrong, but it seems to me that KageDragon doesn't have to deal with Grid visibility at all.
Yet, I can't honestly say that I've fully understood the scenario...  

@KageDragon: As I understand, you built an Editor class aimed to "manipulate" a Grid, perhaps putting elements inside it.
I'm not sure if the Editor is shaped like a Factory/Builder or a Wrapper; in other words, does it release the Grid instance after an initial configuration or does it keep the instance as a private state in order to accomplish further manipulation?

In both cases, the approach is a simple (yet absolutely correct) abstraction of the UI component, more than a step towards a MVVM approach.
It may somewhat resemble an MVP/Passive View approach, in which a "presenter" manipulates the view through an abstraction.
I'm not saying that the approach is necessarily wrong; it's perfectly fine (and simpler, too), to switch to an MVP-style for a particular scenario, even in project completely built using MVVM pattern.
The point of MVVM, however, it's not _just_ to make VM ignorant of View concerns (which is fine) but also to build a model aimed to represent the *state* of the UI in abstract terms.
The actual View "modifications" should not occur through a direct (yet mediated by an abstraction) manipulation of the UI.
On the contrary, the View should "observe" the state of the VM and modify itself accordingly (which is usually obtained through bindings), thus "visually" representing the state of the VM.
Therefore, a View modification should only occur _indirectly_, as a result of a status change that the VM operates on itself (or on an associated sub-model).

If you want to describe what you are trying to build, I could try to help you modeling the VM and the View representation.

To help you to fix the actual implementation, instead, I need to understand some details of what you presently have: 
- who is in charge of creating the Grid (the Editor, I guess?)
- do you intended to "push" the Grid into the view, or you wanted the editor to work on the Grid already present in the xaml?
- how is the Grid instance exposed by Editor? 

Aug 10, 2011 at 4:46 AM

OK then, some background: I'm trying to build a railway Signalbox simulator, that in theory would allow me to display and edit any different type from around the world, as long as someone can write a dll for it.

The 1st one i'm doing, as its visually the simplest is a late-80's UK one called IECC. Here's a screenshot of what the real one looks like. It's essentially a fixed-width DOS mode app, with a custom font. It has nothing more complicated then some simple shapes. Also the only thing that changes is colours of various elements, and some shapes that display differently depending on circumstances, which sounded like a good fit for Styles and triggers.  Mine will be a large zoomable grid, as opposed to the original's paged character-mode multi-monitor format. Each 'system' will be a self-contained DLL with all the views etc allowing editing\simulating etc. I have this working already (thanks MEF). The visual representation and the actual physical data Length. position speed limits etc) will be separated, allowing many visual views of the same data....

Right now, I have a Grid, created by the IECCEditor class, and the IECCEditorViewModel imports the instance of that class, (I'll change it later to import IEditor\IGridEditor\ICanvasEditor) and the IECCEditorView binds to the grid using IECCEditor,EditorGrid. It's used as a layout container to hold the various shapes. The EditorGrid will remain as long as the editor is running so the user can add elements, drag and drop etc 

Right now, the EditorGrid is exposed as a simple property of IECCEditor.

I'm not sure what you mean by 'push' the EditorGrid.  Here is what I have in IECCView:


<UserControl Content={Binding Editor.EditorGrid />


Hope this makes sense, I'm writing at 12:30 am again :D

Aug 10, 2011 at 10:35 PM

That mostly confirms my guess. It seems pretty complex, so I'll just sketch a simplified model which is unlikely to fit the actual use cases.
I just want to show what I intended with "building a model representing the state of the UI".


class IECCEditorViewModel : Screen {
	public IECCEditorViewModel() {
		var cells = ... //obtain the cells
		Cells = new BindableCollection(cells);
	public BindableCollection<ICellViewModel> Cells { get; private set;}
<!-- IECCEditorView -->
<ItemsControl Name="Cells">
			<UniformGrid Columns="80" Rows="25" />


interface ICellViewModel {}

class EmptyCellViewModel: ICell {}
<!-- EmptyCellView -->
<UserControl ... >
	<!-- omitted -->

class RailwayCellViewModel: PropertyChangedBase, ICell {
	public Orientation Orientation {get; set;} //change notification omitted

<!-- RailwayCellView -->
<UserControl ... >
	<!-- omitted -->

class SemaphoreCellViewModel: PropertyChangedBase, ICell {
	public SemaphoreStatus Status {get; set;} //change notification omitted
<!-- SemaphoreCellView -->
<UserControl ... >
	<!-- omitted -->

Each implementation of ICell (a sub VM itself, composed in the main IECCEditorViewModel) has its own corresponding View. 
As long as the view is named following the naming convention, CM is able to pick exaclty the one matching each VM subtype (you can obtain the same using data templates, that are conceptually equivalent, but having separeted user controls helps keeping the code well organized).
The Cells collection in IECCEditorViewModel is automatically bound to the ItemsControl's ItemsSource property by CM's convention.
Finally, all the views corresponding to the VMs contained in Cells are laid out in a UniformGrid (which fills up each row from left to right) instead of a simple vertical list.

Please note that swapping two cells in the Cells collection or changing a property in a particular cell has the (indirect) effect of updating the View accordingly (thanks to bindings).
I'm aware that the "flat" Cells list may not be the best data structure for complex editing scenarios (though you can easily add an indexed property mapping each list position into x,y indexes); I hope you got the idea, however.

Aug 16, 2011 at 4:17 AM

Wow, I never would have though of using viewmodels\views for the cell objects, but It makes sense.

I've been experimenting with this, and have my BindableCollection of ICell's showing up nicely.

I have a somewhat related question. The BlankCellView has a ContextMenu, I've got the PlacementWithoutContext pointing at the BlankCellViewModel, and the MenuItem action will find a method in that viewmodel.

Since ICells are strictly UI things, and I also need to create the actual data objects behind the visuals, which are the IECCEditor class's job.

The vm that has the collection of ICells (IECCEditorViewModel) has the IECCEditor injected.

My first thought was inject the IECCEditor (as IEditor) into the BlankCellView, but that seems kinda wrong to me.




Aug 16, 2011 at 9:37 AM

I disagree: ICells are not *pure* UI concern, related Views (as BlankCellView) are. 
I realized that in my example I haven't stressed it enough, mentioning properties that could be misinterpreted as UI-oriented.
SemaphoreCellViewModel.SemaphoreStatus, though, was intended to represent a property that *could* be represented visually, but belongs to Application (or even Business) Logic.

In my idea, ICells are View Models, so each of them is responsible of handling the state of the single small portion of the app (including the data objects pertaining to it).
Obviously, you can decide to break down the structure and leave the data-related work in IECCEditor, but ICell *could* definitely have to do with IECCEditor, and hold a reference to it.
It can be questionable, however, whether an ICell has to "pull" its data from an IECCEditor or if IECCEditor should push data into ICell; it's a matter of design tastes.

Hope it helps.