简体   繁体   中英

WPF pack:/[assemblyName];component/… vs pack://application:,,,/[assemblyName];component/…?

I came across this oddity while trying to resolve issues using merged ResourceDictionaries in a WPF app I'm working on.

I have custom controls (TextButton, MenuButton) and resources (colors, brushes, control styles and custom control templates) defined in an external DLL ("common"). In another library I have a user control that uses these styles ("pluginA").

As long as I was working with the standard WPF controls (TextBlock, Button, Grid etc.) - I could apply the styles from the "common" dll without any problems. The designer would pick up the style and apply it correctly.

If I plop in one of the custom controls (TextButton) into the User Control in "pluginA" - the designer would find the custom control , but couldn't resolve the type for the style to be applied (Type reference cannot find the type named '{clr-namespace:Common}TextButton').

The xmlns declaration in my usercontrol looks like this:

<UserControl x:Class="PluginA.Views.LeftBarView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:core="clr-namespace:Common.Core;assembly=Common"
             xmlns:common="clr-namespace:Common;assembly=Common"
             mc:Ignorable="d" 
             d:DesignHeight="600" d:DesignWidth="300">
<UserControl.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <core:SharedResourceDictionary Source="/Common;component/Resources/DefaultTheme/DefaultTheme.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</UserControl.Resources>

With this definition, the designer doesn't apply any styles - but it works in runtime. Great, but not quite that helpful as I don't want to run the application to see if a minor tweak took effect.

So I tried this:

<core:SharedResourceDictionary Source="pack://application:,,,/Common;component/Resources/DefaultTheme/DefaultTheme.xaml" />

But that didn't change anything (designer still wouldn't find the resources). In the process of changing the code, I got to this:

<core:SharedResourceDictionary Source="pack:/Common;component/Resources/DefaultTheme/DefaultTheme.xaml" />

Now the designer is happy and can find the resources - runtime is happy and displays the resources, and yet I can't find any description of this being a valid PACK URI... Can anyone explain why this would work?

pack:/Common;component/Resources/DefaultTheme/DefaultTheme.xaml

This is technically a valid URI, but it is not a valid pack URI. Parsing it according to the rules of the pack format would yield:

Package URI: <empty>
Part URI: /Common;component/Resources/DefaultTheme/DefaultTheme.xaml

In effect, you have made an absolute URI out of a part URI by appending the pack: scheme. However, without a well-formed package component, the result is not a valid pack URI. And, interestingly, the Uri class will not actually parse the original string as an absolute URI; it is parsed incorrectly as a relative URI, and that is part of the reason it works when assigned to ResourceDictionary.Source . Let's take a look at the property setter:

public Uri Source
{
    get { return _source; }
    set
    {
        // ...
        _source = value;

        Clear();

        Uri uri = BindUriHelper.GetResolvedUri(_baseUri, _source);
        WebRequest request = WpfWebRequestHelper.CreateRequest(uri);
        // ...
}

The key lies within BindUriHelper.GetResolvedUri(_baseUri, _source) . The logic there, which differs from much of the pack URI handling in WPF, sees that _source is not an absolute URI (at least according to the broken Uri class), so it attempts to combine it with the resolved base URI, which we presume to be pack://application:,,,/ . The URIs are combined via new Uri(Uri baseUri, Uri relativeUri) , which works only because Uri incorrectly parsed the original string as a relative URI. The URI which ultimately gets used to create the WebRequest is equivalent to:

new Uri(
    new Uri("pack://application:,,,/"),
    new Uri("pack:/Common;component/Resources/DefaultTheme/DefaultTheme.xaml"))

...which produces:

pack://application:,,,/Common;component/Resources/DefaultTheme/DefaultTheme.xaml

And viola, we end up loading the resources from a valid pack URI even though we gave it an invalid one.

We know that the "bad" URI works because it gets accidentally transformed into a good one. As to why that same "good" URI not work in the designer when it's used directly, that is very curious.

Perhaps you simply need to rebuild both the Common project and the project that is attempting to merge the resource dictionary. If it still fails, then it's possible your UserControl.Resources has a different BaseUri when running in the designer than it does at runtime. Let's see if we can figure out what the BaseUri is at design time. Modify your UserControl as follows:

<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             ...
             xmlns:m="clr-namespace:System.Windows.Markup;assembly=System.Xaml"
             x:Name="Root">
  <UserControl.Resources>
    <ResourceDictionary">
      <Style x:Key="BaseUriTextStyle" TargetType="TextBlock">
        <Setter Property="Text"
                Value="{Binding ElementName=Root,
                                Path=Resources.(m:IUriContext.BaseUri)}" />
      </Style>
    </ResourceDictionary>
  </UserControl.Resources>

  <TextBlock Style="{StaticResource BaseUriTextStyle}" />
</UserControl>

See what gets displayed in the designer. It may give us a clue.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM