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

I ran into this weirdness while trying to solve problems using merged ResourceDictionaries in a WPF application 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 custom control that uses these styles ("pluginA").

As long as I was working with standard WPF controls (TextBlock, Button, Grid, etc.), I could apply styles from the "shared" DLL without too much trouble. The designer will pick a style and apply it correctly.

If I wrap myself in one of the custom controls (TextButton) in the User Control in "pluginA" - the designer finds the custom control, but cannot decide the type of applied style (The link type 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 does not apply any styles, but works at runtime. Great, but not quite that useful, as I don't want to run the app to check if a small tweak has entered.

So I tried this:

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

      

But that didn't change anything (the designer still couldn't find the resources). In the process of changing the code, I realized:

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

      

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

+3


source to share


1 answer


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

This is a technically valid URI, but it is not a valid pack

URI. Parsing it in accordance with the format rules pack

will give:

Package <empty>


URI: Part URI:/Common;component/Resources/DefaultTheme/DefaultTheme.xaml

In fact, you made an absolute URI from the URI part by adding a scheme pack:

. However, without a well-formed package component, the result is not a valid pack

URI. And, interestingly, the class Uri

won't actually parse the original string as an absolute URI; it is being parsed incorrectly as a relative URI and that is part of the reason it works when assigned ResourceDictionary.Source

. Let's take a look at the setter property:

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 is different from most of the URI handling pack

in WPF, sees that _source

it is not an absolute URI (at least according to the slice Uri

), so it tries to concatenate it with a resolved base URI, which we assume as pack://application:,,,/

. The URIs are concatenated through new Uri(Uri baseUri, Uri relativeUri)

, which only works because it did Uri

not parse the original string as a relative URI correctly. The URI ultimately used for creation 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 resources from a valid package URI, even if we gave it an invalid one.

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

Perhaps you just need to rebuild the project Common

and the project that is trying to merge the resource dictionary. If it still doesn't work, it is possible that yours UserControl.Resources

has different BaseUri

running time in the constructor than at runtime. Let's see if we can figure out what BaseUri

's in development time. Change 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 is displayed in the designer. This may give us a clue.

+1


source







All Articles