'How to perform a correct binding using wpf MVVM
I'm working on a bank application in c# wpf using the MVVM pattern which allows a manager in charge of a branch to display the data of one of the customers of this branch in another tab by selecting the line corresponding to this customer. I use a custom framework. Here is my Xaml code corresponding to the main view that displays the agencies for which the Manager is responsible:
<f:UserControlBase x:Class="BankingApp.View.ManagerAgencyView"
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:vm="clr-namespace:BankingApp.ViewModel"
xmlns:vw="clr-namespace:BankingApp.View"
xmlns:f="clr-namespace:PRBD_Framework;assembly=PRBD_Framework"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=vm:ManagerAgencyViewModel,
IsDesignTimeCreatable=False}"
FontSize="14" d:DesignHeight="498" d:DesignWidth="918">
<UserControl.DataContext>
<vm:ManagerAgencyViewModel x:Name="vm"/>
</UserControl.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<TextBlock Margin="5" Text="Agency:"/>
<ComboBox
Margin="5,5,5,5"
Grid.Column="1"
ItemsSource="{Binding Path=Agencies,Mode=TwoWay}"
DisplayMemberPath="Name"
SelectedItem="{Binding SelectedAgency}"
/>
<f:MyDataGrid
Grid.Row="1"
ItemsSource="{Binding Clients}"
SelectedItem="{Binding SelectedClient}"
CanUserAddRows="False"
AutoGenerateColumns="False"
x:Name="gridAccesses" Grid.ColumnSpan="2" Margin="0,0,5,0"
>
<DataGrid.Columns>
<DataGridTextColumn Header="idClient" Width="auto" Binding="{Binding UserId}"
IsReadOnly="True" />
<DataGridTextColumn Header="FirstName" Width="*" Binding="{Binding FirstName}"
IsReadOnly="True" />
<DataGridTextColumn Header="LastName" Width="*" Binding="{Binding LastName}"
IsReadOnly="True" />
</DataGrid.Columns>
</f:MyDataGrid>
<Button Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="3" Width="80" Margin="10"
Content="New Client" Command=""/>
<f:MyTabControl Grid.Row="3" Grid.ColumnSpan="2" >
<TabItem Header="Client">
<vw:ManagerClientDataView x:Name="ClientData" DataContext="{Binding
ManagerClientData, ElementName=vm}"/>
</TabItem>
<TabItem Header="Account">
<vw:ManagerClientAccount/>
</TabItem>
</f:MyTabControl>
</Grid>
</f:UserControlBase>
When I select on one of the agencies, it shows me all the clients of this agency like this: enter image description here
here is the corresponding viewModel
using BankingApp.Model;
using PRBD_Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BankingApp.ViewModel {
public class ManagerAgencyViewModel : ViewModelCommon {
public ManagerClientDataViewModel ManagerClientData { get; private set; } = new
ManagerClientDataViewModel();
public ManagerAgencyViewModel() {
OnRefreshData();
}
public ObservableCollectionFast<Agency> Agencies { get; set; } = new();
public ObservableCollectionFast<Client> Clients { get; set; } = new();
private Client _selectedClient;
public Client SelectedClient {
get => _selectedClient;
set {
SetProperty(ref _selectedClient, value, () => ManagerClientData.SelectedClient =
value);
}
}
private Agency _selectedAgency;
public Agency SelectedAgency {
get => _selectedAgency;
set => SetProperty(ref _selectedAgency, value, () => ClientOfAgency(SelectedAgency));
}
private void ClientOfAgency(Agency agency) {
if (agency != null)
Clients.RefreshFromModel(Context.Clients.Where(c => c.Agency.AgencyId ==
agency.AgencyId));
}
protected override void OnRefreshData() {
Agencies.RefreshFromModel(Context.Agencies.OrderBy(a => a.Name));
ClientOfAgency(SelectedAgency);
}
public void init() {
}
}
}
Here is the code of my tab to display the data of the selected client:
<f:UserControlBase x:Class="BankingApp.View.ManagerClientDataView"
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:vm="clr-namespace:BankingApp.ViewModel"
xmlns:vw="clr-namespace:BankingApp.View"
xmlns:f="clr-namespace:PRBD_Framework;assembly=PRBD_Framework"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Margin="0,10,0,10">
<TextBlock Text="FirstName:"/>
<TextBox Text="{Binding Firstname}"/>
</StackPanel>
<StackPanel Grid.Row="1" Margin="0,0,0,10">
<TextBlock Text="LastName:"/>
<TextBox Text="{Binding Lastname}"/>
</StackPanel>
<StackPanel Grid.Row="2" Margin="0,0,0,10">
<TextBlock Text="Email:"/>
<TextBox Text="{Binding Email}"/>
</StackPanel>
<StackPanel Grid.Row="3" Margin="0,0,0,10">
<TextBlock Text="Password:"/>
<PasswordBox f:PasswordHelper.Password="{Binding Password, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
<StackPanel Grid.Row="4">
<TextBlock Text="Confirm Password:"/>
<PasswordBox f:PasswordHelper.Password="{Binding Password, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</Grid>
</f:UserControlBase>
And the corresponding viewmodel
using BankingApp.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using PRBD_Framework;
namespace BankingApp.ViewModel {
public class ManagerClientDataViewModel : ViewModelCommon {
private Client _selectedClient;
public Client SelectedClient {
get => _selectedClient;
set {
SetProperty(ref _selectedClient, value);
Console.WriteLine(SelectedClient.FirstName);
}
}
public string Firstname {
get => SelectedClient?.FirstName;
set => SetProperty(SelectedClient.FirstName, value, SelectedClient, (s, v) => {
s.FirstName = v;
Console.WriteLine(s.FirstName);
});
}
public string Lastname {
get => SelectedClient?.LastName;
set => SetProperty(SelectedClient.LastName, value, SelectedClient, (s, v) => {
s.LastName = v;
Console.WriteLine(s.LastName);
});
}
public string Email {
get => SelectedClient?.Email;
set => SetProperty(SelectedClient.Email, value, SelectedClient, (s, v) => {
s.Email = v;
Console.WriteLine(s.Email);
});
}
public string Password {
get => SelectedClient?.Password;
set => SetProperty(SelectedClient.Password, value, SelectedClient, (s, v) => {
s.Password = v;
});
}
}
}
My problem is that when I select a customer, his data does not appear in the form, could someone help me? Thanks
Solution 1:[1]
this is a working example without the use of any frameworks. Maybe this helps to track down your issue or just use this code ;-)
XAML:
<Window x:Class="WpfApp1AgencyTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1AgencyTest"
mc:Ignorable="d"
xmlns:vm="clr-namespace:WpfApp1AgencyTest"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<vm:ManagerAgencyViewModel/>
</Window.DataContext>
<Grid>
<TabControl>
<TabItem Header="Manager" >
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<TextBlock Margin="5" Text="Agency:"></TextBlock>
<ComboBox Grid.Column="1" Margin="5,5,5,5"
ItemsSource="{Binding Path=Agencies}"
DisplayMemberPath="Name"
SelectedItem="{Binding SelectedAgency}"
></ComboBox>
</Grid>
<DataGrid
Grid.Row="1"
ItemsSource="{Binding SelectedAgency.Clients}"
SelectedItem="{Binding SelectedClient}"
AutoGenerateColumns="False"
>
<DataGrid.Columns>
<DataGridTextColumn Header="id" Binding="{Binding ID}"></DataGridTextColumn>
<DataGridTextColumn Header="First name" Binding="{Binding Firstname}"></DataGridTextColumn>
<DataGridTextColumn Header="Last name" Binding="{Binding Lastname}"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
<TabControl>
<TabItem Header="Client">
<StackPanel DataContext="{Binding SelectedClient}">
<TextBlock>First name:</TextBlock>
<TextBox Text="{Binding Firstname}"></TextBox>
<TextBlock>Last name:</TextBlock>
<TextBox Text="{Binding Lastname}"></TextBox>
<TextBlock>Password:</TextBlock>
<TextBox Text="{Binding Password}"></TextBox>
<TextBlock>Confirm Password:</TextBlock>
<TextBox></TextBox>
</StackPanel>
</TabItem>
<TabItem Header="Account">
</TabItem>
</TabControl>
</StackPanel>
</TabItem>
</TabControl>
</Grid>
</Window>
class Client
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace WpfApp1AgencyTest
{
public class Client : INotifyPropertyChanged
{
private string id;
public string ID
{
get { return id; }
set { id = value; RaisePropertyChange(); }
}
private string firstname;
public string Firstname
{
get => firstname; set
{
firstname = value;
RaisePropertyChange();
}
}
private string lastname;
public string Lastname
{
get => lastname; set
{
lastname = value;
RaisePropertyChange();
}
}
private string password;
public string Password
{
get => password; set
{
password = value;
RaisePropertyChange();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChange([CallerMemberName] string caller = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(caller));
}
}
}
}
class Agency
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace WpfApp1AgencyTest
{
public class Agency
{
private string name;
public string Name
{
get { return name; }
set { name = value; RaisePropertyChange(); }
}
public ObservableCollection<Client> Clients { get; set; } = new();
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChange([CallerMemberName] string caller = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(caller));
}
}
}
}
class ManagerAgencyViewModel
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace WpfApp1AgencyTest
{
public class ManagerAgencyViewModel
{
public ManagerAgencyViewModel()
{
Agencies =
new ObservableCollection<Agency>() {
new Agency()
{
Name="Dexia",
Clients = new ObservableCollection<Client>()
{
new Client(){ID="1", Firstname = "b",Lastname="inconnu"},
new Client(){ ID="2",Firstname = "Mustafa",Lastname="Azoud" },
new Client{ID="3",Firstname="Samiha",Lastname="Draa"}
}
},
new Agency()
{
Name="2Advacend",
Clients = new ObservableCollection<Client>()
{
new Client(){ ID="4", Firstname = "Joe",Lastname="Doe"},
new Client(){ ID="5", Firstname = "Max",Lastname="Headroom" },
}
}
};
}
public ObservableCollection<Agency> Agencies { get; set; }
private Agency selectedAgency;
public Agency SelectedAgency
{
get => selectedAgency;
set
{
selectedAgency = value;
RaisePropertyChange();
}
}
private Client selectedClient;
public Client SelectedClient { get => selectedClient; set {
selectedClient = value;
RaisePropertyChange();
} }
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChange([CallerMemberName] string caller = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(caller));
}
}
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | silverfighter |