WP7 + (локатор Datacontext, установленный в XAML) + (привязка панели приложения к команде) = NullReferenceException - PullRequest
0 голосов
/ 25 октября 2011

Я занимаюсь разработкой приложения Silverlight WP7 с использованием MVVM Light Toolkit, который изначально работает правильно. Для каждого просмотра я установил его DataContext в кодовом виде представления, используя следующий код:

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

Мой ViewModelLocator объявлен в файле App.xaml

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

Даже если я пытаюсь установить текст данных в xaml вместо того, чтобы делать это в коде, я получаю сообщение об ошибке.

Это код:

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

Это последний 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>

Это моя 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;
        }

    }

  }
}

Подсказка в том, что если я удалю привязку AppBarItemCommand и добавлю дополнительные элементы управления с привязками к другим свойствам в модели представления, приложение будет работать без проблем. Проблема возникает только тогда, когда я устанавливаю привязку для AppBarItemCommand.

Это трассировка стека, которую возвращает исключение:

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)

Есть идеи?

Спасибо

Ответы [ 3 ]

4 голосов
/ 18 октября 2012

То, что вы на самом деле видели, метод NullReferenceException от ChangeIsEnable, был ошибкой AppBarUtils.
Эта ошибка была зарегистрирована в http://appbarutils.codeplex.com/discussions/274048 и была исправлена ​​в последней версии.AppBarUtils теперь может работать с MVVM Light без каких-либо проблем.

1 голос
/ 27 октября 2011

Наконец, рассмотрев проблему с Microsoft MVP, он сказал мне, что это всего лишь ограничение ApplicationBar. Он объяснил мне, что ApplicationBar не является общим элементом управления и не поддерживает определенные привязки, как этот.

На мой взгляд, это ошибка, и она должна быть решена.

Спасибо за ваши ответы

0 голосов
/ 27 октября 2011

Два обходных пути, которые вы можете выбрать.

  • Первый обходной путь

    В вашем XAML,

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

    неверно закодирован, потому что вы задаете имя класса как путь привязки, а не имя экземпляра. Чтобы это исправить, вы должны инициализировать экземпляр MainPageViewModel и использовать его.

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

    Над кодом правильный. Main является экземпляром MainPageViewModel. Это часть стандартного шаблона набора инструментов MVVM-Light. Таким образом, вы можете проверить, как экземпляр VM инициализируется и используется в файле ViewModelLocator.cs .

    К вашему сведению, последовательность действий ViewModelLocator.cs выглядит следующим образом.

    1. Давным-давно существует статическая модель MainViewModel с именем _main .
    2. App.XAML вызывает ViewModelLocator, а ViewModelLocator вызывает метод CreateMain, который инициализирует _main .
    3. Только для чтения Main свойство вызывает _main .
    4. Поэтому, когда Main вызывается в любом XAML с одной волшебной строкой выше DataContext = Main, Source = {StaticResource Locator} , это дает и один экземпляр MainViewModel.

  • Второй обходной путь

    Я не могу четко понять, почему вам нужно настроить XAML DataContext в его коде. но если вы хотите, чтобы каждый XAML использовал разные DataContext, вы сначала инициализируете виртуальную машину в коде, как показано ниже.

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

В этом случае каждый XAML использует разные экземпляры ViewModel.

...