2012-01-04 24 views
4

我已經使用了幾年的資源擴展現在停止在設計時在新的.Net 4項目中工作,出現以下錯誤:標記擴展名'StaticResourceExtension'需要'IXamlSchemaContextProvider'在IServiceProvider中爲ProvideValue實現

Markup extension 'StaticResourceExtension' requires 'IXamlSchemaContextProvider' be implemented in the IServiceProvider for ProvideValue.

從擴展相關的方法如下:

public override object ProvideValue(IServiceProvider serviceProvider) 
    { 
     Style resultStyle = new Style(); 
     foreach (string currentResourceKey in resourceKeys) 
     { 
      object key = currentResourceKey; 
      if (currentResourceKey == ".") 
      { 
       IProvideValueTarget service = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget)); 
       key = service.TargetObject.GetType(); 
      } 
      Style currentStyle = new StaticResourceExtension(key).ProvideValue(serviceProvider) as Style; 
      if (currentStyle == null) 
       throw new InvalidOperationException("Could not find style with resource key " + currentResourceKey + "."); 
      resultStyle.Merge(currentStyle); 
     } 
     return resultStyle; 
    } 

據推測,編譯器給錯誤的,因爲當我打電話currentStyle = new StaticResourceExtension(key).ProvideValue(serviceProvider);,我沿着通過的ServiceProvider缺少IXamlSchemaC ontextProvider信息。不知道它會來自不過,我甚至不知道如何爲標記擴展服務提供商首先被設定,我只是用這樣的:

<Style x:Key="ReadOnlyTextCell" TargetType="{x:Type TextBlock}" BasedOn="{util:MultiStyle ReadOnlyCell TextCell}"/>


爲後綴名的完整代碼是在這裏:

using System; 
using System.Windows; 
using System.Windows.Markup; 

/* MultiStyleExtension - used to merge multiple existing styles. 
* 
* Example: 
    <Window xmlns:local="clr-namespace:FlagstoneRe.WPF.Utilities.UI"> 
     <Window.Resources> 
      <Style x:Key="ButtonStyle" TargetType="Button"> 
       <Setter Property="Width" Value="120" /> 
       <Setter Property="Height" Value="25" /> 
       <Setter Property="FontSize" Value="12" /> 
      </Style> 
      <Style x:Key="GreenButtonStyle" TargetType="Button"> 
       <Setter Property="Foreground" Value="Green" /> 
      </Style> 
      <Style x:Key="RedButtonStyle" TargetType="Button"> 
       <Setter Property="Foreground" Value="Red" /> 
      </Style> 
      <Style x:Key="BoldButtonStyle" TargetType="Button"> 
       <Setter Property="FontWeight" Value="Bold" /> 
      </Style> 
     </Window.Resources> 

     <Button Style="{local:MultiStyle ButtonStyle GreenButtonStyle}" Content="Green Button" /> 
     <Button Style="{local:MultiStyle ButtonStyle RedButtonStyle}" Content="Red Button" /> 
     <Button Style="{local:MultiStyle ButtonStyle GreenButtonStyle BoldButtonStyle}" Content="green, bold button" /> 
     <Button Style="{local:MultiStyle ButtonStyle RedButtonStyle BoldButtonStyle}" Content="red, bold button" /> 

* Notice how the syntax is just like using multiple CSS classes. 
* The current default style for a type can be merged using the "." syntax: 

     <Button Style="{local:MultiStyle . GreenButtonStyle BoldButtonStyle}" Content="Bold Green Button" /> 

*/ 

namespace FlagstoneRe.WPF.Utilities.UI 
{ 
    [MarkupExtensionReturnType(typeof(Style))] 
    public class MultiStyleExtension : MarkupExtension 
    { 
     private string[] resourceKeys; 

     /// <summary> 
     /// Public constructor. 
     /// </summary> 
     /// <param name="inputResourceKeys">The constructor input should be a string consisting of one or more style names separated by spaces.</param> 
     public MultiStyleExtension(string inputResourceKeys) 
     { 
      if (inputResourceKeys == null) 
       throw new ArgumentNullException("inputResourceKeys"); 
      this.resourceKeys = inputResourceKeys.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); 
      if (this.resourceKeys.Length == 0) 
       throw new ArgumentException("No input resource keys specified."); 
     } 

