Using resources as conversion results in a bind converter
Asked Answered
D

5

13

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?

Discalced answered 1/6, 2011 at 13:21 Comment(0)
D
-1

Actually what I ended up doing (for now) was to change from FindResource to TryFindResource, and put the statements in a try/catch block. This seems to work so far.

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

catch (ResourceReferenceKeyNotFoundException)
{
   return new SolidColorBrush(Colors.LightGray);
}
Discalced answered 8/6, 2011 at 10:48 Comment(1)
I personally went with Joel's solution above, as it seems much less computationally intensive. TryFindResource would take its toll in my application where I had to modify item colors in a huge datagrid.Uniat
L
17

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}" />
Largely answered 1/6, 2011 at 13:40 Comment(2)
Thanks Joel, your solution sounds a lot cleaner. I'm pretty new to the WPF world.Epilepsy
This (using properties) is definitely a nicer solution.Autopilot
E
6

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;
Epilepsy answered 1/6, 2011 at 13:33 Comment(2)
let me know if I am off base.Epilepsy
That would definitely work if the resources were in the application-level dictionary or the system resources.Largely
C
3

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

Canula answered 27/8, 2017 at 16:46 Comment(0)
S
0

I have run into this problem as well. I think that calling Application.Current is the best way to get at resources from an IValueConverter, since they are not defined on a per window, or page or control, level. This would require the resources to be at least application-level, as stated above.

However, since the Application.Current reference is set to null in the designer, this method will always break the designer. What you appear to have done is given something for the designer to display, while you have given your running application access to resources in the converter.

For all of you out there with this issue, you don't need to implement the Kludge that lewi implemented; this is only if you want the designer surface to load. It does not affect your application while running as the Application.Current call has something to do.

Starflower answered 4/6, 2012 at 19:18 Comment(0)
D
-1

Actually what I ended up doing (for now) was to change from FindResource to TryFindResource, and put the statements in a try/catch block. This seems to work so far.

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

catch (ResourceReferenceKeyNotFoundException)
{
   return new SolidColorBrush(Colors.LightGray);
}
Discalced answered 8/6, 2011 at 10:48 Comment(1)
I personally went with Joel's solution above, as it seems much less computationally intensive. TryFindResource would take its toll in my application where I had to modify item colors in a huge datagrid.Uniat

© 2022 - 2024 — McMap. All rights reserved.