在 Windows Phone 上如何在运行时加载 XAML ViewModel?

3

我目前正在参加Microsoft Virtual Academy的Windows Phone教程,其中一个挑战是在项目中使用设计XAML视图模型,并在运行时加载。

研究了数小时后,我认为该求助Stack Overflow了,因为我没有得到任何进展。我阅读了许多文章,但没有给出正确答案,所以我有几个问题:

  1. 如何修复我的错误?
  2. 如何通过编程方式在运行时加载XAML模型视图?
  3. 如何使用XAML在运行时加载XAML模型视图?
  4. 在哪里调用运行时加载XAML的方法?

样本数据文件SoundViewModelSampleData.xaml看起来像这样:

<vm:SoundViewModel
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:vm="clr-namespace:Soundboard.ViewModels"
    xmlns:mo="clr-namespace:Soundboard.Models">

    <vm:SoundViewModel.Animals>
        <vm:SoundGroupViewModel Title="Animals Sample">
            <vm:SoundGroupViewModel.Items>
                <mo:SoundDataModel Title="Animals 1" FilePath="Animals.wav" />
            </vm:SoundGroupViewModel.Items>
        </vm:SoundGroupViewModel>
    </vm:SoundViewModel.Animals>
    <vm:SoundViewModel.Cartoons>
        <vm:SoundGroupViewModel Title="Cartoons Sample">
            <vm:SoundGroupViewModel.Items>
                <mo:SoundDataModel Title="Cartoons 1" FilePath="Cartoons.wav" />
                <mo:SoundDataModel Title="Cartoons 2" FilePath="Cartoons.wav" />
            </vm:SoundGroupViewModel.Items>
        </vm:SoundGroupViewModel>
    </vm:SoundViewModel.Cartoons>
</vm:SoundViewModel>

我找到的最简单的通过编程加载它的代码是:
string path = @".\SampleData\SoundViewModelSampleData.xaml";
using (System.IO.StreamReader reader = new System.IO.StreamReader(path))
{
    SoundViewModel vm = XamlReader.Load(reader.ReadToEnd()) as SoundViewModel;
}

虽然我可能现在从错误的位置调用它,但我遇到了以下错误:

类型为 'System.Windows.Markup.XamlParseException' 的第一次机会异常发生在 System.Windows.ni.dll 中

{System.Windows.Markup.XamlParseException: 未知的解析器错误: Scanner 2147500037。[行: 5 位置: 14] at MS.Internal.XcpImports.CreateFromXaml(String xamlString, Boolean createNamescope, Boolean requireDefaultNamespace, Boolean allowEventHandlers, Boolean expandTemplatesDuringParse, Boolean trimDeclaredEncoding) at System.Windows.Markup.XamlReader.Load(String xaml) at Soundboard.ViewModels.SoundViewModel.LoadData()}

未知的解析器错误: Scanner 2147500037。[行: 5 位置: 14]

假设我可以解决这个错误,这将解决我的问题1和2(修复错误并以编程方式加载数据)。

你能找出是什么导致了这个问题吗?

如上所述,我可能在错误的位置加载它,即在应用程序加载时创建 ViewModel 内部。

namespace Soundboard.ViewModels
{
    public class SoundViewModel
    {
        public SoundGroupViewModel Animals { get; set; }
        public SoundGroupViewModel Cartoons { get; set; }

        public bool IsDataLoaded { get; set; }

        public void LoadData()
        {
            string path = @".\SampleData\SoundViewModelSampleData.xaml";
            using (System.IO.StreamReader reader = new System.IO.StreamReader(path))
            {
                SoundViewModel vm = System.Windows.Markup.XamlReader.Load(reader.ReadToEnd()) as SoundViewModel;
        }
        IsDataLoaded = true;
    }
}

在我的app.xaml.cs中,我有以下内容:

public static SoundViewModel SoundViewModel
{
    get
    {
        if (_soundViewModel == null)
        {
            _soundViewModel = new SoundViewModel();
            _soundViewModel.LoadData();
        }

        return _soundViewModel;
    }
}

现在我该如何只使用XAML来实现运行时,并使用d:datacontext进行设计时呢?
我已经阅读了一些文章,但它们都是关于WPF的,大多数与加载用户控件等有关,但没有涉及到ViewModel。
非常感谢您的帮助。
谢谢。
2个回答

3

我曾经也遇到了和你类似的问题,是关于XamlReader的。我发现即使在同一个程序集里面,你也需要在xaml文件的根元素中定义程序集的命名空间。在下面的示例代码中,即使xaml文件包含在SoundBoard.dll中,我也会在xaml文件中声明它的命名空间。

xmlns:vm = "clr-namespace:SoundBoard.ViewModels;assembly=SoundBoard">

嗨bahti,非常感谢!!问题解决了!我最终将XamlReader放在app.xaml.cs中,但感觉不太对!你知道更好的方法吗? - Thierry

1

我也尝试过这样做,最好的结果是将数据XAML文件移动到assets目录下,并将其标记为资源(同时删除Custom Tool),然后使用以下代码进行加载:

    public void LoadData()
    {
        // Load data
        //LoadCodeData();
        LoadXamlData();

        IsDataLoaded = true;
    }

    private void LoadXamlData()
    {
        string path = "SoundBoard;component/assets/runtimecontent/SampleData.xaml";
        Uri uri = new Uri(path, UriKind.Relative);
        using (System.IO.StreamReader reader = new System.IO.StreamReader((System.Windows.Application.GetResourceStream(uri)).Stream))
        {
            SoundModel model = System.Windows.Markup.XamlReader.Load(reader.ReadToEnd()) as SoundModel;
            this.Animals = model.Animals;
            this.Cartoons = model.Cartoons;
            this.Taunts = model.Taunts;
            this.Warnings = model.Warnings;
            this.CustomSounds = model.CustomSounds;
        }
    }

我也做了Bahti建议的事情。


LoadData通常位于ViewModel中,那么你的位于哪里?在app.xaml.cs中吗?我尝试了与你类似的东西,直接在我的ViewModel中使用以下代码:this = XmalReader.Load,但它不喜欢它,所以最终我把这段代码放在了app.xaml.cs中,但仍然不喜欢它!从那里,我可以设置静态的SoundViewModel,该模型可以在整个应用程序中访问。 - Thierry
我认为你不能直接分配给'this'指针。这就是为什么我要分配给属性的原因。我的LoadData函数在教程期间创建的SoundModel类中。我也做了Bahti建议的事情。 - Defeqel
抱歉我错过了那部分内容,但现在我明白你的意思了!我会尝试一下的!谢谢。T. - Thierry
我刚刚尝试使用了您的方法(在必要时进行修改),但是当我运行它时,我会收到AccessViolationException。它在执行以 using 开头的行时抛出。我已经按照Bahti建议的做了,并且我还将xaml文件的构建操作标记为“内容”。您有任何想法为什么会发生这种情况吗? - Lev Dubinets

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接