Silverlight 4,动态加载XAP模块

6

我知道使用Prism或MEF框架可以动态加载xap模块。然而,我不想使用这些框架,而是手动加载我的xap文件。因此,我创建了以下类(改编自互联网):

public class XapLoader
{
    public event XapLoadedEventHandler Completed;

    private string _xapName;

    public XapLoader(string xapName)
    {
        if (string.IsNullOrWhiteSpace(xapName))
            throw new ArgumentException("Invalid module name!");
        else
            _xapName = xapName;
    }

    public void Begin()
    {
        Uri uri = new Uri(_xapName, UriKind.Relative);
        if (uri != null)
        {
            WebClient wc = new WebClient();
            wc.OpenReadCompleted += onXapLoadingResponse;
            wc.OpenReadAsync(uri);
        }
    }

    private void onXapLoadingResponse(object sender, OpenReadCompletedEventArgs e)
    {
        if ((e.Error == null) && (e.Cancelled == false))
            initXap(e.Result);

        if (Completed != null)
        {
            XapLoadedEventArgs args = new XapLoadedEventArgs();
            args.Error = e.Error;
            args.Cancelled = e.Cancelled;
            Completed(this, args);
        }
    }

    private void initXap(Stream stream)
    {
        string appManifest = new StreamReader(Application.GetResourceStream(
        new StreamResourceInfo(stream, null), new Uri("AppManifest.xaml",
                                       UriKind.Relative)).Stream).ReadToEnd();

        XElement deploy = XDocument.Parse(appManifest).Root;

        List<XElement> parts = (from assemblyParts in deploy.Elements().Elements()
                                select assemblyParts).ToList();

        foreach (XElement xe in parts)
        {
            string source = xe.Attribute("Source").Value;
            AssemblyPart asmPart = new AssemblyPart();
            StreamResourceInfo streamInfo = Application.GetResourceStream(
                 new StreamResourceInfo(stream, "application/binary"), 
                 new Uri(source, UriKind.Relative));
            asmPart.Load(streamInfo.Stream);
        }
    }
}

public delegate void XapLoadedEventHandler(object sender, XapLoadedEventArgs e);

public class XapLoadedEventArgs : EventArgs
{
    public Exception Error { get; set; }

    public bool Cancelled { get; set; }
}

上述代码可以正常工作,我可以用以下方法加载任何xap:
XapLoader xapLoader = new XapLoader("Sales.xap");
xapLoader.Completed += new XapLoadedEventHandler(xapLoader_Completed);
xapLoader.Begin();

现在,我有一个名为InvoiceView的UserControl位于Sales.xap项目中,所以我想要实例化这个类。在当前的Main.xap项目中,我添加了对Sales.xap项目的引用,但是由于我手动加载它,所以我将"Copy Local = False"设置为false。但是当执行时,以下代码会抛出TypeLoadException异常:
Sales.InvoiceView view = new Sales.InvoiceView();

看起来代码找不到InvoiceView类。但我检查了XapLoader的initXap()方法已经成功执行。那么为什么代码找不到InvoiceView类呢?有人能帮我解决这个问题吗?


一种更简单的方法是使用 Silverlight 4 内置的托管可扩展性框架(MEF)以及 .NET 4。 - Chris Pietschmann
4个回答

1

好的,我找到了原因。上面的代码是有效的。在创建一个新的Silverlight项目(Sales.xap)后,我偶然编译了一次我的解决方案。然后我删除了Sales.xap中的App类,并将默认的MainPage类重命名为SalesView。然而,无论我编译多少次我的解决方案,Visual Studio的开发Web服务器都会加载Sales.xap的第一个版本(从哪里来的?),所以我的代码找不到SalesView。在我的主机Asp.Net项目中,我将开发服务器的端口号设置为不同的端口号,问题就解决了。所以问题出在Visual Studio的开发服务器上。显然,它将编译后的xap文件保存在某个临时文件夹中,并且当源代码更改时并不总是更新这些xap文件。


我之前遇到了同样的问题,当时我使用的是Chrome - @Xds说得对。这个问题似乎与Chrome过度缓存xap文件有关!如果你在同一URL下打开一个隐身窗口,你会看到最新的更改(因为隐身模式不进行缓存)。此外,如果你在IE浏览器中打开相同的URL,它也会显示最新版本 - 它不进行缓存。我们希望Google Chrome不要如此严格地缓存xap文件。:( - DeeStackOverflow

1

这是基于提问者下面的自我回答,而不是问题本身。

如果你删除一个项目/模块,输出的DLLs/XAP文件仍然存在。如果你点击“显示所有文件”按钮,你会在相关项目的clientbinbinobj文件夹中看到一些残留的输出文件。

alt text

你可以逐个从项目中删除它们,或者当存在疑虑时,搜索所有 BIN 和 OBJ 文件夹(例如使用桌面浏览器)并删除所有这些文件夹。BIN/CLIENTBIN/OBJ 文件夹将在需要时重新创建(这是 Visual Studio 中“清理”选项应该完成的工作!)

希望这有所帮助。


我选择了您的答案作为正确答案,但是我注意到Visual Studio的Build->Clean菜单没有帮助。删除bin/obj文件夹可能会有所帮助(我还没有这样做),我只是不想重复这样做。所以,目前我正在做的是更改Web服务器的端口号。顺便说一下,即使我什么都没有重命名,这个问题也经常发生。 - synergetic

0

当我执行新编译的Silverlight时,避免出现这样的问题的方法是清除浏览器缓存。甚至Chrome有一个清除Silverlight缓存的选项 ;)


0

这种 XAP 缓存现象通常是由于 Visual Studio 嵌入式 Web 服务器(ASP.NET Development Server)引起的。 只需停止该服务器的出现,缓存就会被清除。 然后重新启动项目,最新版本的 xap 将被调用。


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