Мне нужно установить для свойства Content ContentPresenter значение DynamicResource, ключ которого известен во время выполнения. DynamicResource Key не является свойством зависимости, поэтому я не могу вставить туда привязку, поэтому я создал прикрепленное свойство, которое служит прокси для Content:

public static class ContentPresenterHelper {

    private static void ContentResourceKeyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {

        var element = d as ContentPresenter;
        if (element != null) {

            element.SetResourceReference(ContentPresenter.ContentProperty, e.NewValue);
        }
    }

    public static readonly DependencyProperty ContentResourceKeyProperty = DependencyProperty.RegisterAttached("ContentResourceKey",
        typeof(object),
        typeof(ContentPresenterHelper),
        new PropertyMetadata(String.Empty, ContentResourceKeyChanged));

    public static void SetContentResourceKey(ContentPresenter element, object value) {

        element.SetValue(ContentResourceKeyProperty, value);
    }

    public static object GetContentResourceKey(ContentPresenter element) {

        return element.GetValue(ContentResourceKeyProperty);
    }
}

Я использую его следующим образом:

<ContentPresenter u:ContentPresenterHelper.ContentResourceKey="{Binding SomeProp}" />

Я использовал аналогичный подход при динамическом присвоении изображения из ресурсов свойству Image Source, и это сработало.

Однако в этом конкретном случае попытка решить проблему способом, который я показал, приводит к бесконечной рекурсии:

PresentationFramework.dll!System.Windows.FrameworkElement.IsLoaded.get()    Unknown
PresentationFramework.dll!MS.Internal.FrameworkObject.IsLoaded.get()    Unknown
PresentationFramework.dll!System.Windows.BroadcastEventHelper.IsParentLoaded(System.Windows.DependencyObject d) Unknown
PresentationFramework.dll!System.Windows.FrameworkElement.IsLoaded.get()    Unknown
PresentationFramework.dll!MS.Internal.FrameworkObject.IsLoaded.get()    Unknown
PresentationFramework.dll!System.Windows.BroadcastEventHelper.IsParentLoaded(System.Windows.DependencyObject d) Unknown
PresentationFramework.dll!System.Windows.FrameworkElement.IsLoaded.get()    Unknown
PresentationFramework.dll!MS.Internal.FrameworkObject.IsLoaded.get()    Unknown
...

Почему это так? Как я могу решить эту проблему?

1
Spook 20 Янв 2014 в 11:49

1 ответ

Лучший ответ

Когда возитесь с ContentPresenter, всегда помните, что ContentPresenter.Content - особенное свойство: оно влияет на DataContext. В сочетании с привязкой данных это может производить всевозможные странные эффекты. Вообще говоря, привязка ContentPresenter.Content через DataContext ненадежна, и ее следует избегать. Попробуйте использовать ContentControl, поскольку он не соединяет свой DataContext с Content таким образом. Кроме того, вместо присоединенного свойства я бы написал конвертер, который будет искать ваш динамический ресурс по ключу и использовать его для непосредственной привязки Content, но это дело вкуса.

1
Anton Tykhyy 20 Янв 2014 в 12:07
Единственная проблема с конвертером в том, что он не имеет немедленного доступа к ресурсам. Мне пришлось бы вручную прикрепить ResourceDictionary при создании экземпляра конвертера, не так ли? Или, может быть, есть более хитрый способ предоставить конвертеру доступ к ресурсам?
 – 
Spook
20 Янв 2014 в 12:20
Это зависит от того, как оцениваются ваши ресурсы. Если ресурсы, на которые вы нацелены, относятся к области применения, вы можете обойтись без доступа к локальному контексту. Однако можно создавать преобразователи, которые имеют доступ к локальному контексту, производя от MarkupExtension. Однако с шаблонами бывает сложно. В любом случае, преобразователь или вложенное свойство - дело вкуса.
 – 
Anton Tykhyy
20 Янв 2014 в 12:25
Так или иначе; обмен ContentPresenter на ContentControl полностью решил мою проблему; спасибо за помощь, сэр :)
 – 
Spook
20 Янв 2014 в 12:35