Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WinRT information: Cannot find a resource with the given key. Issues with converter reference

Tags:

c#

wpf

uwp

I get this error when starting the UWP app. It's failing to find the FirstNameToVisibilityConverter resource. I would really appreciate it if someone could identify why I get this error, or post a small working sample of a UWP app using a converter. Thanks!

XAML:

<UserControl
    x:Class="MyHelloWorld.HelloWorld"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:p="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MyHelloWorld"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300"
    d:DesignWidth="400">

<Grid>
    <Grid.Resources>
        <local:FirstNameToVisibilityConverter x:Key="FirstNameToVisibilityConverter"/>
        <p:Style TargetType="TextBox">
            <Setter Property="HorizontalAlignment" Value="Center"/>
            <Setter Property="VerticalAlignment" Value="Center"/>
        </p:Style>
    </Grid.Resources>
    <StackPanel>
        <TextBlock Foreground="Red">HI</TextBlock>
        <TextBlock Foreground="Red">THERE</TextBlock>
        <TextBox Foreground="Red" Text="{x:Bind FirstWord}"/>
        <TextBlock Foreground="Red" Text="{x:Bind SecondWord}" Visibility="{x:Bind FirstWord, Converter={StaticResource FirstNameToVisibilityConverter}}"/>
        <CheckBox Foreground="Red" Content="Click me to hide the first word" IsChecked="{x:Bind FirstWordChecked}"/>
    </StackPanel>
</Grid>

Code Behind:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

namespace MyHelloWorld
{
    public sealed partial class HelloWorld : UserControl
    {
        public string FirstWord { get; set; }
        public string SecondWord { get; set; }
        public bool? FirstWordChecked { get; set; }

        public HelloWorld()
        {
            this.InitializeComponent();
        }
    }

    public class FirstNameToVisibilityConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, string language)
        {
            if ((string)value == "Today") return Visibility.Collapsed;
            return Visibility.Visible;
        }

        public object ConvertBack(object value, Type targetType, object parameter, string language)
        {
            throw new NotImplementedException();
        }
    }  
}
like image 800
Sean Avatar asked Dec 24 '22 01:12

Sean


1 Answers

This is because you're using the compile-time binding extension {x:Bind} instead of the run-time binding extension {Binding}, combined with the fact that the XAML compiler handles the Converter property as a special case, generating an explicit LookupConverter() method for finding the converter:

public global::Windows.UI.Xaml.Data.IValueConverter LookupConverter(string key)
{
    if (this.localResources == null)
    {
        global::Windows.UI.Xaml.FrameworkElement rootElement;
        this.converterLookupRoot.TryGetTarget(out rootElement);
        this.localResources = rootElement.Resources;
        this.converterLookupRoot = null;
    }
    return (global::Windows.UI.Xaml.Data.IValueConverter) (this.localResources.ContainsKey(key) ? this.localResources[key] : global::Windows.UI.Xaml.Application.Current.Resources[key]);
}

While the normal resource lookup rules would traverse the object tree, checking each parent recursively until it found a resource of the given name, as you can see above the explicitly-generated method bypasses the ResourceDictionary behavior, and only looks in the root-level Resources (i.e. the UserControl object's Resources), or the application Resources.

So, you need to declare your converter resource in one of those locations. For example:

<UserControl
    x:Class="TestSO39734815UwpResource.UserControl1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:p="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:TestSO39734815UwpResource"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300"
    d:DesignWidth="400">

  <UserControl.Resources>
    <local:FirstNameToVisibilityConverter x:Key="FirstNameToVisibilityConverter"/>
  </UserControl.Resources>

  <Grid>
    <Grid.Resources>
      <p:Style TargetType="TextBox">
        <Setter Property="HorizontalAlignment" Value="Center"/>
        <Setter Property="VerticalAlignment" Value="Center"/>
      </p:Style>
    </Grid.Resources>
    <StackPanel>
      <TextBlock Foreground="Red">HI</TextBlock>
      <TextBlock Foreground="Red" Text="THERE"/>
      <TextBox Foreground="Red" Text="{x:Bind FirstWord}"/>
      <TextBlock Foreground="Red" Text="{x:Bind SecondWord}"
                 Visibility="{x:Bind FirstWord, Converter={StaticResource FirstNameToVisibilityConverter}}"/>
      <CheckBox Foreground="Red" Content="Click me to hide the first word" IsChecked="{x:Bind FirstWordChecked}"/>
    </StackPanel>
  </Grid>
</UserControl>

Another alternative would be to put the resource in the App.xaml file, in the <Application.Resources/> element, or of course to use the {Binding} extension instead, which does go through the normal resource lookup process.


Addendum:

Personally, I feel this is a bug in the platform. The generated code-behind is accessing the framework elements anyway, so it should not be difficult to provide an implementation that would traverse the tree just as the run-time based {Binding} would. Some degree of limitation is understandable, but an arbitrary inconsistency like this just makes it harder to transfer WPF skills to Winrt/UWP.

As such, I went ahead and opened a bug on the Connect site: x:Bind doesn't find converter resource. I'm guessing Microsoft will disagree, but at the very least if we're lucky they will a) provide some rationale for the implementation decision, and b) change the documentation so that this limitation is called out clearly.

like image 85
Peter Duniho Avatar answered Dec 28 '22 08:12

Peter Duniho