3
votes

This question is related to my previous question here: Predicate won't validate parameter correctly

At first some information on my Models:

BaseViewModel:

public abstract class BaseViewModel : INotifyPropertyChanged 
{
    public event PropertyChangedEventHandler PropertyChanged;

    private string _name;
    public string Name
    {
        get 
        {
            return _name;
        }
        set 
        {
            if (Name != value) 
            {
                _name = value;
                OnPropertyChanged("Name");
            }
        }
    }

    private BaseViewModel _homePage;
    public BaseViewModel HomePage 
    {
        get 
        {
            return _homePage;
        }
        set 
        {
            if (HomePage != value) 
            {
                _homePage = value;
                OnPropertyChanged("HomePage");
            }
        }
    }

    public void OnPropertyChanged(string propertyName) 
    {
        PropertyChangedEventHandler temp = PropertyChanged;
        if (temp != null) 
        {
            temp(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Problem:

<Button Content="{Binding Name}" Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
        CommandParameter="{Binding HomePage}"/>

As you can see in the BaseViewModel, the Properties "Name" and "HomePage" are defined in the same class so they should be accessible if the DataContext is a ViewModel which derives from "BaseViewModel". At first I lost my mind because nothing seemed to work - the output window said that the value of "HomePage" could not be retrieved - other that the Name which got bound properly every time.

After I nearly gave up I named my View "Test" and tried to redirect the CommandProperty binding along an ElementName - and it worked:

<Button Content="{Binding Name}" Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
        CommandParameter="{Binding DataContext.HomePage, ElementName=Test}"/>

But why? Why differs the DataContext between the Name Property which could be bound without assigning an ElementName and HomePage which requires the ElementName?

Update 1:

MainViewModel : BaseViewModel

private RelayCommand _command;
    public RelayCommand ChangePageCommand {
        get {
            return _command ?? (_command = new RelayCommand(p => ChangeViewModel((BaseViewModel)p), x => {
                return x is BaseViewModel;
            }));
        }
    }

public void ChangeViewModel(BaseViewModel viewModel) {
        CurrentPageViewModel = viewModel;
    }

Update 2:

<Window.Resources>
    <DataTemplate DataType="{x:Type home:HomeViewModel}">
        <home:Home/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type ua:UserAdministrationViewModel}">
        <ua:UserAdministration/>
    </DataTemplate>
</Window.Resources>
<ContentControl Content="{Binding CurrentPageViewModel}"/>

Update 3:

Now it's getting really strange - I tried to add an TextBlock and bind it directly to HomePage.Text - and it works.

<Button Height="50" Content="{Binding Name}" Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
        CommandParameter="{Binding DataContext.HomePage, ElementName=Test}"/>
        <TextBlock Text="{Binding HomePage.Name}"/>

So why can't I directly access HomePage when binding to the CommandParameter but binding a TextBlock's Text Property directly to HomePage.Name works?

Update 4:

The Output Windows sais:

System.Windows.Data Information: 10 : Cannot retrieve value using the binding and no valid fallback value exists; using default instead. BindingExpression:Path=Name; DataItem=null; target element is 'Button' (Name=''); target property is 'Content' (type 'Object')

System.Windows.Data Information: 10 : Cannot retrieve value using the binding and no valid fallback value exists; using default instead. BindingExpression:Path=DataContext.ChangePageCommand; DataItem=null; target element is 'Button' (Name=''); target property is 'Command' (type 'ICommand')

System.Windows.Data Information: 10 : Cannot retrieve value using the binding and no valid fallback value exists; using default instead. BindingExpression:Path=HomePage; DataItem=null; target element is 'Button' (Name=''); target property is 'CommandParameter' (type 'Object')

System.Windows.Data Information: 10 : Cannot retrieve value using the binding and no valid fallback value exists; using default instead. BindingExpression:Path=HomePage.Name; DataItem=null; target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')

But everything except the CommandParameter gets bound successfully. Is it possible that the CommandParameter (and the underlaying CanExecute-Method) won't get re-validated once the binding is refreshed? Maybe the binding fails at first but every binding except the CommandParameter gets refreshed and revalidated. But how could I solve this issue?

1
Can you show the code where is ChangePageCommand?,fhnaseer
See the updated queston.Th1sD0t
And where you are setting the DataContext of this View,fhnaseer
The DataContex gets set automatically through the DataTemplate. For my HomeView the DataContext is the HomeViewModel and so on. As mentioned the Binding to the Name Property works perfectly (DataContext is the UserAdministrationViewModel which derives from the BaseViewModel) but the binding on the HomePage Property doesn'tTh1sD0t
Bind all properties/commands in same way and check the results, "Button Content="{Binding Name}" Command="{Binding ChangePageCommand}" CommandParameter="{Binding HomePage}"/>"fhnaseer

1 Answers

1
votes

I'm proud to announce that I've solved this silly problem:

I assume that the RelativeSource I'm using to redirect my CommandBinding to the Window's ViewModel breaks the DataContext of all following Properties (!Not Controls).

Not working:

<Button Grid.Row="0" 
            Grid.Column="0" 
            Height="50" 
            Content="{Binding PreviousPageViewModel.Name}"
            Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType=Window}}"
            CommandParameter="{Binding PreviousPageViewModel}" />

Working:

<Button Grid.Row="0" 
            Grid.Column="0" 
            Height="50" 
            Content="{Binding PreviousPageViewModel.Name}" 
            CommandParameter="{Binding PreviousPageViewModel}" 
            Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>

So in this case the order of Properties is important.

Note: The DataContext for all following Controls is correct.