Как сопоставить тип из пространства имен CLR в XAML со сборкой, отличной от той, в которой он объявлен?



В XAML я хотел бы использовать типы из двух разных сборок, каждая со своим собственным пространством имен. Вместо того чтобы явно объявлять пространства имен в атрибуте xmlns:<xml-namespace>="<clr-namespace>", я хотел бы использовать атрибут сборки [XmlnsDefinition] для сопоставления URI с пространствами имен для этих типов.



Одна из сборок не имеет отношения к WPF per se, поэтому я хотел бы избежать какой-либо ссылки на сборки, связанные с WPF, и в частности на сборку System.Xaml.dll, которая потребуется, если эта сборка используется атрибут [XmlnsDefinition].



У меня есть решение Visual Studio, которое организовано следующим образом:




Gu.Units.sln
Gu.Units.csproj // no ref to System.Xaml here
Gu.Units.Wpf.csproj // references Gu.Units and System.Xaml


В Gu.Units.Wpf.csproj у меня есть такое отображение:



[assembly: XmlnsDefinition("http://Gu.com/Units", clrNamespace: "Gu.Units", AssemblyName = "Gu.Units")]
[assembly: XmlnsDefinition("http://Gu.com/Units", clrNamespace: "Gu.Units.Wpf", AssemblyName = "Gu.Units.Wpf")]
[assembly: XmlnsPrefix("http://Gu.com/Units", "units")]


Я попытался использовать его в XAML следующим образом:



<UserControl x:Class="Gu.Units.Wpf.Demo.Sample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:units="http://Gu.com/Units">
<Label Content="{x:Static units:LengthUnit.Millimetres}" />
</UserControl>


Но по какой-то причине пространство имен Gu.Units игнорируется. То есть он не входит в пространство имен XML, определяемое URI http://Gu.com/Units. Вместо этого я получаю:


Имя "LengthUnit" не существует в пространстве имен "http://Gu.com/Units".




Явное объявление пространства имен в XAML-то есть наличие xmlns:units="clr-namespace:Gu.Units;assembly=Gu.Units" - работает нормально, но я также хотел бы избежать этого.



Есть ли способ, чтобы моя сборка Gu.Units.Wpf.dll предоставляла необходимый атрибут [XmlnsDefinition] для отображения пространства имен из сборки Gu.Units.dll, так что последняя сама не нуждается в ссылке на System.Xaml.dll, и в ней вообще нет специфичного для XAML кода?

619   1  

1 ответ:

Если я правильно понимаю, ваш вопрос сводится к следующему:

Иметь xmlns:units="clr-namespace:Gu.Units;assembly=Gu.Units" прекрасно работает, но это то, чего я пытаюсь избежать.

Есть ли способ сделать это?

