Basic Question -> DataContext

Topics: Getting Started
Oct 5, 2012 at 12:13 PM

Hey

There are some things, I don't understand yet and I someone can help me out of my pane.

Following Scenario:

I have a class, which works when I DON'T use MVVM in the way, that I bind code like this to the datacontext of the View:

Public Sub New()
            InitializeComponent()
            Me.DataContext =  New MainViewModel()
 End Sub

What this code does is: creating the Buttons for a Ribbon and I want to bind them. The code snipset to this Looks like this:

<telerik:RadRibbonView x:Name="ribbonView" ApplicationButtonVisibility="Collapsed"
				ApplicationName="{Binding AppName}" ItemsSource="{Binding Tabs}" Title="{Binding Title}"
				ItemTemplate="{StaticResource TabTemplate}" SelectedItem="{Binding SelectedTab, Mode=TwoWay}">
			<telerik:RadRibbonView.QuickAccessToolBar>
				<telerik:QuickAccessToolBar ItemsSource="{Binding QuickAccessItems}"
						ItemTemplate="{StaticResource ButtonTemplate}" />
			</telerik:RadRibbonView.QuickAccessToolBar>
		</telerik:RadRibbonView>

Now I have read, that binding to the DataContext is not the right way with Caliburn.

But I don't find the right way for doing this. - PLEASE HELP! - THX

Oct 5, 2012 at 12:30 PM
Edited Oct 5, 2012 at 2:01 PM

If the snippet is taken from your main view and it is called MainView, you don't need to set the DataContext explicitly: CM does it for you, provided that you have configured your Bootstrapper as needed.

Note that setting the view-model inside the view (as you are doing), is considered a view-first approach (the view is instantiated before its view-model). CM supports both view-first and view-model-first scenarios. Depending on your needs, you can use either of them (I prefer view-model-first, by the way).

On a side note, it is not true that binding TO the DataContext is not the right way, in CM... it's SETTING the DataContext explicitly in the view which is something like a code smell, in a CM application (you should use either View.Model or Bind.Model/Bind.ModelWithoutContext depending on the fact that you are using a view-model-first or view-first approach).

Oct 5, 2012 at 12:52 PM
Edited Oct 5, 2012 at 12:58 PM

Hey

Thx for your reply, but I don't get it working anyway...

I have a "HauptmenueView" which holds the Usercontrol from which the above code Comes. So in the working main view (With working, I mean, the ViewModel Code is fired)

<Window
		xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
		xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
		xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
		xmlns:MainView="clr-namespace:OPT_Studio" 
        xmlns:Main="clr-namespace:OPT_Studio.Views.Main" 
        x:Class="HauptmenueView"
		Height="350" Width="525" WindowState="Maximized">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Main:MVVMView />

    </Grid>
</Window>
This is the XAML of the "Main View", which fires the correct ViewModel's code.
But the <Main:MVVMView/> does not fire it's ViewModel's Code
 
In the View's Project, the File is in the Namespace: Views.Main 
And in the ViewModels Project, the depending file is in Namespace: Ribbon.Customer
 The additional code of the bootstrapper is:
        Caliburn.Micro.ViewLocator.AddNamespaceMapping("ISViewModel", "OPT_Studio")
        Caliburn.Micro.ViewLocator.AddNamespaceMapping("ISViewModel.Ribbon.Customer", "OPT_Studio.Views.Main")
But it isn't working....
 
Oct 5, 2012 at 1:59 PM
Edited Oct 5, 2012 at 2:00 PM

How did you configure your Bootstrapper, regarding the startup part? How is the HauptmenueView created? Is there an HauptmenueViewModel which is created by the Bootstrapper? What kind of approach do you intend to use, view-model-first or view-first? Unless I know these things, I can only tell you to have a careful read to the documentation and samples provided in the project.

In a view-model first approach, you need to create view-models inside either the Bootstrapper or other view-models, and let CM discover views (both main view and internal ones). If you main view is created by CM, you need to substitute the

