I'm doing my 1st C# WPF project with MVVM and I have few questions.
1st, I want to list some objects from my model, and when I click on one of them a form is displayed on the screen (or a created form by default). In my form, the binding is not working and I can't find why.
The 1st view-model who call the form view-model:
public class ZRoleViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public ZRoleViewModel()
{
IRepository<Role> mRole = new Repository<Role>();
_roles = new ObservableCollection<Role>(mRole.GetAll());
SelectedRole = new Role();
SelectedIndex = -1;
_editRole = new ZRoleEditViewModel();
}
private ObservableCollection<Role> _roles;
private Role _selectedRole;
private int _selectedIndex;
private ZRoleEditViewModel _editRole;
public ObservableCollection<Role> Roles
{
get => _roles;
set
{
_roles = value;
RaisePropertyChanged("Roles");
}
}
public Role SelectedRole
{
get => _selectedRole;
set
{
_selectedRole = value;
RaisePropertyChanged("SelectedRole");
if (_selectedRole != null && _editRole != null)
{
_editRole.Role = _selectedRole;
}
}
}
public int SelectedIndex
{
get => _selectedIndex;
set
{
_selectedIndex = value;
RaisePropertyChanged("SelectedIndex");
}
}
public ZRoleEditViewModel EditRole { get => _editRole; set => _editRole = value; }
}
Its xaml:
<UserControl x:Class="WpfPlanAction.View.ZRoleUserControl"
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:WpfPlanAction.View"
xmlns:viewModel="clr-namespace:WpfPlanAction.ViewModel"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.DataContext>
<viewModel:ZRoleViewModel/>
</UserControl.DataContext>
<UserControl.Resources>
<DataTemplate DataType="{x:Type viewModel:ZRoleEditViewModel}">
<local:ZRoleEditUserControl/>
</DataTemplate>
</UserControl.Resources>
<Grid>
<DockPanel Margin="10">
<StackPanel DockPanel.Dock="Left">
<TextBlock Text="Rôles" FontWeight="Bold"/>
<DataGrid ItemsSource="{Binding Roles}"
SelectedItem="{Binding SelectedRole}"
SelectedIndex="{Binding SelectedIndex}"
IsReadOnly="True"
SelectionMode="Single"
AutoGenerateColumns="False"
RowHeaderWidth="0">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Libelle}" Header="Libellé"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
<ContentControl Content="{Binding EditRole}"></ContentControl>
</DockPanel>
</Grid>
Here, my DataGrid's binding is working well. My ContentControl displays my second UserControl, but when I'm selecting an object, the binding is not working in my form.
Here the second view-model:
public class ZRoleEditViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public ZRoleEditViewModel()
{
_role = new Role();
}
private Role _role;
public Role Role
{
get => _role;
set
{
_role = value;
RaisePropertyChanged("Role");
}
}
}
And the xaml:
<UserControl
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:WpfPlanAction.View"
xmlns:viewModel="clr-namespace:WpfPlanAction.ViewModel"
xmlns:Model="clr-namespace:WpfPlanAction.Model" x:Class="WpfPlanAction.View.ZRoleEditUserControl"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" Loaded="UserControl_Loaded">
<UserControl.Resources>
<CollectionViewSource x:Key="roleViewSource" Source="{Binding Role}" d:DesignSource="{d:DesignInstance {x:Type Model:Role}, CreateList=True}"/>
</UserControl.Resources>
<UserControl.DataContext>
<viewModel:ZRoleEditViewModel/>
</UserControl.DataContext>
<Grid>
<Grid x:Name="grid1" VerticalAlignment="Top" Margin="10,10,0,0" HorizontalAlignment="Left" DataContext="{StaticResource roleViewSource}" Width="280">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label VerticalAlignment="Center" Grid.Row="0" Margin="3" HorizontalAlignment="Left" Grid.Column="0" Content="Abrege:"/>
<TextBox x:Name="abregeTextBox" Width="209" VerticalAlignment="Center" Text="{Binding Abrege, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}" Grid.Row="0" Margin="3,4,-85,4" Height="24" HorizontalAlignment="Left" Grid.Column="1"/>
<Label VerticalAlignment="Center" Grid.Row="1" Margin="3" HorizontalAlignment="Left" Grid.Column="0" Content="Libelle:"/>
<TextBox x:Name="libelleTextBox" Width="209" VerticalAlignment="Center" Text="{Binding Libelle, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}" Grid.Row="1" Margin="3,4,-84,4" Height="24" HorizontalAlignment="Left" Grid.Column="1"/>
</Grid>
</Grid>
RaisePropertyChanged("Role") is called but the binding won't work.
I tried to do it with only one view-model and one user control, the binding works well, too well (but as expected), when i'm editing the form, the list is updated before I commit. maybe there is a better way to display the list and the editing form on the same page.
And in a 2nd time, I'm looking for a simple way to manage a relation many to many without using a specific view. By example: Role have a List parsonsByRole and Person have a List RolesByPerson, I thought to display a full list of Role and Add/Remove buttons in the Person editing form to populate RolesByPerson. Maybe there is a better way to do it.
Thanks!
Thanks to the comments I have now a better understanding how it's works.
Here my solution:
1st view-model:
public class ZRoleViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public ZRoleViewModel()
{
IRepository<Role> mRole = new Repository<Role>();
_roles = new ObservableCollection<Role>(mRole.GetAll());
SelectedIndex = -1;
SelectedRole = new Role();
_editRole = new ZRoleEditViewModel(SelectedRole);
_editUC = new ZRoleEditUserControl(_editRole);
}
private ObservableCollection<Role> _roles;
private Role _selectedRole;
private int _selectedIndex;
private ZRoleEditViewModel _editRole;
private ZRoleEditUserControl _editUC;
public ObservableCollection<Role> Roles
{
get => _roles;
set
{
_roles = value;
RaisePropertyChanged("Roles");
}
}
public Role SelectedRole
{
get => _selectedRole;
set
{
_selectedRole = value;
RaisePropertyChanged("SelectedRole");
if (_selectedRole != null && _editRole != null)
{
_editRole.Role = _selectedRole;
}
}
}
public int SelectedIndex
{
get => _selectedIndex;
set
{
_selectedIndex = value;
RaisePropertyChanged("SelectedIndex");
}
}
public ZRoleEditUserControl EditUC { get => _editUC; }
}
And the view:
<UserControl x:Class="WpfPlanAction.View.ZRoleUserControl"
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:WpfPlanAction.View"
xmlns:viewModel="clr-namespace:WpfPlanAction.ViewModel"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.DataContext>
<viewModel:ZRoleViewModel/>
</UserControl.DataContext>
<Grid>
<DockPanel Margin="10">
<StackPanel DockPanel.Dock="Left">
<TextBlock Text="Rôles" FontWeight="Bold"/>
<DataGrid ItemsSource="{Binding Roles}"
SelectedItem="{Binding SelectedRole}"
SelectedIndex="{Binding SelectedIndex}"
IsReadOnly="True"
SelectionMode="Single"
AutoGenerateColumns="False"
RowHeaderWidth="0">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Libelle}" Header="Libellé"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
<ContentControl Content="{Binding EditUC, Mode=OneTime}" />
</DockPanel>
</Grid>
I load in the ContentControl the view instead of the view-model and I change the constructor of my edit view and view-model to be sure to not use the default constructor.
2nd view-model:
public class ZRoleEditViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public ZRoleEditViewModel(Role currentRole)
{
_role = currentRole;
}
private Role _role;
public Role Role
{
get => _role;
set
{
_role = value;
RaisePropertyChanged("Role");
}
}
}
The view:
<UserControl
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:WpfPlanAction.View"
xmlns:viewModel="clr-namespace:WpfPlanAction.ViewModel"
xmlns:Model="clr-namespace:WpfPlanAction.Model" x:Class="WpfPlanAction.View.ZRoleEditUserControl"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" Loaded="UserControl_Loaded">
<Grid>
<Grid x:Name="grid1" VerticalAlignment="Top" Margin="10,10,0,0" HorizontalAlignment="Left" Width="280">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label VerticalAlignment="Center" Grid.Row="0" Margin="3" HorizontalAlignment="Left" Grid.Column="0" Content="Abrege:"/>
<TextBox x:Name="abregeTextBox" Width="209" VerticalAlignment="Center" Text="{Binding Role.Abrege, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}" Grid.Row="0" Margin="3,4,-85,4" Height="24" HorizontalAlignment="Left" Grid.Column="1"/>
<Label VerticalAlignment="Center" Grid.Row="1" Margin="3" HorizontalAlignment="Left" Grid.Column="0" Content="Libelle:"/>
<TextBox x:Name="libelleTextBox" Width="209" VerticalAlignment="Center" Text="{Binding Role.Libelle, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}" Grid.Row="1" Margin="3,4,-84,4" Height="24" HorizontalAlignment="Left" Grid.Column="1"/>
</Grid>
</Grid>
I remove the resources and the datacontext tags.
Its code behind:
public partial class ZRoleEditUserControl : UserControl
{
public ZRoleEditUserControl(ZRoleEditViewModel context)
{
InitializeComponent();
this.DataContext = context;
}
}
My main issue is fix, the binding is working well. Too well again, I guess I will have to clone my object Role when I'm creating the edit form or use UpdateSourceTrigger=Explicit with maybe a BindingGroup.
Thanks for your help!
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.