WP7 + (XAML 中设置的数据上下文定位器) + (将应用程序栏绑定到命令)= NullReferenceException

发布于 2024-12-12 00:23:54 字数 8049 浏览 0 评论 0原文

我正在使用 MVVM Light Toolkit 开发一个 Silverlight WP7 应用程序,该应用程序最初运行正常。 对于每个视图,我使用以下代码在视图的代码隐藏中设置其 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)

知道吗?

谢谢

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

鯉魚旗 2024-12-19 00:23:54

您实际看到的 ChangeIsEnable 方法中的 NullReferenceException 是 AppBarUtils 的一个错误。
此错误已在 http://appbarutils.codeplex.com/discussions/274048 报告,并已已在最新版本中修复。 AppBarUtils 现在可以毫无问题地与 MVVM Light 配合使用。

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.

你怎么敢 2024-12-19 00:23:54

最后与 Microsoft MVP 一起审查了该问题,他告诉我这只是 ApplicationBar 的限制。他给我的原因是 ApplicationBar 不是一个通用控件,并且不支持像这个一样的某些绑定。

在我看来,这是一个错误,应该解决。

感谢您的回答

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

猫腻 2024-12-19 00:23:54

您可以选择两种解决方法。

  • 第一个解决方法

    在您的 XAML 中,

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

    编码错误,因为您将类名称设置为绑定路径而不是实例名称。
    要解决此问题,您必须初始化MainPageViewModel 实例并使用它。

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

    上面的代码是正确的。 Main 是 MainPageViewModel 的一个实例。
    这是MVVM-Light工具包的一个默认模板。因此,您可以在 ViewModelLocator.cs 文件中检查 VM 实例是如何初始化和使用的。

    仅供参考,ViewModelLocator.cs 的工作流程如下。

    1. 很久很久以前,有一个名为 _main 的静态 MainViewModel。
    2. App.XAML 调用 ViewModelLocator,ViewModelLocator 调用初始化 _main 的 CreateMain 方法。
    3. 只读 Main 属性调用 _main
    4. 因此,当在任何具有上述神奇一行的 XAML 中调用 MainDataContext=Main, Source={StaticResource Locator},它给出 MainViewModel 的相同且单一的实例。

  • 第二种解决方法

    我无法清楚地理解为什么需要在其代码隐藏中设置 XAML DataContext。但是,如果您想让每个 XAML 消耗不同的 DataContext,请首先在代码隐藏中初始化 VM,如下所示。

    private SomeViewModel vm = new SomeViewModel();
    
    // 构造函数
    公共主页()
    {
        this.DataContext = vm;
    }
    

在这种情况下,每个 XAML 使用不同的 ViewModel 实例。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文