当WPF资源定义在单独的项目中时,避免Visual Studio设计器错误

34

如何避免在WPF资源在单独项目中定义时,出现Visual Studio设计器错误?

我有一个复合的WPF应用程序,包含三个项目:主应用程序、一个“基础设施”库和一个“模块”库。主应用程序通过它们的输出DLL引用其他项目(这些项目不在同一个解决方案中)。

我正在“基础设施”库中定义一个皮肤(一些刷子和样式在ResourceDictionary中),我希望主应用程序选择一个皮肤,并使其对整个应用程序可用(通过App.xaml中的MergedDictionaries)。

在我的模块中,我想使用主应用程序加载的皮肤中定义的资源。如果我像本地可用资源一样引用资源,例如:

Background={StaticResource MainBackgroundBrush}

几乎所有的东西都按照预期工作。唯一的例外是Visual Studio的设计器会被搞混并告诉我“找不到'StaticResource引用'MainBackgroundBrush'”,这有效地阻止了我使用设计器。

我该怎么做才能在项目中定义一个“皮肤”ResourceDictionary,引用该皮肤到主应用程序中,然后在模块项目中使用其资源?


2
虽然这并没有从技术上回答你的问题,但是在我的经验中,Expression Blend 2在这些特定情况下不会出现双重间接查找的问题,并且渲染效果良好。 - scwagner
在VS2010 Beta 1中,如果找不到资源,您仍然可以使用设计器。它会给出警告,然后似乎会忽略该属性。 - sourcenouveau
3个回答

5
你可以创建自己的ResourceDictionary类,继承自ResourceDictionary。然后,在设计时,您可以安排此自定义ResourceDictionary加载一些明确定义的样式(即那些在运行时从应用程序加载的样式),而在运行时不执行任何操作。为此,可以评估IsInDesignMode属性。
比如你有这样一个类,叫做'DesignTimeResourceDictionary',然后你只需使用类似以下代码:
 <UserControl.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <Util:DesignTimeResourceDictionary Source="SomeUriToYourResources"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
 </UserControl.Resources>

在设计时加载资源,使设计者可以正常工作。运行时,您可以使DesignTimeResourceDictionary跳过资源加载并实现所需的行为。

如果需要,您可以创建真实资源的副本,或者只需创建包含所有必需键以使设计者正常工作的虚拟字典。


3
一种可能的解决方案是使用DynamicResource而不是StaticResource。Visual Studio 2008设计师仅显示控件而没有任何样式,就像VS2010 beta 1在无法解析StaticResource时所做的那样。
在某些情况下,使用DynamicResource是合适的,例如当特定样式在运行时可能会更改时,比如换肤。
我找到了一些相关问题支持这一点: 我还发现有人声称只要资源不是本地的,就应该使用DynamicResource

2
我只想扩展Simon D.的答案。他提出的解决方案正是我现在正在使用的。我只想分享完整的源代码。这是来自仅在设计模式时使用ResourceDictionary的技巧源。
public class DesignTimeResourceDictionary : ResourceDictionary
{
    /// <summary>
    /// Local field storing info about designtime source.
    /// </summary>
    private string designTimeSource;

    /// <summary>
    /// Gets or sets the design time source.
    /// </summary>
    /// <value>
    /// The design time source.
    /// </value>
    public string DesignTimeSource
    {
        get
        {
            return this.designTimeSource;
        }

        set
        {
            this.designTimeSource = value;
            if ((bool)DesignerProperties.IsInDesignModeProperty.GetMetadata(typeof(DependencyObject)).DefaultValue)
            {
                base.Source = new Uri(designTimeSource);
            }
        }
    }

    /// <summary>
    /// Gets or sets the uniform resource identifier (URI) to load resources from.
    /// </summary>
    /// <returns>The source location of an external resource dictionary. </returns>
    public new Uri Source
    {
        get
        {
            throw new Exception("Use DesignTimeSource instead Source!");
        }

        set
        {
            throw new Exception("Use DesignTimeSource instead Source!");
        }
    }
}

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation Jump "
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml Jump "
    Title="MainWindow" Height="350" Width="525"
    xmlns:local="clr-namespace:WpfApplication1">

  <Window.Resources>
    <local:DesignTimeResourceDictionary DesignTimeSource="pack://application:,,,/BlueColors.xaml"/>
  </Window.Resources>

    <Grid>
      <Button Background="{DynamicResource defaultBackground}"
      HorizontalAlignment="Center" VerticalAlignment="Center">click me</Button>
    </Grid>
</Window>

1
对我来说解决得(几乎)完美。唯一的问题是它在窗口元素的样式上引发了一个问题,但我认为这是设计师使用“WindowInstance”类而不是实际的“Window”所导致的问题。至少现在我可以在设计时看到我的样式! - Dutchmanjonny

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