Error de ruta de BindingExpression para la propiedad en ViewModel

I have the below visibility binding that is throwing:

System.Windows.Data Error: 40 : BindingExpression path error: 'Vis' property not found on 'object' ''LoginViewModel' (HashCode=22943289)'. BindingExpression:Path=Vis; DataItem='LoginViewModel' (HashCode=22943289); target element is 'Login' (Name=''); target property is 'Visibility' (type 'Visibility')

Can't see what I've done wrong, the property does exist in the MainViewModel. Maybe I'm going about showing and hiding this the wrong way.

<Window x:Class="Bt.MainWindow"
        xmlns:vm="clr-namespace:Bt"
        xmlns:ctrls="clr-namespace:Bt.Controls">

    <Window.DataContext>
        <vm:MainViewModel x:Name="MWin" />
    </Window.DataContext>

    <Grid>
        <ctrls:Login Visibility="{Binding Vis}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"></ctrls:Login>
    </Grid>
</Window>

Ver modelo:

namespace Bt
{
    class MainViewModel : INotifyPropertyChanged
    {
        public MainViewModel()
        {
            Vis = "Collapsed";
        }

        private string _vis = "Collapsed";
        public string Vis
        {
            get { return _vis; }
            set
            {
                    _vis = value;
                    RaisePropertyChanged("Vis");
            }
        }
    }
}


[EDITAR] Capturing inside the User Control, when the User Control's visibility is changed in the Main window.

I realize that the converter is not being called correctly, so may need some help there as well. As for the rest hopefully you can see what I'm trying to achieve.

Vista

<UserControl x:Class="Bt.Controls.Login" 
             xmlns:app="clr-namespace:Bt" 
             xmlns:viewmodel="clr-namespace:Bt.Controls"
             mc:Ignorable="d"
             Visibility="{Binding Visi,Converter={StaticResource BooleanToVisibilityConverter}}"
            >
</UserControl>

Ver modelo:

namespace Bt.Controls
{
    class LoginViewModel : INotifyPropertyChanged
    {
        public LoginViewModel(){}

        private bool _visi = true;
        public bool Visi
        {
            get { return _visi; }
            set
            {
                _visi = value;
                RaisePropertyChanged("Visi");
                MessageBox.Show("Visi set");
                reset_timer(_visi);
            }
        }
    }

    [ValueConversion(typeof(bool), typeof(Visibility))]
    public class VisibilityConverter : IValueConverter
    {
        public const string Invert = "Invert";

        #region IValueConverter Members

        public object Convert(object value, Type targetType, object parameter,
            System.Globalization.CultureInfo culture)
        {
            if (targetType != typeof(Visibility))
                throw new InvalidOperationException("The target must be a Visibility.");

            bool? bValue = (bool?)value;

            if (parameter != null && parameter as string == Invert)
                bValue = !bValue;

            return bValue.HasValue && bValue.Value ? Visibility.Visible : Visibility.Collapsed;
        }

        public object ConvertBack(object value, Type targetType, object parameter,
            System.Globalization.CultureInfo culture)
        {
            return ((value is Visibility) && (((Visibility)value) == Visibility.Visible));
        }
        #endregion
    }
}

preguntado el 12 de febrero de 14 a las 07:02

2 Respuestas

As evident from error BindingEngine is looking for property Vis in LoginViewModel y no en MainViewModel. (You must have set DataContext for your Login UserControl to LoginViewModel).

Tu necesitas get Window's DataContext que puedes obtener usando RelativeSource:

<ctrls:Login Visibility="{Binding DataContext.Vis,
       RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"/>

Also you should create Vis as bool y use BooleanToVisibility converter in your binding.

<Grid>
    <Grid.Resources>
       <BooleanToVisibilityConverter x:Key="BooelanToVisibilityConverter"/>
    </Grid.Resources>
    <ctrls:Login Visibility="{Binding DataContext.Vis,
                           RelativeSource={RelativeSource Mode=FindAncestor,
                                                          AncestorType=Window}}",
                         Converter={StaticResource BooelanToVisibilityConverter}
                 HorizontalAlignment="Stretch" 
                 VerticalAlignment="Stretch"/>
</Grid>

Lo haría discourage use of Visibility property in ViewModel because it's View thing which should not be there in ViewModel. Having bool is perfectly fine which you can always convert using converter.

Respondido 12 Feb 14, 07:02

just an extension to this. In the ctrls:Login ViewModel, I wanted to capture when it becomes visible, I have tried binding the UserControl's View Visibility to a property in the ViewModel, however when testing if set to Visible in the main window it doesn't get reflect by a messagebox I have placed in the property changed handler. I will add the code above for clarity. - Hank

Guardar Vis como tipo de Visibility

class MainViewModel : INotifyPropertyChanged
{
    public MainViewModel()
    {

    }

    private Visibility _vis = Visibility.Collapsed;
    public Visibility Vis
    {
        get { return _vis; }
        set
        {
            _vis = value;
            RaisePropertyChanged("Vis");
        }
    }
}

Also specify the Source for the binding,

<Window x:Class="Bt.MainWindow"
    xmlns:vm="clr-namespace:Bt"
    xmlns:ctrls="clr-namespace:Bt.Controls">

    <Window.Resources>
        <vm:MainViewModel x:Key="MWin" />           
    </Window.Resources>

    <Grid>
        <ctrls:Login Visibility="{Binding Vis, RelativeSource={StaticResource MWin}}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"></ctrls:Login>
    </Grid>
</Window>

As Rohit says, you could use BooleanToVisibility instead of changing the property as Visibility..

Respondido 12 Feb 14, 07:02

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.