在绑定转换器中使用资源作为转换结果

发布于 2024-11-11 02:49:18 字数 1605 浏览 4 评论 0原文

当我尝试将值转换器从定义的枚举状态绑定到画笔时,我在 XAML 设计器中收到错误:

未找到“OKStatus”资源。

该应用程序运行时运行良好,但我无法在设计器中看到我的 GUI。 我的资源在 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 不会了解我定义的资源,所以我需要一种方法来访问我的资源设计期间。 这可能吗?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

记忆で 2024-11-18 02:49:18

是的,有可能,而且你的猜测是正确的。资源查找从逻辑树开始,创建新的 FrameworkElement() 并不能满足这一点。它完全断开了。

您可以做的(如果 N8 的建议不起作用,您可能必须做的)是向您的转换器提供对 UserControl 的引用,作为要调用的 FrameworkElement FindResource() 上。

N8 的建议可能行不通的原因是 Application.Current.FindResource() 可能从应用程序级资源开始,然后转到系统资源,但您要查找的资源位于 < code>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 绑定到它。我认为这太过分了。

另一种方法是将 TrueBrushFalseBrush 属性放在转换器上,并在 XAML 中分配它们,这就是我倾向于做的事情,以便我的转换器模糊且通用。 (注意:名称略有不同。)

<config:BoolToBrushConverter x:Key="Bool2Brush"
                      TrueBrush="{StaticResource OKStatusBrush}"
                      FalseBrush="{StaticResource ErrorStatusBrush}" />

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}" />
别理我 2024-11-18 02:49:18

我认为问题在于您试图从框架元素而不是可视化树中查找资源。您可以尝试以下方法吗?

Application.Current.FindResource("OKStatus") as Brush;

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;
被你宠の有点坏 2024-11-18 02:49:18

据我从 TechNet Wiki 了解到,有必要使用 MultiValue Converter 和 MultiValueBinding 来通过 UserControl 获取正确的注册转换器和正确的 FrameworkElement。

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

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

独夜无伴 2024-11-18 02:49:18

我也遇到了这个问题。我认为调用 Application.Current 是从 IValueConverter 获取资源的最佳方式,因为它们不是在每个窗口、页面或控件级别上定义的。如上所述,这需要资源至少是应用程序级别的。

但是,由于 Application.Current 引用在设计器中设置为 null,因此此方法始终会破坏设计器。您似乎所做的事情是为设计器提供了一些显示内容,同时您也为正在运行的应用程序提供了对转换器中资源的访问权限。

对于所有遇到这个问题的人来说,你们不需要实施刘易斯实施的 Kludge;仅当您希望加载设计器表面时才这样做。它不会影响您的应用程序运行时,因为 Application.Current 调用有一些事情要做。

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.

你另情深 2024-11-18 02:49:18

实际上,我最终所做的(目前)是将 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);
}

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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文