如何在设计时在棱镜模块中使用资源字典?

时间:2011-08-17 15:14:26

标签: c# silverlight mvvm prism mef

我在一个Silverlight应用程序中使用prism框架,在单独的XAP中有多个模块。

我在我的shell项目中定义了一个资源字典。在我的模块中,我可以很好地使用资源,但由于模块与shell分离,直到它们在运行时加载,设计器不会显示它们或识别它们。

有没有办法让模块在设计时知道我的资源而不在每个视图xaml中合并我的资源文件?

我的资源文件位于“常见”项目中。

4 个答案:

答案 0 :(得分:5)

我认为我肯定有设计时资源的解决方案。

<强>优点:

  1. 适用于任何基于模块的(MEF,UNITY ..)应用程序。
  2. 适用于任何设计师(Visual Studio,Blend ..)
  3. 它不会创建相同ResourceDictionary的多个实例
  4. 让我们考虑以下解决方案:

    • MyApp.Shell(.exe)
    • MyApp.Module1(.dll) - 使用MEF在运行时加载
    • MyApp.Module2(.dll) - 使用MEF在运行时加载
    • MyApp.Common(.dll) - 所有项目引用

    您可以在MyApp.Common中定义画笔,隐式样式,模板等。

    使用我的 SharedResourceDictionary 在所有项目中包含ResourceDictionary。在设计时,它将为每个设计器加载ResourceDictionary,在运行时,ResourceDictionary将仅在必要时加载。

    用法示例:

    在App.xaml中包含SharedResourceDictionary

    <Application.Resources>
      <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
          <common:SharedResourceDictionary SharedSource="MyApp.Common;component/CommonResources.xaml" />
      </ResourceDictionary>
    </Application.Resources>
    

    包括SharedResourceDictionary无处不在设计师找不到    一些共享资源,例如在MyApp.Module1 / UserControl1.xaml

    <UserControl.Resources>
      <common:SharedResourceDictionary SharedSource="MyApp.Common;component/CommonResources.xaml" />
    </UserControl.Resources>
    

    来源:

    /// <summary>
    /// Loads singleton instance of ResourceDictionary to current scope;
    /// </summary>
    public class SharedResourceDictionary : ResourceDictionary
    {
      /// <summary>
      /// store weak references to loaded ResourceDictionary, to ensure that ResourceDictionary won't be instanciated multiple times
      /// </summary>
      protected static Dictionary<string, WeakReference> SharedResources = new Dictionary<string, WeakReference>();
    
      public string SharedSource
      {
        get { return _SharedSource; }
        set
        {
          if (_SharedSource != value)
          {
            _SharedSource = value;
            sharedSourceChanged();
          }
        }
      }
      private string _SharedSource;
    
    
      private void sharedSourceChanged()
      {
        //ResourceDictionary will be instanciated only once
        ResourceDictionary sharedResourceDictionary;
    
        lock (SharedResources)
        {
          WeakReference weakResourceDictionary = null;
          if (SharedResources.ContainsKey(_SharedSource))
          {
            weakResourceDictionary = SharedResources[_SharedSource];
          }
          else
          {
            SharedResources.Add(_SharedSource, null);
          }
    
          if (weakResourceDictionary == null || !weakResourceDictionary.IsAlive) //load ResourceDictionary or get reference to exiting
          {
            sharedResourceDictionary = (ResourceDictionary)Application.LoadComponent(new Uri(_SharedSource, UriKind.Relative));
            weakResourceDictionary = new WeakReference(sharedResourceDictionary);
          }
          else
          {
            sharedResourceDictionary = (ResourceDictionary)weakResourceDictionary.Target;
          }
    
          SharedResources[_SharedSource] = weakResourceDictionary;
        }
    
    
        if (Application.Current != null)
        {
          //if sharedResourceDictionary is defined in application scope do not add it to again to current scope
          if (containsResourceDictionary(Application.Current.Resources, sharedResourceDictionary))
          {
            return;
          }
        }
    
        this.MergedDictionaries.Add(sharedResourceDictionary);
      }
    
      private bool containsResourceDictionary(ResourceDictionary scope, ResourceDictionary rs)
      {
        foreach (var subScope in scope.MergedDictionaries)
        {
          if (subScope == rs) return true;
          if (containsResourceDictionary(subScope, rs)) return true;
        }
        return false;
      }
    }
    

答案 1 :(得分:4)

我发现有几个解决方案:

