Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using resources as conversion results in a bind converter

When I try to bind a valueconverter from a defined enum Status to brush, I get an error in my XAML designer:

'OKStatus' resource not found.

The application works fine runtime, but I'm not able to see my GUI in the designer. My resources are defined in the color.xaml file, which is read at run time. All code is within the same namespace

My 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>

and

Status

My converter:

[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;
    }
}

In this code the frameElem wont have any knowledge of the resources I have defined I guess, so I need a way to get access to my resources during design. Is this possible?

like image 472
lewi Avatar asked Jun 01 '11 13:06

lewi


3 Answers

As I have learned by TechNet Wiki, there is necessary to use MultiValue Converter and MultiValueBinding to get correct registred converter and correct FrameworkElement by the UserControl.

XAML Example:

<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>

Then the converter declaration can looks :

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)
  {
  .....
  }
}

The detail explanation is: https://social.technet.microsoft.com/wiki/contents/articles/12423.wpfhowto-pass-and-use-a-control-in-it-s-own-valueconverter-for-convertconvertback.aspx

like image 181
Václav Švára Avatar answered Nov 20 '22 00:11

Václav Švára


Yes, it is possible, and your guess is correct. Resource finding starts with the logical tree, and creating a new FrameworkElement() doesn't satisfy this. It's completely disconnected.

What you can do (and what you may have to do if N8's suggestion doesn't work), is to hand your converter a reference to the UserControl as the FrameworkElement to call FindResource() on.

The reason N8's suggestion probably won't work is that Application.Current.FindResource() probably starts at application-level resources and then goes to system resources, but the resources you're after are in the UserControl's resources. If they were placed in App.xaml's resources, it would work. However, I think Application.Current may be null at design-time.

The easiest way I can think of to do this is in your UserControl's constructor:

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

I'm pretty sure it goes before InitializeComponent(), rather than after.

Doing this in XAML would be more complicated, as you probably have to add a DependencyProperty to your converter so that you could bind the UserControl to it. I think that would be going overboard.

Another way is to put TrueBrush and FalseBrush properties on your converter and assign them in XAML, which is what I tend to do so that my converters are vague and general-use. (NB: Names are slightly different.)

<config:BoolToBrushConverter x:Key="Bool2Brush"
                      TrueBrush="{StaticResource OKStatusBrush}"
                      FalseBrush="{StaticResource ErrorStatusBrush}" />
like image 34
Joel B Fant Avatar answered Nov 19 '22 22:11

Joel B Fant


I think the issue is that you are trying to find the resource out of a framework element not in the visual tree. Could you try the following instead?

Application.Current.FindResource("OKStatus") as Brush;
like image 6
Nathan Tregillus Avatar answered Nov 20 '22 00:11

Nathan Tregillus