<Main:MVVMView />

part with something like this

<ContentControl cm:View.Model="{Binding Path=PropertyHoldingViewModel}"/>

On the contrary, in a view-first approach you need to substitute the MVVMView control with

<ContentControl cm:Bind.Model="KeyUsedToRegisterTheViewModelInIoC"/>
Oct 5, 2012 at 3:06 PM

Thanks for your time!

So, handled a few things new, so I can give you parts of the source code and it should be easier to understand:

Bootstrapper:

Imports System.Reflection
Imports System.Collections.ObjectModel
Imports Telerik.Windows.Controls

Public Class AppBootstrapper
    Inherits Caliburn.Micro.Bootstrapper(Of ISViewModel.HauptmenueViewModel)


    Sub New()

        Caliburn.Micro.ViewLocator.AddNamespaceMapping("ISViewModel", "OPT_Studio")
        Caliburn.Micro.ViewLocator.AddNamespaceMapping("ISViewModel.ViewModels.Article.Input", "OPT_Studio.Views.Article.Input")

    End Sub

    Protected Overrides Function SelectAssemblies() As IEnumerable(Of Assembly)

        Return Enumerable.Repeat(Assembly.GetExecutingAssembly, 1)

    End Function

End Class

 

By running this simple code, the View called "HauptmenueView" is created. (In the main Project, where the bootstrapper is also in: OPT_Studio)

In the HauptmenueView, I have the following XAML right now:

<Window
		xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
		xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
		xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
		
        xmlns:Input="clr-namespace:OPT_Studio.Views.Article.Input" 
        x:Class="HauptmenueView"
		Height="350" Width="525" WindowState="Maximized">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        
        <Input:ArticleInputView Grid.Row="1"/>

    </Grid>
</Window>

 

So, this Shows up the Window and also my Usercontrol Called ArticleInputView => Works fine, but the code of the ViewModel, depending to ArticleInputView is not fired.

In this code, there is (at the Moment) just a Public Sub New in there. - > This should be fired.

The ViewModels Code is in the second Project called: ISViewModel, which I set at the bootstrapper.

When I just Change the Inherits code to the ViewModels Code of ArticleInputView then it is fired (What makes sense, - but it also Shows me, that the way seems to be correct)

The question is now:

You wrote:

<ContentControl cm:View.Model="{Binding Path=PropertyHoldingViewModel}"/>

But I don't have a Property (at the Moment) but it should be fired anyway.

THX

PS: I want ViewModel First Approach.

 

Oct 5, 2012 at 3:18 PM

To deal with a view-model-first approach you need to get rid of the code in the views used to instantiate view-models, instead you have to provide view-model properties exposing them.

Note that if CM instantiates the views (using View.Model), whatever DataContext you have set will be automatically replaced by CM using the value provided by the property. Maybe this is the reason why your view-model code is not called at all: as soon as the view is displayed, its DataContext is erased... nevertheless if you put a breakpoint in at the point where the VM is created, you should note that the code is actually called, it is just that the instance is no more available at the time you 'see' the view.

Oct 5, 2012 at 4:21 PM

OH WOW - I did it!!!!!

Maybe, I am not alone with this Problem, I post the solution.

First of all: THX BladeWise! - You helped a lot

So, the Clou was, that I have to publish a Property of the second view! - This I have had forgotten

Public Sub New()
        SubModule = New ViewModels.Article.Input.ArticleInputViewModel
    End Sub
    Public Property SubModule() As ViewModels.Article.Input.ArticleInputViewModel

 

This is the code of the HauptmenueView. - Ther the "SubModule" is created as a property. And this property can be bound to CM

<ContentControl x:Name="myInput" cal:View.Model="{Binding SubModule}" Grid.Row="1" />

 

And now, the the second view (is displayed and the code is also fired correct.

Have a nice day