1)创建模块项目时,将App.xaml保留在项目中而不是删除它,并在那里实例化资源,就好像它本身就是它自己的应用程序一样(你也可以添加一个新的Application类)该项目,如果你已经删除它)。当您的模块加载到shell中时,该文件将被忽略,因此它基本上只在设计时有效。这在视觉工作室和混合中效果很好,但如果你有很多模块,内存占用可能会成为一个问题。

2)使用设计时资源。有关在此设置此内容的一些信息:http://adamkinney.com/blog/2010/05/04/design-time-resources-in-expression-blend-4-rc/。这仅提供混合支持,您的视图将被删除视觉工作室中的所有样式和格式。这对我来说并不理想,因为我喜欢在visual studio中处理UI的某些方面。似乎也没有记录的手动设置设计时资源的方法。

答案 2 :(得分:2)

将资源从Shell迁移到共享工具并使设计师工作得很好的小型自我体验指南

基于阅读此类问题并在相同/类似问题上搜索互联网的一些想法。我写这个主要是因为问题2(下面),这与这个问题有关,恕我直言。

所以,我们有相同的设计,所有的风格和资源都在壳牌。这产生了两个问题:

  1. XAML-Editor中的上下文帮助不可用(&lt; - 资源不可用) 实测值)
  2. 设计师无法正常显示(&lt; - 资源不会 实测值)
  3. 因此我们将所有样式迁移到共享程序集(参考资料)。

    要解决第一个问题,你需要像Liero建议的那样,即为每个UserControl添加资源字典。我没有尝试他的SharedDictionary,但正常的ResourceDictionary肯定会带回上下文帮助并删除蓝色下划线。然而,设计师仍未正确显示。

    所以第二个问题。在描述in this article的设计时,有一个小技巧可以为设计师带来样式。基本上,您将名为DesignTimeResources.xaml的资源字典添加到包含对资源的引用的项目中:

    <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="pack://application:,,,/Resources;component/Themes/Generic.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    
    </ResourceDictionary>
    

    将其移至Properties文件夹。然后手动编辑项目文件并将此文件的项目更改为:

    <Page Include="Properties\DesignTimeResources.xaml" Condition="'$(DesignTime)'=='true' OR ('$(SolutionPath)'!='' AND Exists('$(SolutionPath)') AND '$(BuildingInsideVisualStudio)'!='true' AND '$(BuildingInsideExpressionBlend)'!='true')">
          <Generator>MSBuild:Compile</Generator>
          <SubType>Designer</SubType>
          <ContainsDesignTimeResources>true</ContainsDesignTimeResources>
        </Page>
    

    基本上,如果添加设计时资源,它将是Blend生成的文件。 VS无法创建它,虽然可以读得很好。项目文件的编辑说你基本上不想发布这个文件。

    这里还有两个小问题,也许它会对某人有所帮助。

    • 在将资源从Shell迁移到资源时,我们的资源项目不会构建奇怪的错误,因为它无法找到从样式文​​件引用的UserControls(所有有问题的控件也在Resources项目中定义)。之前从Shell引用它们时工作得很好。问题是某些工具(如Resharper)会自动引用名称空间中的这些控件,如“clr-namespace:XXX;assembly=Resources”。 “;assembly=Resources” - 您应该删除的部分,因为它现在是相同的程序集。
    • 我们已经在UserControls中处理了一些本地资源,如下所示:

      <UserControl.Resources>
          <PresentationHelpers:BoolToVisibilityConverter x:Key="boolToVisibilityConverter" />
      </UserControl.Resources>
      

    所以刚开始我刚刚在这个块中添加了新的ResourceDictionary,它要求我提供一个x:Key。我习惯于直接向UserControl.Resources添加资源,我没有意识到为了合并另一个字典,你需要<ResourceDictionary>标签,通常你可以跳过。所以它看起来像这样:

    <UserControl.Resources>
      <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
          <Helpers:RedbexResourceDictionary Source="pack://application:,,,/Resources;component/Themes/Generic.xaml" />
        </ResourceDictionary.MergedDictionaries>
        <PresentationHelpers:BoolToVisibilityConverter x:Key="boolToVisibilityConverter" />
      </ResourceDictionary>
    </UserControl.Resources>
    

答案 3 :(得分:1)

如果您希望为观看次数提供设计时数据,我建议您阅读this article。它显示了如何使用Blend在项目中创建设计时间数据,该数据未包含在应用程序的发布版本中。

希望它有所帮助。