Ответ: Нет, нет никакого способа избежать объявлениянекоторого префикса пространства имен XML.

  1. атрибут [XmlnsPrefix] не влияет на созданный вручную XAML. То есть он не вводит префикс xmlns в область XAML. Это просто маркер, который Средства разработки XAML могут извлекать данные таким образом, чтобы при автогенерации объявлений XAML они могли выбрать префикс xmlns для использования. Например, если вы используете конструктор VS для добавления нового объекта из библиотеки ссылок, он может посмотреть в этой библиотеке, чтобы узнать, что при добавлении объекта в XAML ему нужно добавить соответствующий атрибут xmlns: к внешнему элементу контейнера и использовать указанный префикс в XAML, где был добавлен объект.

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

  3. , который оставляет атрибут [XmlnsDefinition], если он указан в сборке, на которую ссылается сборка, содержащая редактируемый в данный момент XAML. В этом случае атрибут полезен, но все же не позволяет отказаться от объявления атрибута xmlns:. Вместо этого он позволяет сопоставить URI (например, http://Gu.com/Units) с одним или несколькими пространствами имен CLR. Таким образом, одно объявление атрибута префикса xmlns: может a) ссылаться на более чем одно пространство имен среды CLR, и b) делать это без того, чтобы авторский XAML должен был на самом деле назвать какое-либо пространство имен среды CLR конкретно (т. е. он инкапсулирует аспект управляемого кода ссылки на сборку, скрывая это за a УРИ).

    Таким образом, вместо записи xmlns:units="clr-namespace:Gu.Units;assembly=Gu.Units" можно записать xmlns:units="http://Gu.com/Units", что позволит префиксу units xmlns квалифицировать любой тип из любого пространства имен CLR, которое было присоединено к URI http://Gu.com/Units через атрибут [XmlnsDefinition].

    Но вы все равно должны объявить префикс пространства имен XML. Просто декларация принимает иную форму, чем это было бы необходимо в противном случае.


Примечание: когда несколько сборок используют атрибут [XmlnsDefinition] для объявления CLR пространства имен в том же URI, что и друг у друга, все пространства имен ссылаются на этот URI в XAML, который ссылается на все эти сборки. Вы можете воспользоваться этим, чтобы объединить пространства имен вашей собственной библиотеки с пространствами URI, на которые, как вы ожидаете, уже будут ссылаться в XAML (например, http://schemas.microsoft.com/winfx/2006/xaml/presentation).

До тех пор, пока этот URI фактически используется в атрибуте xmlns:, создаваемом в XAML инструментом разработки, это "решает" проблему, о которой вы спрашиваете. Но слияние вашего собственного собрания пространства имен с уже существующими из фреймворка-это хак и неразумно. Даже если нет конфликтов имен типов, это все равно плохая практика, и, конечно, если есть конфликты имен типов, это может вызвать серьезные проблемы.


редактировать:

Согласно вашему комментарию:

Проблема, которую я пытаюсь решить, состоит в том, чтобы присоединиться к ГУ.Единицы измерения для http://Gu.com/Units без добавления ссылки на систему.Xaml для Gu.Единицы измерения

От документация :

Примените один или несколько атрибутов XmlnsDefinitionAttribute к сборкам, чтобы определить типывнутри сборки для использования XAML. [Курсив мой]

То есть вы можете использовать [XmlnsDefinition] только для отображения типов из данного пространства имен, которые фактически объявлены в той же сборке, где указан сам атрибут.

Атрибут включает в себя свойство AssemblyName, которое , по-видимому, указывает на вас может включать типы из других сборок. Это естественное чтение документации и, вероятно, было намерением использования атрибута. К сожалению, платформа не контролирует, как другой код использует этот атрибут, и средства XAML фактически игнорируют его.

Атрибут [XmlnsDefinition] можно использовать только для сопоставления URI с пространствами имен, объявленными в сборке, в которой этот атрибут найден. можно указать и другие имена сборок, но конструктор XAML и компилятор в Visual Studio не обращает внимания на свойство AssemblyName.

Команда WPFпризнала, что это ошибка , но заявила, что они не будут устранять проблему.


Можно обойти проблему по типу за типом. Наиболее очевидным подходом является объявление нового типа в сборке, где указан [XmlnsDefinition], наследуя нужный тип от другой сборки. Например, в сборке Gu.Units.Wpf можно объявить такой тип, как это:

public class LengthUnits : Gu.Units.LengthUnits { }

Тогда вы будете использовать тип Gu.Units.Wpf.LengthUnits вместо Gu.Units.LengthUnits. Очевидно, что это будет работать только в том случае, если тип является незапечатанным ссылочным типом. Кроме того, в зависимости от того, как на самом деле используется тип, вы можете столкнуться с проблемами, когда код пытается использовать экземпляр Gu.Units.LengthUnits, где требуется экземпляр Gu.Units.Wpf.LengthUnits. Для простых сценариев односторонней привязки это, вероятно, не возникнет, но я легко могу представить себе другие сценарии, где это произойдет.

Меньше очевидный способ обойти проблему-использовать атрибут [TypeForwardedTo]. Например, в сборку Gu.Units.Wpf можно включить следующее:

[assembly: TypeForwardedTo(typeof(Gu.Units.LengthUnits))]
Это имеет то преимущество, что рассматриваемый тип будет тем же самым типом. Однако это эффект среды выполнения и не очень хорошо работает с инструментами конструктора XAML. Ваш проект будет построен и запущен правильно, но дизайнер все равно будет жаловаться, что "тип 'units:LengthUnits' не найден".

Я не знаю ни одного это позволит решить проблему более широко, на основе пространства имен.

Comments

    Ничего не найдено.