i'm learning the MVVM Pattern and have some trouble with Databindings. I understand how it works, but in my example it's using the wrong DataContext to search for the binding.
I did not found another Question that suits to my problem.
So i have this View:
<UserControl x:Class="Kenshinaro.CashRegister.UI.View.CashRegister.CashRegisterChoiceView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<ItemsControl ItemsSource="{Binding CashRegisters}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel></WrapPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</UserControl>
With following ViewModel:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Kenshinaro.CashRegister.UI.View.CashRegister;
namespace Kenshinaro.CashRegister.UI.ViewModel.CashRegister
{
public class CashRegisterChoiceViewModel : MVVM.ViewModel
{
private ObservableCollection<CashRegisterItemsControlView> _cashRegisters = new ObservableCollection<CashRegisterItemsControlView>()
{
new CashRegisterItemsControlView()
{
DataContext = new CashRegisterItemsControlViewModel()
{
View = new CashRegisterCardView()
{
DataContext = new CashRegisterCardViewModel()
{
Model = new Model.CashRegister()
{
Name = "1"
}
}
}
}
},
new CashRegisterItemsControlView()
{
DataContext = new CashRegisterItemsControlViewModel()
{
View = new CashRegisterCardView()
{
DataContext = new CashRegisterCardViewModel()
{
Model = new Model.CashRegister()
{
Name = "2"
}
}
}
}
},
new CashRegisterItemsControlView()
{
DataContext = new CashRegisterItemsControlViewModel()
{
View = new CashRegisterCardView()
{
DataContext = new CashRegisterCardViewModel()
{
Model = new Model.CashRegister()
{
Name = "3"
}
}
}
}
}
};
public ObservableCollection<CashRegisterItemsControlView> CashRegisters
{
get => _cashRegisters;
set => SetProperty(ref _cashRegisters, value);
}
}
}
(My Base ViewModel class is implementing the INotifyPropertyChanged interface)
So this View shows a list of this view:
<UserControl x:Class="Kenshinaro.CashRegister.UI.View.CashRegister.CashRegisterItemsControlView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Kenshinaro.CashRegister.UI.View.CashRegister"
xmlns:md="http://materialdesigninxaml.net/winfx/xaml/themes"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<md:Card Margin="10">
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<local:CashRegisterCardView Padding="5"/>
<Button Margin="20" Grid.Row="1" Content="Pick me"/>
</Grid>
</md:Card>
</UserControl>
ViewModel:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Kenshinaro.CashRegister.UI.ViewModel.CashRegister
{
public class CashRegisterItemsControlViewModel : MVVM.ViewModel
{
private View.CashRegister.CashRegisterCardView _view;
public View.CashRegister.CashRegisterCardView View
{
get => _view;
set => SetProperty(ref _view, value);
}
}
}
And this View shows also this view:
<UserControl x:Class="Kenshinaro.CashRegister.UI.View.CashRegister.CashRegisterCardView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Kenshinaro.CashRegister.UI.View.CashRegister"
xmlns:md="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" Background="White" >
<Grid>
<TextBlock Text="{Binding DataContext.Name, RelativeSource={RelativeSource AncestorType=local:CashRegisterCardView}}"/>
</Grid>
</UserControl>
ViewModel:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Kenshinaro.CashRegister.Model;
namespace Kenshinaro.CashRegister.UI.ViewModel.CashRegister
{
public class CashRegisterCardViewModel : MVVM.ViewModel
{
private Model.CashRegister _model;
public Model.CashRegister Model
{
get => _model;
set => SetProperty(ref _model, value);
}
public String Name
{
get => _model.Name;
set => _model.Name = value;
}
}
}
Now if I start the App the Binding on the TextBlock in the last view doesn't appear. The Output of VisualStudio says:
System.Windows.Data Error: 40 : BindingExpression path error: 'Name' property not found on 'object' ''CashRegisterItemsControlViewModel' (HashCode=35528341)'. BindingExpression:Path=DataContext.Name; DataItem='CashRegisterCardView' (Name=''); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')
As I can get out of it, it is searching for the Property in the DataContext of CashRegisterItemsControlView but I just don't understand why, because i set the DataContext manually on collection initialization (Just for testing):
private ObservableCollection<CashRegisterItemsControlView> _cashRegisters = new ObservableCollection<CashRegisterItemsControlView>()
{
new CashRegisterItemsControlView()
{
DataContext = new CashRegisterItemsControlViewModel()
{
View = new CashRegisterCardView()
{
DataContext = new CashRegisterCardViewModel()
{
Model = new Model.CashRegister()
{
Name = "1"
}
}
}
}
},
...
So why it's still taking the wrong DataContext?
Now when i change the Binding to View.DataContext.Name it's searching in the manually set DataContext:
System.Windows.Data Error: 40 : BindingExpression path error: 'View' property not found on 'object' ''CashRegisterCardView' (Name='')'. BindingExpression:Path=View.DataContext.Name; DataItem='CashRegisterCardView' (Name=''); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')
I'm really confused.. I hope you can help me.
Thanks!
If you need information about the other classes just ask. I don't want to put more code into the question as it just long enough.