Как заставить WPF использовать URI ресурса, которые используют строгое имя сборки? Argh! - PullRequest
18 голосов
/ 21 сентября 2009

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

Это оказалось так, и теперь это вызывает у меня проблемы - у меня есть система плагинов, которая должна поддерживать установку плагинов бок о бок, которые отличаются только номерами версий (версиями сборки). Это, конечно, может поддерживаться .NET, поскольку сборки имеют разные идентификаторы, даже если у них одно и то же имя файла DLL, при условии, что они имеют строгое имя и либо имеют открытый / закрытый ключ, либо имеют другой номер версии сборки.

Теперь, если мы посмотрим на код, сгенерированный для windows и usercontrols Visual Studio, мы увидим в автоматически сгенерированном файле следующее:

/// <summary>
/// InitializeComponent
/// </summary>
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public void InitializeComponent() {
    if (_contentLoaded) {
        return;
    }
    _contentLoaded = true;
    System.Uri resourceLocater = new System.Uri("/Sensormatic.AMK1000.Panel;component/views/servicepanelui.xaml", System.UriKind.Relative);

    #line 1 "..\..\..\Views\ServicePanelUI.xaml"
    System.Windows.Application.LoadComponent(this, resourceLocater);

    #line default
    #line hidden
}

Обратите внимание на строку, в которой создан указатель ресурса - он использует относительный URI, который не указывает строгое имя, или версию сборки, которая содержит ресурс xaml.

Я подумал, что, возможно, LoadComponent проверит идентичность вызывающей сборки и использует ее открытый ключ и сведения о версии или, возможно, проверит идентичность сборки, которая содержит тип для параметра 'this'.

Похоже, что это не тот случай - если у вас есть две сборки с разными номерами версий (но с одним и тем же именем файла), вы можете получить IOException с сообщением «Не удается найти ресурс X» (для приведенного выше примера «Не удается найти ресурс»). просмотров / servicepanelui.xaml».

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

Итак, кто-нибудь знает, как это обойти? Как обеспечить соответствие строгого имени WPF.

Обратите внимание, насколько я понимаю, это ошибка WPF. Вам не нужно использовать изоляцию Appdomain, чтобы избежать этого.

Ответы [ 7 ]

5 голосов
/ 17 мая 2012

Вы можете установить следующее в вашем файле проекта, чтобы изменить URI в сгенерированном коде:

<PropertyGroup>
  <AssemblyVersion>1.0.0.0</AssemblyVersion>
  <AssemblyPublicKeyToken>[YOUR_PUBLIC_KEY_TOKEN]</AssemblyPublicKeyToken>
</PropertyGroup>
5 голосов
/ 14 июня 2011

Я столкнулся с такой же проблемой, и это может быть возможным решением

каждый раз, когда создается элемент управления с использованием страницы .xaml в прикрепленном конструкторе файлов .cs, перед вызовом InitializeComponent () добавьте следующие строки:

contentLoaded = true;
var assemblyName = GetType().Assembly.GetName();
System.Windows.Application.LoadComponent(GetType(), new Uri(
                string.Format("/{0};v{1};component{2}/{3}.xaml",
                assemblyName.Name,
                assemblyName.Version,
                [[[namespace]]],
                type.Name
                ), UriKind.Relative))

где [[[namespace]]] введите полное пространство имен класса, кроме пространства имен по умолчанию для проекта Visual Studio

(Примечание: при подключении есть галочка https://connect.microsoft.com/VisualStudio/feedback/details/668914/xaml-generated-code-uses-resource-uri-without-assembly-strong-name)

3 голосов
/ 21 сентября 2009

Я склонен согласиться с тем, что это, вероятно, ошибка или, по крайней мере, недостаток инструментария XAML. Возможно, вам следует сообщить об этом на Connect .

Я не пробовал, но вот несколько возможных обходных путей:

  1. Внедрить шаг перед сборкой, чтобы автоматически изменить файлы .g.cs для использования pack URI , которые задают полную информацию о сборке ( AssemblyShortName [; Version] [; PublicKey]; component / Путь )
  2. Присоединить к AppDomain.AssemblyResolve , чтобы помочь CLR найти правильную сборку
1 голос
/ 16 ноября 2016

Этот код, основанный на ответе Риккардо, работал для меня в VS2010.

Сначала я определил метод загрузчика, который я могу вызвать из моего конструктора XAML.

namespace Utility
{
    public class Utility
    {
        public static void LoadXaml(Object obj)
        {
            var type = obj.GetType();
            var assemblyName = type.Assembly.GetName();
            var uristring = string.Format("/{0};v{1};component/{2}.xaml",
                assemblyName.Name,
                assemblyName.Version,
                type.Name);
            var uri = new Uri(uristring, UriKind.Relative);
            System.Windows.Application.LoadComponent(obj, uri);
        }
    }
}

Затем в конструкторе для каждого элемента управления XAML я заменил InitializeComponent () на:

        _contentLoaded = true;
        Utility.Utility.LoadXaml(this);
        InitializeComponent();

Я заметил, что некоторые из моих привязок RelativeSource перестали работать, но я смог обойти это.

1 голос
/ 01 ноября 2014

Вы также можете передать параметр / p: AssemblyVersion = $ version в процесс msbuild, если ваши сборки автоматизированы.

1 голос
/ 02 апреля 2013

Я боролся с этим в VS2012. Я не мог заставить решение Риккардо работать в этой среде. Этот вариант его кода ...

_contentLoaded = true;
var assemblyName = GetType().Assembly.GetName();
Application.LoadComponent(this, new Uri(String.Format("/{0};v{1};component/CustomersFrame.xaml", assemblyName.Name, assemblyName.Version), UriKind.Relative));

... действительно удалось решить проблему «невозможно найти ресурс», но затем я обнаружил следующую ошибку в дочернем элементе: «Не удалось зарегистрировать именованный объект. Не удается зарегистрировать повторяющееся имя «поиск» в этой области.

Решение Аарона Мартена работает для меня. Извините, я не могу комментировать или голосовать, но у меня нет представителя.

0 голосов
/ 16 октября 2018

У нас тоже была такая же проблема, но нам пришлось установить версию сборки только для некоторых конкретных проектов в нашем решении.

Поскольку мне понравилась идея установить номер версии для сборки, как рекомендовано user195275, я провел некоторое исследование того, как сделать это для одного файла csproj.

Итак, в сочетании со следующим потоком Как прочитать версию сборки из assemblyInfo.cs? Мы нашли следующее решение:

<Target Name="BeforeBuild">
    <ReadLinesFromFile File="$(MSBuildProjectDirectory)\Properties\AssemblyInfo.cs">
        <Output TaskParameter="Lines"
                ItemName="ItemsFromFile"/>
    </ReadLinesFromFile>

    <PropertyGroup>
        <Pattern>\[assembly: AssemblyVersion\(.(\d+)\.(\d+)\.(\d+)\.(\d+)</Pattern>
        <In>@(ItemsFromFile)</In>
        <Out>$([System.Text.RegularExpressions.Regex]::Match($(In), $(Pattern)))</Out>
    </PropertyGroup>

    <CreateProperty Value="$(Out.Remove(0, 28))">
        <Output TaskParameter="Value" PropertyName="AssemblyVersion"/>
    </CreateProperty>
</Target>

что он делает: он анализирует номер версии из AssemblyInfo.cs и устанавливает его как свойство, как в ответе Аарона Мартенса. Что приводит к единственной точке обслуживания для номера версии для нас.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...