2015-01-30 2 views
1

내 응용 프로그램에는 색상 리소스가 있습니다. xaml에서 동적 리소스로 해당 색을 사용하는 요소가 하나 있습니다.WPF에서 코드 뒤에 리소스 참조를 복제하려면 어떻게해야합니까?

<Window x:Class="ResourcePlay.MainWindow" 
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
      Title="MainWindow" Height="350" Width="425"> 
    <Window.Resources> 
     <Color x:Key="MyColor">Red</Color> 
    </Window.Resources> 
    <Grid> 
     <Rectangle VerticalAlignment="Top" Width="80" Height="80" Margin="10"> 
      <Rectangle.Fill> 
       <SolidColorBrush x:Name="TopBrush" Color="{DynamicResource MyColor}"/> 
      </Rectangle.Fill> 
     </Rectangle> 
     <Rectangle VerticalAlignment="Bottom" Width="80" Height="80" Margin="10"> 
      <Rectangle.Fill> 
       <SolidColorBrush x:Name="BottomBrush"/> 
      </Rectangle.Fill> 
     </Rectangle> 
    </Grid> 
    </Window> 

코드에서이 리소스 참조를 복제하려고합니다.

using System.Windows; 
    using System.Windows.Media; 

    namespace ResourcePlay { 
    public partial class MainWindow : Window { 
     public MainWindow() { 
      InitializeComponent(); 

      // I want to copy the resource reference, not the color. 
      BottomBrush.Color = TopBrush.Color; 

      // I'd really rather do something like this. 
      var reference = TopBrush.GetResourceReference(SolidColorBrush.ColorProperty); 
      BottomBrush.SetResourceReference(reference); 

      // I want this to change the colors of both elements 
      Resources["MyColor"] = Colors.Green; 
     } 
    } 
    } 

그러나 SetResourceReference는 FrameworkElements 또는 FrameworkContentElements에서만 작동합니다. SolidColorBrush는 단지 Freezable입니다. 또한, 난 어떻게 코드 뒤에 리소스 참조 얻을 모르겠어요.

두 색상이 동시에 변경되도록 WPF에서이 작업을 수행 할 수 있습니까? 제 실제 응용 프로그램에서는 문제가 아주 간단하지 않으므로 xaml에 두 번째 DynamicResource를 추가 할 수는 없습니다.

+0

'SolidColorBrush'를 리소스 자체로 선언 한 다음 원하는 요소의 'Fill'속성에 사용하는 이유가 무엇인지 구체적으로 설명해주십시오. –

+0

@PeterDuniho 여기 SolidColorBrush는 내 응용 프로그램에서 사용하는 사용자 정의 Freezable 서브 클래스의 프록시입니다. 실제 개체는 종속성 주입을 위해 매개 변수화되어 있으므로 xaml에서 개체를 만들 수 없습니다. –

+0

내 정확한 질문이기 때문에 왜 누군가가 문제를 downvote 모르겠다. 이 스레드는 나를위한 해결책을 제공했습니다. – TravisWhidden

답변

3

Il Vic은 리플렉션을 사용하여 제안했습니다. 이것으로 확장하여 DependencyObject에 대한 확장 메서드를 만들어서 원하는대로 할 수있었습니다. 나는 코드에서 리플렉션을 사용하는 것을 정말 좋아하지 않으며, 다른 누군가가 이것을 구현하는 더 좋은 방법을 안다면, 나는 그것을보고 싶다. 적어도 이것은 코드에서 DynamicResources를 디버깅 할 때마다 도움이 될 것입니다.

