0
votes

I'm developing a Silverlight WP7 app using MVVM Light Toolkit which initially works properly. For each View I set its DataContext in the codebhind of the View using the following code:

public partial class MainPage : PhoneApplicationPage
{
    // Constructor
    public MainPage()
    {
        InitializeComponent();
        DataContext = ViewModelLocator.MainPageViewModelStatic;                          
    }
}

My ViewModelLocator is declared at the App.xaml file

<Application.Resources>        
    <vm:ViewModelLocator x:Key="Locator" 
                        d:IsDataSource="True" />
</Application.Resources>

Eventhough when I try to set the datacontext within the xaml instead of do it this within the codebehind I get an error.

This is the codebehind:

public partial class MainPage : PhoneApplicationPage
{
    // Constructor
    public MainPage()
    {
        InitializeComponent();
        //DataContext = ViewModelLocator.MainPageViewModelStatic;                          
    }
}

This is the final XAML:

<phone:PhoneApplicationPage 
x:Class="Views.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:au="clr-namespace:AppBarUtils;assembly=AppBarUtils"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="696"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True"   
DataContext="{Binding MainPageViewModel, Source={StaticResource Locator}}">

<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
</Grid>

<!--Sample code showing usage of ApplicationBar-->

<phone:PhoneApplicationPage.ApplicationBar>
    <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True" BackgroundColor="{StaticResource PhoneAccentColor}">
        <shell:ApplicationBarIconButton IconUri="/Resources/Icons/appbar.check.rest.png" Text="Iniciar" />            
        <shell:ApplicationBar.MenuItems>
            <shell:ApplicationBarMenuItem Text="Iniciar" />                
        </shell:ApplicationBar.MenuItems>
    </shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>

<i:Interaction.Behaviors>
    <au:AppBarItemCommand
    ItemText="Iniciar" Command="{Binding IniciarSesion}" />
</i:Interaction.Behaviors>

</phone:PhoneApplicationPage>

This is my ViewModel:

namespace ViewModels
{
  public class MainPageViewModel : ViewModelBase
  {
    public const string mensajePropertyName = "mensaje";

    private string _mensaje = "";

    public string mensaje
    {
        get
        {
            return _mensaje;
        }

        set
        {
            if (_mensaje == value)
            {
                return;
            }

            var oldValue = _mensaje;
            _mensaje = value;

            RaisePropertyChanged(mensajePropertyName);
        }
    }       

    private RelayCommand _iniciarSesion;

    public RelayCommand IniciarSesion
    {
        get
        {
            if (_iniciarSesion == null)
            {
                _iniciarSesion=new RelayCommand((()=> this.mensaje="hola"));
            }
            return _iniciarSesion;
        }

    }

  }
}

A clue is that if I remove the AppBarItemCommand binding and I add more controls with bindings to other properties in the viewmodel the app runs without problems. The problem appears just when I set the binding for the AppBarItemCommand.

This is the stack trace the exception returns:

