I'm trying to create an WPF application with the MVVM model.
At the moment i'm stuck at the following;
In the application there is the option to seach on a number (registration number), nothing fancy just a textbox.
When the user pressed "Search" I wan't to load the data from the DB in to the model and showing it on the user controls.
This is my MainWindowViewModel
public class MainWindowViewModal : ViewModelBase
{
//Fields
private string _zoekenUitvaartnummer;
private string _zoekenAchternaam;
private string _zoekenGeboortedatum;
private bool _volgendePagina = false;
private IOverledeneRepository overledeneRepository;
//Properties
public string ZoekenUitvaartnummer
{
get => _zoekenUitvaartnummer;
set
{
_zoekenUitvaartnummer = value;
OnPropertyChanged(nameof(ZoekenUitvaartnummer));
}
}
//->Commands
public ICommand SearchUitvaartnummerCommand { get; }
public MainWindowViewModal()
{
overledeneRepository = new OverledeneRepository();
SearchUitvaartnummerCommand = new ViewModelCommand(ExecuteSearchUitvaartnummerCommand, CanExecuteSearchUitvaartnummerCommand);
SearchAchternaamCommand = new ViewModelCommand(ExecuteSearchAchternaamCommand, CanExecuteSearchAchternaamCommand);
}
private bool CanExecuteSearchUitvaartnummerCommand(object obj)
{
bool validNummerSearch;
if(string.IsNullOrWhiteSpace(ZoekenUitvaartnummer))
{
validNummerSearch = false;
}
else
{
validNummerSearch = true;
}
return validNummerSearch;
}
private void ExecuteSearchUitvaartnummerCommand(object obj)
{
var SearchUitvaartleider = overledeneRepository.GetUitvaarleiderByUitvaartId(ZoekenUitvaartnummer);
OverledeneViewModel.Instance.LoadCurrentUitvaartLeider(ZoekenUitvaartnummer);
}
}
on overledeneRepository.GetUitvaarleiderByUitvaartId(ZoekenUitvaartnummer) the database is called
public class OverledeneRepository : RepositoryBase, IOverledeneRepository
{
public OverledeneUitvaartleiderModel GetUitvaarleiderByUitvaartId(string uitvaartId)
{
OverledeneUitvaartleiderModel uitvaartLeiderGegevens = null;
using (var connection = GetConnection())
using (var command = new SqlCommand())
{
connection.Open();
command.Connection = connection;
command.CommandText = "SELECT UitvaartId, CONCAT(Initialen,' ',Achternaam) as PersoneelName, PersoneelId, Uitvaartnummer " +
" FROM OverledeneUitvaartleider" +
" INNER JOIN ConfigurationPersoneel ON PersoneelId = Id" +
" WHERE Uitvaartnummer=@uitvaartNummer";
command.Parameters.Add("@uitvaartNummer", System.Data.SqlDbType.VarChar).Value = uitvaartId;
using (var reader = command.ExecuteReader())
{
if (reader.Read())
{
uitvaartLeiderGegevens = new OverledeneUitvaartleiderModel()
{
UitvaartId = (Guid)reader[0],
PersoneelNaam = reader[1].ToString(),
PersoneelId = (Guid)reader[2],
Uitvaartnummer = reader[3].ToString(),
};
}
}
}
return uitvaartLeiderGegevens;
}
}
the returned value is parsed to the viewmodel
public class OverledeneViewModel : ViewModelBase
{
//Fields
private string _uitvaartNummer;
private string _uitvaartLeider;
private IOverledeneRepository overledeneRepository;
private OverledeneUitvaartleiderModel _uitvaartLeiderModel;
//Properties
public OverledeneUitvaartleiderModel UitvaartLeiderInfo
{
get
{
return _uitvaartLeiderModel;
}
set
{
_uitvaartLeiderModel = value;
OnPropertyChanged(nameof(UitvaartLeiderInfo));
}
}
private OverledeneViewModel()
{
overledeneRepository = new OverledeneRepository();
UitvaartLeiderInfo = new OverledeneUitvaartleiderModel();
SaveCommand = new ViewModelCommand(ExecuteSaveCommand, CanExecuteSaveCommand);
}
public static OverledeneViewModel Instance {get;} = new();
public void LoadCurrentUitvaartLeider(string uitvaartNummer)
{
var UitvaarLeiderResult = overledeneRepository.GetUitvaarleiderByUitvaartId(uitvaartNummer);
if (UitvaarLeiderResult != null)
{
UitvaartLeiderInfo.Uitvaartnummer = UitvaarLeiderResult.Uitvaartnummer;
UitvaartLeiderInfo.PersoneelNaam = UitvaarLeiderResult.PersoneelNaam;
}
}
}
With this model behind
public class OverledeneUitvaartleiderModel
{
public Guid UitvaartId { get; set; }
public Guid PersoneelId { get; set; }
public string PersoneelNaam { get; set; }
public string Uitvaartnummer { get; set; }
}
and finally this is the xaml where I would expect the data to be visable
<UserControl x:Class="Test.Views.OverledeneView"
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:Test.ViewModels"
mc:Ignorable = "d"
d:DesignHeight="696.96" d:DesignWidth="1190.257"
DataContext="{x:Static local:OverledeneViewModel.Instance}">
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<Grid x:Name="GridOverledeneView" Background="White" ScrollViewer.CanContentScroll="True">
<TextBox x:Name="input_UitvaartNrOverledene" Text="{Binding Path=uitvaartLeiderModel.Uitvaartnummer, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" TabIndex="1" VerticalContentAlignment="Center" HorizontalAlignment="Left" Margin="204,14,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="301" Height="31" FontSize="16" SelectionBrush="#FFD70000" FontFamily="Arial" BindingGroup="{Binding Text, ElementName=UitvaartLeider}"/>
</Grid>
</ScrollViewer>
</UserControl>
this is the ViewModelBase
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
What am I doing wrong?
Can anybody help me and explain to me what i'm doing wrong?
Any help would be really appreciated.
Patrick
Most likely you have confusion in instances of the OverledeneViewModel
class.
In the MainWindowViewModal.ExecuteSearchUitvaartnummerCommand
method you create the first instance of OverledeneViewModel
. And run the verification method on this instance.
private void ExecuteSearchUitvaartnummerCommand(object obj)
{
var SearchUitvaartleider = overledeneRepository.GetUitvaarleiderByUitvaartId(ZoekenUitvaartnummer);
var DL = new OverledeneViewModel();
DL.LoadCurrentUitvaartLeider(ZoekenUitvaartnummer);
}
In the XAML of the OverledeneView
View class, you create another instance of the OverledeneViewModel
that is in an initial immutable state.
<UserControl.DataContext>
<local:OverledeneViewModel/>
</UserControl.DataContext>
If you don't need multiple instances of OverledeneViewModel
at the same time, then try using Singleton.
Example:
public class OverledeneViewModel : ViewModelBase
{
// Some Code
// Hiding the constructor
private OverledeneViewModel()
{
overledeneRepository = new OverledeneRepository();
UitvaartLeiderInfo = new OverledeneUitvaartleiderModel();
SaveCommand = new ViewModelCommand(ExecuteSaveCommand, CanExecuteSaveCommand);
}
// The only instance available
public static OverledeneViewModel Instance {get;} = new();
}
private void ExecuteSearchUitvaartnummerCommand(object obj)
{
var SearchUitvaartleider = overledeneRepository.GetUitvaarleiderByUitvaartId(ZoekenUitvaartnummer);
// var DL = new OverledeneViewModel();
OverledeneViewModel.Instance.LoadCurrentUitvaartLeider(ZoekenUitvaartnummer);
}
<UserControl x:Class="Test.Views.OverledeneView"
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:Test.ViewModels"
mc:Ignorable = "d"
d:DesignHeight="696.96" d:DesignWidth="1190.257"
DataContext={x:Static local:OverledeneViewModel.Instance}>
<!--<UserControl.DataContext>
<local:OverledeneViewModel/>
</UserControl.DataContext>-->
When I put the result in the messagebox it's showing 1234, maybe the binding is off somewhere? really lost here
I didn't notice the second mistake right away.
In "OverledeneView" you have set the binding:
<TextBox x:Name="input_UitvaartNrOverledene"
Text="{Binding Path=uitvaartLeiderModel.Uitvaartnummer, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
-----/>
However, in the class "OverledeneViewModel" there is no property "uitvaartLeiderModel".
I think you need a binding like this:
<TextBox x:Name="input_UitvaartNrOverledene"
Text="{Binding UitvaartLeiderInfo.Uitvaartnummer, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
-----/>
P.S. There are also notes on the implementation of the "OverledeneUitvaartleiderModel" class. It does not have an implementation of INotifyPropertyChanged, which means that when its properties change, the view will not be automatically updated. I think you'd be better off replacing this class with a read-only structure.