public static class DependencyObjectExtensions 
    { 
    public static object GetDynamicResourceKey(this DependencyObject obj, DependencyProperty prop) 
    { 
     // get the value entry from the depencency object for the specified dependency property 
     var dependencyObject = typeof(DependencyObject); 
     var dependencyObject_LookupEntry = dependencyObject.GetMethod("LookupEntry", BindingFlags.NonPublic | BindingFlags.Instance); 
     var entryIndex = dependencyObject_LookupEntry.Invoke(obj, new object[] { prop.GlobalIndex }); 
     var effectiveValueEntry_GetValueEntry = dependencyObject.GetMethod("GetValueEntry", BindingFlags.NonPublic | BindingFlags.Instance); 
     var valueEntry = effectiveValueEntry_GetValueEntry.Invoke(obj, new object[] { entryIndex, prop, null, 0x10 }); 

     // look inside the value entry to find the ModifiedValue object 
     var effectiveValueEntry = valueEntry.GetType(); 
     var effectiveValueEntry_Value = effectiveValueEntry.GetProperty("Value", BindingFlags.Instance | BindingFlags.NonPublic); 
     var effectiveValueEntry_Value_Getter = effectiveValueEntry_Value.GetGetMethod(nonPublic: true); 
     var rawEntry = effectiveValueEntry_Value_Getter.Invoke(valueEntry, new object[0]); 

     // look inside the ModifiedValue object to find the ResourceReference 
     var modifiedValue = rawEntry.GetType(); 
     var modifiedValue_BaseValue = modifiedValue.GetProperty("BaseValue", BindingFlags.Instance | BindingFlags.NonPublic); 
     var modifiedValue_BaseValue_Getter = modifiedValue_BaseValue.GetGetMethod(nonPublic: true); 
     var resourceReferenceValue = modifiedValue_BaseValue_Getter.Invoke(rawEntry, new object[0]); 

     // check the ResourceReference for the original ResourceKey 
     var resourceReference = resourceReferenceValue.GetType(); 
     var resourceReference_resourceKey = resourceReference.GetField("_resourceKey", BindingFlags.NonPublic | BindingFlags.Instance); 
     var resourceKey = resourceReference_resourceKey.GetValue(resourceReferenceValue); 

     return resourceKey; 
    } 

    public static void SetDynamicResourceKey(this DependencyObject obj, DependencyProperty prop, object resourceKey) 
    { 
     var dynamicResource = new DynamicResourceExtension(resourceKey); 
     var resourceReferenceExpression = dynamicResource.ProvideValue(null); 
     obj.SetValue(prop, resourceReferenceExpression); 
    } 
    } 

번째 방법은 액티베이터 일부 불결함 않도록 DynamicResourceExtension를 사용하지만, 첫 번째 방법은 엄청나게 취성 느낀다. 다음과 같이

나는 내 원래의 예에서이 방법을 사용할 수 있습니다

public MainWindow() { 
    InitializeComponent(); 

    var key = TopBrush.GetDynamicResourceKey(SolidColorBrush.ColorProperty); 
    BottomBrush.SetDynamicResourceKey(SolidColorBrush.ColorProperty, key); 

    Resources["MyColor"] = Colors.Green; 
    } 

이 어떤 DependencyProperty에 대한 작동은, 그것이 DynamicResource로 설정되어 제공 우리는 자원 키를 얻을 때. 프로덕션 코드에는 약간의 기교가 필요할 것입니다.

+0

나는 내 코드에서도 거대한 반영을 사용하는 것을 좋아하지 않지만, 표준 어셈블리에서 매우 잘 파고 탐구했다. 잘 했어! –

+0

이것은 나를 위해 챔피언처럼 일했습니다 .net 4.6 - 나중에 슬픔이 생길지 모르지만, 내가해야 할 일을 정확히 수행했습니다. 고맙습니다. – TravisWhidden

2

실제로 ResourceReferenceExpression이라는 내부 개체가 필요합니다. DynamicResourceExtention에서 사용됩니다.

이 코드를 사용할 수 있습니다 :

public MainWindow() 
{ 
    InitializeComponent(); 

    BottomBrush.SetValue(SolidColorBrush.ColorProperty, 
     Activator.CreateInstance(Type.GetType("System.Windows.ResourceReferenceExpression, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"), "MyColor")); 

    Resources["MyColor"] = Colors.Green; 
} 

신중하게 그것을 사용! ResourceReferenceExpression이 내부에 있기 때문에 어쩌면 그 이유가있을 것입니다 (아마도 그 객체를 잘못 사용하면 메모리 누수가 발생할 수 있습니다).

+0

내가 찾는 것은 _ 가장 _ 있습니다. 'TopBrush'가 사용하고있는'ResourceReferenceExpression'을 얻는 방법이 있습니까? 그리고 맨 아래에 같은 표현 (또는 사본)을 사용 하시겠습니까? 차라리 두 번째 브러시의 "MyColor"를 하드 코딩 할 필요가 없습니다. 첫 번째 브러시가 사용하는 것이 무엇이든간에 사용하고 싶습니다. –

+0

'ResourceReferenceExpression'을 얻을 방법이 없다고 가정합니다. 우리는 내부 객체에 대한 것이지 공용 객체에 관한 것이 아닙니다. 문제에 대한 접근 방식을 변경해야 할 것입니다. 어쩌면 자원으로 'SolidColorBrush'를 넣을 수도 있습니다 (Peter가 제안한 것처럼). 또는 코드에서가 아니라 XAML에서 브러시 색상을 모두 설정할 수도 있습니다. –

관련 문제