at AppBarUtils.AppBarItemCommand.ChangeIsEnabled()
at AppBarUtils.AppBarItemCommand.OnCommandChanged(DependencyPropertyChangedEventArgs e)
at AppBarUtils.AppBarItemCommand.CommandPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
at System.Windows.DependencyObject.RaisePropertyChangeNotifications(DependencyProperty dp, Object oldValue, Object newValue)
at System.Windows.DependencyObject.UpdateEffectiveValue(DependencyProperty property, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, ValueOperation operation)
at System.Windows.DependencyObject.RefreshExpression(DependencyProperty dp)
at System.Windows.Data.BindingExpression.RefreshExpression()
at System.Windows.Data.BindingExpression.SendDataToTarget()
at System.Windows.Data.BindingExpression.SourceAcquired()
at System.Windows.Data.BindingExpression.InheritanceContextChanged(Object sender, EventArgs e)
at System.Windows.DependencyObject.OnInheritanceContextChanged(Object sender, EventArgs e)
at System.Windows.DOCollection.AddInternal(DependencyObject value)
at System.Windows.PresentationFrameworkCollection`1.Add(DependencyObject value)
at System.Windows.DependencyObjectCollection`1.System.Collections.IList.Add(Object value)
at MS.Internal.XamlManagedRuntimeRPInvokes.Add(XamlQualifiedObject& qoCollection, XamlPropertyToken inCollectionProperty, XamlQualifiedObject& inValue)
at MS.Internal.XcpImports.Application_LoadComponentNative(IntPtr pContext, IntPtr pComponent, UInt32 cUriStringLength, String uriString, UInt32 cXamlStrLength, Byte* pXamlStr, UInt32 cAssemblyStrLength, String assemblyStr)
at MS.Internal.XcpImports.Application_LoadComponent(IManagedPeerBase componentAsDO, String resourceLocator, UnmanagedMemoryStream stream, UInt32 numBytesToRead, String assemblyString)
at System.Windows.Application.LoadComponent(Object component, Uri resourceLocator)
at PCCOM.Distrib.Presentation.PreventaClient.Views.MainPage.InitializeComponent()
at PCCOM.Distrib.Presentation.PreventaClient.Views.MainPage..ctor()
at System.Reflection.RuntimeConstructorInfo.InternalInvoke(RuntimeConstructorInfo rtci, BindingFlags invokeAttr, Binder binder, Object parameters, CultureInfo culture, Boolean isBinderDefault, Assembly caller, Boolean verifyAccess, StackCrawlMark& stackMark)
at System.Reflection.RuntimeConstructorInfo.InternalInvoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, StackCrawlMark& stackMark)
at System.Activator.InternalCreateInstance(Type type, Boolean nonPublic, StackCrawlMark& stackMark)
at System.Activator.CreateInstance(Type type)
at System.Windows.Navigation.PageResourceContentLoader.BeginLoad_OnUIThread(AsyncCallback userCallback, PageResourceContentLoaderAsyncResult result)
at System.Windows.Navigation.PageResourceContentLoader.<>c__DisplayClass4.<BeginLoad>b__0(Object args)
at System.Reflection.RuntimeMethodInfo.InternalInvoke(RuntimeMethodInfo rtmi, Object obj, BindingFlags invokeAttr, Binder binder, Object parameters, CultureInfo culture, Boolean isBinderDefault, Assembly caller, Boolean verifyAccess, StackCrawlMark& stackMark)
at System.Reflection.RuntimeMethodInfo.InternalInvoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, StackCrawlMark& stackMark)
at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
at System.Delegate.DynamicInvokeOne(Object[] args)
at System.MulticastDelegate.DynamicInvokeImpl(Object[] args)
at System.Delegate.DynamicInvoke(Object[] args)
at System.Windows.Threading.DispatcherOperation.Invoke()
at System.Windows.Threading.Dispatcher.Dispatch(DispatcherPriority priority)
at System.Windows.Threading.Dispatcher.OnInvoke(Object context)
at System.Windows.Hosting.CallbackCookie.Invoke(Object[] args)
at System.Windows.Hosting.DelegateWrapper.InternalInvoke(Object[] args)
at System.Windows.RuntimeHost.ManagedHost.InvokeDelegate(IntPtr pHandle, Int32 nParamCount, ScriptParam[] pParams, ScriptParam& pResult)

Any idea?

Thank you

3
So, what exactly is your question?Rhys
Without seeing the actual binding and the ViewLocator it is hard to tell what the problem ist. If you are getting the problem with the code you presented it is most likely that the resource you are binding to the Background property or the image is not present.AxelEckenberger
@Rhys What I want to know is if I have made a mistake that explains the failure when there is a binding to AppBarItemCommand and the declaration of the DataContext is done on the XAML. If I change one of these two conditions (I remove the AppBarItemCommand binding or I set the DataContext in the code behind) it starts to work properly, independently of whether I have other controls binded to my viewmodel or not.jersiovic
@Obalix I didn't showed the ViewLocator because it works properly when I remove the binding from the AppBarItemCommand and I add a binding from other control as a textbox with my viewmodeljersiovic

3 Answers

4
votes

What you actually saw, the NullReferenceException from ChangeIsEnable method, was a bug of AppBarUtils.
This bug was reported at http://appbarutils.codeplex.com/discussions/274048 and has been fixed in the latest version. AppBarUtils now can work with MVVM Light without any issues.

1
votes

Finally reviewing the problem with a Microsoft MVP, he told me it is just a limitation of the ApplicationBar. The reason he gave me was that ApplicationBar is not a common control and doesn't support certain bindings as this one.

In my opinion it is bug and it should be solved.

Thanks for your answers

0
votes

Two workarounds that you can choose.

  • First workaround

    In Your XAML,

    DataContext="{Binding MainPageViewModel, Source={StaticResource Locator}}"

    is wrongly coded, because you set Class name as a Binding path not a instance name. To fix this, you have to initialize instance of MainPageViewModel and use it.

    DataContext="{Binding Main, Source={StaticResource Locator}}"

    Above code is a proper one. The Main is an instance of MainPageViewModel. This is a piece of default template of MVVM-Light toolkit. So, you can check how VM instance is initialized and consumed in ViewModelLocator.cs file.

    FYI, the flow of how ViewModelLocator.cs works is as follows.

    1. Long long times ago, there is a static MainViewModel called _main.
    2. App.XAML calls ViewModelLocator, and ViewModelLocator calls CreateMain method which initializes _main.
    3. The readonly Main property calls the _main.
    4. Therefore, when Main is called in any XAML with above magic one line DataContext=Main, Source={StaticResource Locator}, it gives same-and-single instance of MainViewModel.

  • Second workaround

    I cannot clearly understand why you need to set up XAML DataContext in its code-behind. but, if you want to make each XAML consumes different DataContext, you first initialize VM in code-behind as below.

    private SomeViewModel vm = new SomeViewModel();
    
    // Constructor
    public MainPage()
    {
        this.DataContext = vm;
    }
    

In this case, each XAML consumes different ViewModel instance.