     /// <summary> 
     /// Returns a style that merges all styles with the keys specified in the constructor. 
     /// </summary> 
     /// <param name="serviceProvider">The service provider for this markup extension.</param> 
     /// <returns>A style that merges all styles with the keys specified in the constructor.</returns> 
     public override object ProvideValue(IServiceProvider serviceProvider) 
     { 
      Style resultStyle = new Style(); 
      foreach (string currentResourceKey in resourceKeys) 
      { 
       object key = currentResourceKey; 
       if (currentResourceKey == ".") 
       { 
        IProvideValueTarget service = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget)); 
        key = service.TargetObject.GetType(); 
       } 
       Style currentStyle = new StaticResourceExtension(key).ProvideValue(serviceProvider) as Style; 
       if (currentStyle == null) 
        throw new InvalidOperationException("Could not find style with resource key " + currentResourceKey + "."); 
       resultStyle.Merge(currentStyle); 
      } 
      return resultStyle; 
     } 
    } 

    public static class MultiStyleMethods 
    { 
     /// <summary> 
     /// Merges the two styles passed as parameters. The first style will be modified to include any 
     /// information present in the second. If there are collisions, the second style takes priority. 
     /// </summary> 
     /// <param name="style1">First style to merge, which will be modified to include information from the second one.</param> 
     /// <param name="style2">Second style to merge.</param> 
     public static void Merge(this Style style1, Style style2) 
     { 
      if(style1 == null) 
       throw new ArgumentNullException("style1"); 
      if(style2 == null) 
       throw new ArgumentNullException("style2"); 
      if(style1.TargetType.IsAssignableFrom(style2.TargetType)) 
       style1.TargetType = style2.TargetType; 
      if(style2.BasedOn != null) 
       Merge(style1, style2.BasedOn); 
      foreach(SetterBase currentSetter in style2.Setters) 
       style1.Setters.Add(currentSetter); 
      foreach(TriggerBase currentTrigger in style2.Triggers) 
       style1.Triggers.Add(currentTrigger); 
      // This code is only needed when using DynamicResources. 
      foreach(object key in style2.Resources.Keys) 
       style1.Resources[key] = style2.Resources[key]; 
     } 
    } 
} 

回答

2

我遇到了同樣的問題。一些額外的信息:

在運行時,IServiceProvider的價值型「MS.Internal.Xaml.ServiceProviderContext」的。

在Visual Studio Xaml Designer中,IServiceProvider值的類型爲「Microsoft.Expression.DesignModel.Core.InstanceBuilderOperations.InstanceBuilderServiceProvider」。

顯然,VS2010使用Expression Blend中的類來提供比VS2008更好的設計時行爲 - 但價格昂貴,因爲Expression Blend類並不具有與實際運行時系統完全相同的信息。

更多信息:我試着將我自己的類同時實現的IServiceProvider和IXamlSchemaContextProvider。一些調用被傳遞給原始的IServiceProvider,並在請求時提供虛擬(空)XamlSchemaContext。但我仍然得到同樣的錯誤。

裏面的東西WPF是封裝我的IServiceProvider與其他的IServiceProvider類型 - 但一個無法實現IXamlSchemaContextProvider。我對如何解決這個問題沒有進一步的想法。

+0

關閉這個問題是因爲我已經放棄了在設計時測試任何東西,因爲它在運行時幾乎不會看起來相同。你花了很多精力來診斷這個問題,所以你應該得到這些觀點。 – Alain 2012-08-10 18:39:32

0

這是因爲ProvideValue拋出在設計時例外,但在運行時沒有做。因此,Visual Studio設計器無法從擴展中獲取值。

問題行是

Style currentStyle = new StaticResourceExtension(key).ProvideValue(serviceProvider) as Style; 

的Visual Studio不提供這樣一個異常被拋出的ServiceProvider參數的正確值。

使用NativeApi.IsInDesignMode屬性在設計時禁用一些代碼。

+1

禁用在設計模式失敗那一行擴展的目的 - 即合併兩種樣式。如果沒有該行,則無法檢索當前樣式以與第二個樣式合併。這和我已經確定了我的問題中的問題線,所以沒有點。 – Alain 2012-03-07 13:49:51

1

我與VS2010設計了同樣的問題,爲了解決這個問題,你必須修改方法:

public override object ProvideValue(IServiceProvider serviceProvider) 
{ 
    /* if in design-mode return null */ 
    if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(new DependencyObject())) 
     return null; 

    Style resultStyle = new Style(); 
    foreach (string currentResourceKey in resourceKeys) 
    { 
     object key = currentResourceKey; 
     if (currentResourceKey == ".") 
     { 
      IProvideValueTarget service = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget)); 
      key = service.TargetObject.GetType(); 
     } 
     Style currentStyle = new StaticResourceExtension(key).ProvideValue(serviceProvider) as Style; 
     if (currentStyle == null) 
      throw new InvalidOperationException("Could not find style with resource key " + currentResourceKey + "."); 
     resultStyle.Merge(currentStyle); 
    } 
    return resultStyle; 
} 
+0

另一個答案已經建議在設計時禁用擴展。由於我的問題是擴展在設計時不起作用,禁用它只是使其無法工作的另一種方式,因此不能解決問題。我想要一個解決方案,在設計時使擴展*工作*。 – Alain 2012-11-30 17:16:05

2

而不是使用StaticResourceExtension擺脫靜態資源的風格,可以改爲拿到風格從當前應用程序的靜態資源中完成相同的事情。

更換有問題的行:

Style currentStyle = new StaticResourceExtension(key).ProvideValue(serviceProvider) as Style; 

有:

Style currentStyle = Application.Current.TryFindResource(key) as Style; 

(旁註:看到爲什麼要使用TryFindResource(key)代替Resource[key]my other answer