11

x:Reference can not be resolved after I re-arrange elements in XAML.

Here I present a working code. Just move the DataGrid element so it comes after the button element and the bindings for the MenuItem in ContextMenu and MultiBinding in Button.IsEnabled become broken. In Button.IsEnabled only MultiBinding is broken. It can be replaced with commented block and x:Reference works in that single binding.

Both throw XamlParseException.

  • MenuItem gives System.Xaml.XamlObjectWriterException and message talks about unresolved reference.
  • MultiBinding gives System.Collections.Generic.KeyNotFoundException as inner exception.

So when is that x:Reference actually resolved and why does only some bindings break when referenced element comes after the element that references it?

Here is my XAML:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:xxx="clr-namespace:WpfApplication1"
        Title="MainWindow" SizeToContent="WidthAndHeight">
    <Window.Resources>
        <xxx:BoolToVisibleConverter x:Key="boolToVisibleConv"></xxx:BoolToVisibleConverter>
        <xxx:NullToFalseConverter x:Key="nullToFalseConv"></xxx:NullToFalseConverter>
        <xxx:NullsOrToFalseConverter x:Key="nullsOrToFalseConv"></xxx:NullsOrToFalseConverter>
        <ContextMenu x:Key="MyMenu">
            <MenuItem 
                Header="Menuitem enabled when row selected" 
                IsEnabled="{Binding 
                    Path=SelectedItem, 
                    Source={x:Reference dataGridElement}, 
                    Converter={StaticResource nullToFalseConv}}" />
        </ContextMenu>
    </Window.Resources>
    <StackPanel>
        <DataGrid 
            Name="dataGridElement" 
            IsReadOnly="True" />
        <Button 
            Content="Button" 
            ContextMenu="{StaticResource MyMenu}" 
            Visibility="{Binding 
                Path=IsReadOnly, 
                Source={x:Reference dataGridElement},
                Converter={StaticResource boolToVisibleConv}}">
            <Button.IsEnabled>
                <!--<Binding 
                    Path="SelectedItem" 
                    Source="{x:Reference dataGridElement}" 
                    Converter="{StaticResource nullToFalseConv}"/>-->
                <MultiBinding 
                    Converter="{StaticResource nullsOrToFalseConv}">
                    <Binding 
                        Path="SelectedItem" 
                        Source="{x:Reference dataGridElement}"/>
                    <Binding 
                        Path="SelectedItem" 
                        Source="{x:Reference dataGridElement}"/>
                </MultiBinding>
            </Button.IsEnabled>
        </Button>
    </StackPanel>
</Window>

Here is my Code behind (without usings):

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
    public class BoolToVisibleConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value == null || (bool)value == false)
                return System.Windows.Visibility.Hidden;
            else
                return System.Windows.Visibility.Visible;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
    public class NullsOrToFalseConverter : IMultiValueConverter
    {
        public object Convert(object[] value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            foreach (object val in value)
            {
                if (val == null)
                    return false;
            }
            return true;
        }

        public object[] ConvertBack(object value, Type[] targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
    public class NullToFalseConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return (value != null);
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}
user2032138
  • 111
  • 1
  • 3

3 Answers3

2

I suppose it is because your resources(Window.Resources) will be created first, before referenced instance exists. I would try to solve this through DataContext (ViewModel).

<Window.DataContext>
        <yourNameSpace:YourViewModel x:Name="VieModName" />
    </Window.DataContext>
<MenuItem Header="HeadrTxt" Command="{Binding CommandInViewModelCmd}" DataContext="{x:Reference Name=VieModName}" />
Rekshino
  • 6,954
  • 2
  • 19
  • 44
0

Excerpted from MSDN(http://msdn.microsoft.com/en-us/library/ee795380.aspx).

x:Reference is a construct defined in XAML 2009. In WPF, you can use XAML 2009 features, but only for XAML that is not WPF markup-compiled. Markup-compiled XAML and the BAML form of XAML do not currently support the XAML 2009 language keywords and features.

StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
shangkeyun
  • 231
  • 2
  • 3
-1

x:Reference must be avoided in WPF. Because this markup extension is a recent addition to the XAML language (2009). And it is not completely supported in WPF. Use ElementName in your Binding instead of x:Reference.

<Binding Path="SelectedItem" 
         ElementName="dataGridElement"/>

On MSDN.

Cédric Bignon
  • 12,892
  • 3
  • 39
  • 51
  • 1
    The problem is that sometimes ElementName doesn't find the element. And that is the original reason I used x:Reference in the first place. As I understand x:Reference is kind of a workaround for ElementName when it doesn't work. What about the case where neither of them work? For example in this case using ElementName works for the MultiBinding in Button.IsEnabled but does not work for enabling the context menu item. – user2032138 Feb 01 '13 at 11:43
  • @user2032138 It is explained in the MSDN article, "but only for XAML that is not WPF markup-compiled." – Cédric Bignon Feb 01 '13 at 11:50
  • 1
    So in this situation when XAML describes the main UI of the application use of x:Reference is not supported? – user2032138 Feb 01 '13 at 12:36
  • @user2032138 I've never used it. But it in most cases, it is not useful and can be replaced by _ElementName_. – Cédric Bignon Feb 01 '13 at 12:49
  • 3
    @Cédric Bignon, there are specific circumstances where _ElementName_ does not work, and _{x:Reference}_ is an excellent workaround for .NET 4 or above. Specifically, where trying to bind from within a _DataGridTemplateColumn_, _ElementName_ fails due to an issue in the WPF tree, although in my experience it only affects Windows XP. See [this stackoverflow question](http://stackoverflow.com/questions/19244111/what-is-the-difference-between-xreference-and-elementname) and also [this one](http://stackoverflow.com/questions/5834336/wpf-combobox-binding-problem-in-datatemplate) – Andrew B Feb 29 '16 at 20:49