Использование ресурсов в качестве конверсии приводит к конвертации привязки - PullRequest
11 голосов
/ 01 июня 2011

Когда я пытаюсь привязать преобразователь значений из определенного перечисления Status к кисти, я получаю сообщение об ошибке в своем конструкторе XAML:

Ресурс 'OKStatus' не найден.

Приложение работает нормальновремя выполнения, но я не могу увидеть мой графический интерфейс в конструкторе.Мои ресурсы определены в файле color.xaml, который читается во время выполнения.Весь код находится в одном пространстве имен

Мой XAML:

xmlns: config = "clr-namespace: App.MyNamespace"

<UserControl.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="c:\Skins\Colors.xaml" />
            <ResourceDictionary Source="c:\Skins\Common.xaml" />                
        </ResourceDictionary.MergedDictionaries>
        <config:StatusConverter x:Key="StateConverter" />
        <config:BoolConverter x:Key="BoolConverter" />
        <config:BooleanConverter x:Key="BooleanConverter" />
    </ResourceDictionary>
</UserControl.Resources>

и

Статус

Мой конвертер:

[ValueConversion(typeof(bool), typeof(Brush))]
public class BoolConverter : IValueConverter
{
    public object Convert(object value, Type targetType,
        object parameter, CultureInfo culture)
    {
        bool state = (bool)value;

        FrameworkElement FrameElem = new FrameworkElement();

        if (state == true)
            return (FrameElem.FindResource("OKStatus") as Brush);
        else
            return (FrameElem.FindResource("ErrorStatus") as Brush);
    }

    public object ConvertBack(object value, Type targetType,
        object parameter, CultureInfo culture)
    {
        return null;
    }
}

В этом коде frameElem не будет иметь никаких знаний о ресурсах, которые я определил, я думаю, поэтому мне нужен способполучить доступ к моим ресурсам во время проектирования.Это возможно?

Ответы [ 5 ]

14 голосов
/ 01 июня 2011

Да, это возможно, и ваша догадка верна.Поиск ресурса начинается с логического дерева, и создание нового FrameworkElement() не удовлетворяет этому.Он полностью отключен.

Что вы можете сделать (и что вам, возможно, придется сделать, если предложение N8 не сработает), это передать конвертеру ссылку на UserControl как FrameworkElement для вызоваFindResource() вкл.

Причина, по которой предложение N8, вероятно, не сработает, заключается в том, что Application.Current.FindResource(), вероятно, начинается с ресурсов уровня приложения, а затем переходит к системным ресурсам, но ресурсы, которые вы ищете, находятся в UserControl ресурсы.Если бы они были помещены в ресурсы App.xaml, это сработало бы.Тем не менее, я думаю, что Application.Current может быть null во время разработки.

Самый простой способ, которым я могу придумать, это сделать в конструкторе UserControl:

public MyUserControl(){
    var boolconv = new BoolConverter(); 
    boolconv.FrameworkElement = this;
    this.Resources.Add( "BoolConverter", boolconv );
    InitializeComponent();
}

Я уверен, что это происходит до InitializeComponent(), а не после.

Выполнение этого в XAML будет более сложным, так как вам, вероятно, придется добавить DependencyProperty в ваш конвертер, чтобы вы моглипривязать UserControl к нему.Я думаю, что это будет за бортом.

Другой способ - добавить свойства TrueBrush и FalseBrush в ваш конвертер и назначить их в XAML, что я и делаю, чтобы мои конвертеры были расплывчатыми иобщее использование.(Примечание: имена немного отличаются.)

<config:BoolToBrushConverter x:Key="Bool2Brush"
                      TrueBrush="{StaticResource OKStatusBrush}"
                      FalseBrush="{StaticResource ErrorStatusBrush}" />
6 голосов
/ 01 июня 2011

Я думаю, проблема в том, что вы пытаетесь найти ресурс вне элемента фреймворка, а не в визуальном дереве.Не могли бы вы попробовать следующее?

Application.Current.FindResource("OKStatus") as Brush;
1 голос
/ 27 августа 2017

Как я узнал из TechNet Wiki, необходимо использовать MultiValue Converter и MultiValueBinding для получения правильного зарегистрированного преобразователя и правильного FrameworkElement с помощью UserControl.

XAML Пример:

<TextBlock x:Name="tb1" Margin="20">
 <TextBlock.Text>
  <MultiBinding Converter="{StaticResource MyConverter}">
   <Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type UserControl}}"/>
   <Binding Path="MyValue"/>
  </MultiBinding>
</TextBlock.Text>
</TextBlock>

Тогда может выглядеть объявление конвертера:

public class MyConverter : IMultiValueConverter
{
     FrameworkElement myControl;
     object theValue;

     public object Convert(object[] values, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
     {
        myControl = values[0] as FrameworkElement;
        theValue = values[1];

         return myControl.FindResource(">>resource u need<<"); 
      }

       public object[] ConvertBack(object value, System.Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
  {
  .....
  }
}

Подробное объяснение: https://social.technet.microsoft.com/wiki/contents/articles/12423.wpfhowto-pass-and-use-a-control-in-it-s-own-valueconverter-for-convertconvertback.aspx

0 голосов
/ 04 июня 2012

Я тоже столкнулся с этой проблемой. Я думаю, что вызов Application.Current - лучший способ получить ресурсы с IValueConverter, поскольку они не определены на уровне окна, страницы или элемента управления. Это потребует ресурсов как минимум на уровне приложения, как указано выше.

Однако, поскольку в конструкторе для ссылки Application.Current установлено нулевое значение, этот метод всегда нарушает конструктор. То, что вы, похоже, сделали, - это что-то, что дизайнер может отобразить, а у вашего запущенного приложения есть доступ к ресурсам конвертера.

Для всех вас, имеющих эту проблему, вам не нужно реализовывать Kludge, реализованный lewi; это только если вы хотите, чтобы дизайнерская поверхность загружалась. Это не влияет на ваше приложение во время работы, так как вызов Application.Current имеет какое-то отношение.

0 голосов
/ 08 июня 2011

На самом деле то, что я в итоге сделал (на данный момент), это изменил FindResource на TryFindResource и поместил операторы в блок try / catch. Кажется, это работает до сих пор.

try
{
    if (state == true)
       return (FrameElem.TryFindResource("OKStatus") as Brush);
    else
       return (FrameElem.TryFindResource("ErrorStatus") as Brush);
}

catch (ResourceReferenceKeyNotFoundException)
{
   return new SolidColorBrush(Colors.LightGray);
}
...