Update
Managed to fix the selectedIndex problem. I'd forgotten to set SelectedItem as well and naturally that caused a few issues.
So at 9AM this morning we got our 24 hour assignment and I have hit a brick wall.
We're supposed to create a program that allows a supervisor to Add and delete Employees and add Working Sessions, total hours and total earnings. But I am having some problems succesfully implementing this following the MVVM-Pattern. For some reason my Bindings simply aren't working and the only Solution I can see is someone looking over my project and helping me troubleshoot it.
Here is my code - I'm very sorry about having to post the entire thing but given that I have no clue where the problem is I did not see any other options. :
EmployeeModel
[Serializable]
public class WorkSessions : ObservableCollection<WorkSessionModel>
{
public WorkSessions()
{
}
}
[Serializable]
public class WorkSessionModel : INotifyPropertyChanged
{
private DateTime _dateTime;
private string _id;
private double _hours;
public WorkSessionModel()
{
}
public DateTime DateTime
{
get { return _dateTime; }
set
{
_dateTime = value;
NotifyPropertyChanged("DateTime");
}
}
public string ID
{
get { return _id; }
set
{
_id = value;
NotifyPropertyChanged("ID");
}
}
public double Hours
{
get { return _hours; }
set
{
_hours = value;
NotifyPropertyChanged("Hours");
NotifyPropertyChanged("TotalHours");
}
}
[field: NonSerialized]
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
WorkSessionModel
[Serializable]
public class WorkSessions : ObservableCollection<WorkSessionModel>
{
public WorkSessions()
{
}
}
[Serializable]
public class WorkSessionModel : INotifyPropertyChanged
{
private DateTime _dateTime;
private string _id;
private double _hours;
public WorkSessionModel()
{
}
public DateTime DateTime
{
get { return _dateTime; }
set
{
_dateTime = value;
NotifyPropertyChanged("DateTime");
}
}
public string ID
{
get { return _id; }
set
{
_id = value;
NotifyPropertyChanged("ID");
}
}
public double Hours
{
get { return _hours; }
set
{
_hours = value;
NotifyPropertyChanged("Hours");
NotifyPropertyChanged("TotalHours");
}
}
[field: NonSerialized]
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
EmployeeViewModel
public class EmployeeViewModel : ViewModelBase
{
private Employees _employeesModel = new Employees();
public Employees EmployeesView = new Employees();
public ObservableCollection<WorkSessionModel> WorkSessions { get; set; }
private string _id = "0";
private string _name = "noname";
private double _wage = 0;
private int _totalhours = 0;
public string ID
{
get { return _id; }
set { _id = value; RaisePropertyChanged("ID"); }
}
public string Name
{
get { return _name; }
set
{
_name = value;
RaisePropertyChanged("Name");
}
}
public double Wage
{
get { return _wage; }
set
{
_wage = value;
RaisePropertyChanged("Wage");
}
}
public int TotalHours
{
get { return _totalhours; }
set
{
_totalhours = value;
RaisePropertyChanged("TotalHours");
}
}
private EmployeeModel _selectedEmployee = new EmployeeModel();
public EmployeeModel SelectedEmployee
{
get { return _selectedEmployee; }
set
{
_selectedEmployee = value;
RaisePropertyChanged("SelectedEmployee");
}
}
private int _selectedEmployeeIndex;
public int SelectedEmployeeIndex
{
get { return _selectedEmployeeIndex; }
set
{
_selectedEmployeeIndex = value;
RaisePropertyChanged("SelectedEmployeeIndex");
}
}
#region RelayCommands
// Employee Relay Commands
public RelayCommand EmployeeAddNewCommand { set; get; }
public RelayCommand EmployeeDeleteCommand { set; get; }
public RelayCommand EmployeeNextCommand { set; get; }
public RelayCommand EmployeePrevCommand { set; get; }
public RelayCommand EmployeeTotalHoursCommand { get; set; }
#endregion
public EmployeeViewModel()
{
InitCommands();
}
private void InitCommands()
{
EmployeeAddNewCommand = new RelayCommand(EmployeeAddNewExecute, EmployeeAddNewCanExecute);
EmployeeDeleteCommand = new RelayCommand(EmployeeDeleteNewExecute, EmployeeDeleteCanExecute);
EmployeeNextCommand = new RelayCommand(EmployeeNextExecute, EmployeeNextCanExecute);
EmployeePrevCommand = new RelayCommand(EmployeePrevExecute, EmployeePrevCanExecute);
//EmployeeTotalHoursCommand = new RelayCommand(EmployeeTotalHoursExecute, EmployeeTotalHoursCanExecute);
}
//private void EmployeeTotalHoursExecute()
//{
// _selectedEmployee.TotalHours();
//}
//private bool EmployeeTotalHoursCanExecute()
//{
// return true;
//}
private void EmployeeAddNewExecute()
{
EmployeeModel newEmployee = new EmployeeModel();
EmployeesView.Add(newEmployee);
_employeesModel.Add(newEmployee);
SelectedEmployee = newEmployee;
}
private bool EmployeeAddNewCanExecute()
{
return true;
}
private void EmployeeDeleteNewExecute()
{
if (MessageBox.Show("You are about delete all submissions for Employee," + SelectedEmployee.Name + "(" + SelectedEmployee.ID +")\r\nAre you sure?", "This is a Warning!", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
_employeesModel.Remove(SelectedEmployee);
EmployeesView.Remove(SelectedEmployee);
}
}
private bool EmployeeDeleteCanExecute()
{
if (SelectedEmployee != null)
return true;
else return false;
}
private void EmployeeNextExecute()
{
SelectedEmployeeIndex++;
}
private bool EmployeeNextCanExecute()
{
if (SelectedEmployeeIndex < EmployeesView.Count - 1)
return true;
return false;
}
private void EmployeePrevExecute()
{
SelectedEmployeeIndex--;
}
private bool EmployeePrevCanExecute()
{
if (SelectedEmployeeIndex > 0)
return true;
return false;
}
}
View
public partial class MainWindow : Window
{
public EmployeeViewModel EmployeeViewModel = new EmployeeViewModel();
public MainWindow()
{
InitializeComponent();
menu_employee.DataContext = EmployeeViewModel;
sp_employees.DataContext = EmployeeViewModel;
datagrid_employees.ItemsSource = EmployeeViewModel.EmployeesView;
grid_selectedEmployee.DataContext = EmployeeViewModel.SelectedEmployee;
}
}
I can see a few problems with your code:
When the SelectedIndex is updated, SelectedItem remains the same and vice versa.
It looks like your data binding is out of order:
The DataContext property cascades down to every child of a certain dependency object.
The code in the MainWindow constructor should probably be replaced by:
this.DataContext = EmployeeViewModel;
Then in XAML set the rest of the properties using Data Binding. The problem in your situation is that the DataContext of the selectedemployee is only set once. This means if you select another employee, then it will not update.
An example for your SelectedEmployee grid:
<Grid Name="grid_selectedEmployee" DataContext="{Binding SelectedEmployee,
UpdateSourceTrigger=PropertyChanged}">...</Grid>
One of the biggest things I see is you are setting properties, not binding them.
For example,
datagrid_employees.ItemsSource = EmployeeViewModel.EmployeesView;
You are telling your DataGrid that it's ItemsSource should be that specific object. You need to bind it to that value so you are telling it to point to that property instead. This will make your UI correctly reflect what's in your ViewModel
The other huge red flag I see is your ViewModel referencing something called and EmployeeView which leads me to believe your View and ViewModel too closely tied together.
Your ViewModel should contain all your business logic and code, while the View is usually XAML and simply reflects the ViewModel in a user-friendly way.
The View and the ViewModel should never directly reference each other (I have had my View reference my ViewModel in some rare occasions, but never the other way around)
For example, an EmployeesViewModel might contain
ObservableCollection<Employee> Employees
Employee SelectedEmployee
ICommand AddEmployeeCommand
ICommand DeleteEmployeeCommand
while your View (XAML) might look like this:
<StackPanel>
<StackPanel Orientation="Horizontal">
<Button Content="Add" Command="{Binding AddEmployeeCommand}" />
<Button Content="Delete" Command="{Binding DeleteEmployeeCommand}" />
</StackPanel>
<DataGrid ItemsSource="{Binding Employees}"
SelectedItem="{Binding SelectedEmployee}">
... etc
</DataGrid>
<UniformGrid DataContext="{Binding SelectedEmployee}" Columns="2" Rows="4">
<TextBlock Text="ID" />
<TextBox Text="{Binding Id}" />
... etc
</UniformGrid >
</StackPanel>
And the only thing you should be setting is the DataContext of the entire Window. Usually I overwrite App.OnStartup() to start up my application:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var view = new MainWindow();
var vm = new EmployeesViewModel;
view.DataContext = vm;
view.Show();
}
}
Although I suppose in your case this would also work:
public MainWindow()
{
InitializeComponent();
this.DataContext = new EmployeesViewModel();
}
Related
I struggle to refresh the data attached to the RADdatagrid. The view with the Datagrid shows the position of a team in a competition. Every time a new game is finished the view has to be updated automatically. The data is stored in a sqlite database and a simple sql query gets the positions from the database. A button in the view that calls the query method does the job but I want to update the grid directly after the finished game is stored. I implemented INotifyPropertyChanged but no result. I also tried to fire the button_click event but that also didn't bring me the result. I want to do this right but I am open to a quick and dirty solution. Here is a bit of my code:
<StackPanel Grid.Row="2" Orientation="Horizontal">
<TextBlock Grid.Row="2" Margin="40,0,0,5">Team Ranking</TextBlock>
<Button x:Name="RefreshViewButton" Command="{Binding Path=RefreshView}" Margin="40 0 0 0 " Click="RefreshViewButton_Click">Refresh</Button>
</StackPanel>
<StackPanel Grid.Row="6" Margin="0, 0, 50, 0" VerticalAlignment="Stretch">
<telerikGrid:RadDataGrid
AutoGenerateColumns="False"
x:Name="Datagrid"
BorderThickness="0"
ItemsSource="{x:Bind ViewModel.TeamResult, Mode=TwoWay}"
ColumnDataOperationsMode="Flyout"
GridLinesVisibility="None"
RelativePanel.AlignLeftWithPanel="True"
RelativePanel.AlignRightWithPanel="True"
UserEditMode="None" >
<telerikGrid:RadDataGrid.Columns>
<telerikGrid:DataGridNumericalColumn PropertyName="Rank" Header="Rank" SizeMode="Auto"/>
<telerikGrid:DataGridTextColumn PropertyName="Team_Naam" Header="TeamNaam" SizeMode="Auto"/>
<telerikGrid:DataGridTextColumn PropertyName="WedstrijdPunten" Header="WP" CanUserEdit="False" CanUserFilter="False" CanUserGroup="False" CanUserReorder="False" CanUserResize="False" CanUserSort="False" SizeMode="Auto" />
<telerikGrid:DataGridTextColumn PropertyName="PuntenVoor" Header="GP" SizeMode="Auto"/>
</telerikGrid:RadDataGrid.Columns>
</telerikGrid:RadDataGrid>
</StackPanel>
This is my view:
public sealed partial class TeamTotals : Page
{
public TeamResultsViewModel ViewModel { get; set; } = new TeamResultsViewModel();
public TeamTotals()
{
DataContext = ViewModel;
this.InitializeComponent();
}
public void RefreshViewButton_Click(object sender, RoutedEventArgs e)
{
ViewModel.LoadTeamResultsData();
}
}
My ViewModel:
public class TeamResultsViewModel : TeamModel
{
//public List<TeamModel> TeamResult = new List<TeamModel>();
public ObservableCollection<TeamModel> TeamResult = new ObservableCollection<TeamModel>();
TeamModel tm = new TeamModel();
public TeamResultsViewModel()
{
LoadTeamResultsData();
tm.PropertyChanged += Tm_PropertyChanged;
}
private void Tm_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
throw new NotImplementedException();
}
public void LoadTeamResultsData()
{
int i = 0;
if(TeamResult != null)
{
TeamResult.Clear();
}
try
{
string sql = "Select Team_Naam, WedstrijdPunten, PuntenVoor, PuntenTegen FROM Team WHERE KlasseId = 1 " +
"ORDER BY WedstrijdPunten DESC, PuntenVoor DESC LIMIT 10;";
var resultaat = SqliteDataAccess.LoadData<TeamModel>(sql, new Dictionary<string, object>());
foreach (var x in resultaat)
{
TeamResult.Add(x);
x.Rank = i++;
}
}
catch (Exception ex)
{
var messagedialog2 = new MessageDialog($"{ex}");
_ = messagedialog2.ShowAsync();
}
return;
}
}
and the model:
public class TeamModel : INotifyPropertyChanged
{
private int _id;
public int Id
{
get { return _id; }
set { _id = value; }
}
private int _klasseId;
public int KlasseId
{
get { return _klasseId; }
set { _klasseId = value; }
}
private string _team_naam;
public string Team_Naam
{
get { return _team_naam; }
set { _team_naam = value; }
}
private int _coachId;
public int CoachId
{
get { return _coachId; }
set { _coachId = value; }
}
private int _clubId;
public int ClubId
{
get { return _clubId; }
set { _clubId = value; }
}
private int _puntenVoor;
public int PuntenVoor
{
get { return _puntenVoor; }
set { _puntenVoor = value; }
}
private int _puntenTegen;
public int PuntenTegen
{
get { return _puntenTegen; }
set { _puntenTegen = value; }
}
private int _wedstrijdPunten;
public int WedstrijdPunten
{
get { return _wedstrijdPunten; }
set
{
_wedstrijdPunten = value;
OnPropertyChanged("WedstrijdPunten");
}
}
private int _rank;
public int Rank
{
get { return _rank; }
set { _rank = value; }
}
public List<SpelerModel> TeamLeden { get; set; } = new List<SpelerModel>();
public string Standen => $"{Team_Naam} : {PuntenVoor}";
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public override string ToString()
{
return Standen;
}
}
I have a View that has ItemsControl and Wrappanel inside it. By using Drag&Drop files I am trying to update the Wrappanel. I have all data related to files in a JsonFile. There is no problem with appending data to json file. It is working. When I redebug the solution I can see the list of files in Wrappanel.
public partial class StorageView : Window
{
readonly StorageViewModel _storageViewModel;
public StorageView()
{
_storageViewModel = new StorageViewModel();
InitializeComponent();
}
private void TopMenuBorder_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
{
this.DragMove();
}
}
// Wrappanel Drop
private void StorageBrowser_Drop(object sender, DragEventArgs e)
{
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
List<string> files2 = new List<string>();
foreach (var file in files)
{
files2.Add(file);
}
_storageViewModel.TempFiles = files2;
}
// Drag Enter
private void StorageBrowser_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
e.Effects = DragDropEffects.Move;
else
e.Effects = DragDropEffects.None;
}
private void CreateNewFolder_Click(object sender, RoutedEventArgs e)
{
}
}
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
<StackPanel Orientation="Vertical">
<Button Command="{Binding AddFileCommand}" Content="Add File"/>
<ItemsControl ItemsSource="{Binding Files, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Name="StorageBrowser" Background="White" Drop="StorageBrowser_Drop" DragEnter="StorageBrowser_DragEnter" VerticalAlignment="Stretch" AllowDrop="True" Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Label Content="{Binding FileName}"/>
<!--<comp:FileCard/>-->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
public class StorageViewModel : Screen, INotifyPropertyChanged
{
#region Commands
private ICommand _addFileCommand;
private bool canExecute = true;
public bool CanExecute
{
get
{
return this.canExecute;
}
set
{
if (this.canExecute == value)
{
return;
}
this.canExecute = value;
}
}
public ICommand AddFileCommand
{
get
{
return _addFileCommand;
}
set
{
_addFileCommand = value;
}
}
#endregion
#region Notify
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
#region FileModel
private int _id;
private string _fileName;
private string _fullFileName;
private decimal _fileSize;
private string _fileType;
private string _fileIcon;
private string _fileExtension;
private string _fileSizeType;
private string _fileImage;
private string _fullFileSize;
private string _fileOwner;
private DateTime? _created;
private DateTime? _modified;
private DateTime? _lastAccessed;
private bool _isVersionFile;
private List<FileModel> _fileVersions;
public List<FileModel> FileVersions
{
get { return _fileVersions; }
set { _fileVersions = value; OnPropertyChanged(nameof(FileVersions)); }
}
public bool IsVersionFile
{
get { return _isVersionFile; }
set { _isVersionFile = value; OnPropertyChanged(nameof(IsVersionFile)); }
}
public DateTime? LastAccess
{
get { return _lastAccessed; }
set { _lastAccessed = value; OnPropertyChanged(nameof(LastAccess)); }
}
public DateTime? Modified
{
get { return _modified; }
set { _modified = value; OnPropertyChanged(nameof(Modified)); }
}
public DateTime? Created
{
get { return _created; }
set { _created = value; OnPropertyChanged(nameof(Created)); }
}
public string FileOwner
{
get { return _fileOwner; }
set { _fileOwner = value; OnPropertyChanged(nameof(FileOwner)); }
}
public string FullFileSize
{
get { return _fullFileSize; }
set { _fullFileSize = value; OnPropertyChanged(nameof(FullFileSize)); }
}
public string FileImage
{
get { return _fileImage; }
set { _fileImage = value; OnPropertyChanged(nameof(FileImage)); }
}
public string FileSizeType
{
get { return _fileSizeType; }
set { _fileSizeType = value; OnPropertyChanged(nameof(FileSizeType)); }
}
public string FileExtension
{
get { return _fileExtension; }
set { _fileExtension = value; OnPropertyChanged(nameof(FileExtension)); }
}
public string FileIcon
{
get { return _fileIcon; }
set { _fileIcon = value; OnPropertyChanged(nameof(FileIcon)); }
}
public string FileType
{
get { return _fileType; }
set { _fileType = value; OnPropertyChanged(nameof(FileType)); }
}
public decimal FileSize
{
get { return _fileSize; }
set { _fileSize = value; OnPropertyChanged(nameof(FileSize)); }
}
public string FullFileName
{
get { return _fullFileName; }
set { _fullFileName = value; OnPropertyChanged(nameof(FullFileName)); }
}
public string FileName
{
get { return _fileName; }
set { _fileName = value; OnPropertyChanged(nameof(FileName)); }
}
public int Id
{
get { return _id; }
set { _id = value; OnPropertyChanged(nameof(Id)); }
}
#endregion
#region Collections
private List<string> _tempFiles { get; set; }
public List<string> TempFiles { get { return _tempFiles; } set { _tempFiles = value; OnPropertyChanged("TempFiles"); } }
private ObservableCollection<FileModel> _files { get; set; }
public ObservableCollection<FileModel> Files { get { return _files; } set { _files = value; OnPropertyChanged("Files"); } }
#endregion
public StorageViewModel()
{
AddFileCommand = new RelayCommand(AddFile, param=>this.canExecute);
Files = new ObservableCollection<FileModel>(Documentive.GCS.DataAccess.GCSDataAccess.GetDataFromJson());
}
public void AddFile(object o)
{
foreach (var file in TempFiles)
{
Documentive.GCS.DataAccess.GCSDataAccess.AppendDataToFileJson(file);
}
Files.Clear();
Files = Documentive.GCS.DataAccess.GCSDataAccess.GetDataFromJson();
}
}
There is all my codes. I need to update ItemsControl and list new files' data in wrappanel. There is no problem with the FileCard UserControl, I just tried to list data by using label.(just a test)
I don't know if there is a problem with Drag&Drop event and its codes or not.
Thanks.
I think you might actually have a few problems here.
First of all, implement DragOver instead of DragEnter, otherwise you won't be able to drop files into it.
Secondly, get rid of the ScrollViewer, and let ItemsControl manage the scrolling itself. There are a number of reasons for this (not the least of which is virtualization), but just trust me: never put an ItemsControl inside a ScrollViewer.
Next, get rid of the vertical StackPanel. By placing everything in a stack panel you're making your ItemsControl size itself according to the number of items it's already bound to, so dropping files into that big blank area underneath it won't do anything. Set your WrapPanel's background to a color you can actually see, and make sure it's covering the area of the page you think it is. Best way to do that is to use a parent Grid to do layout and then populate that with your Button and ItemsControl, setting your alignment properties accordingly.
The biggest problem with this code though is that your TopMenuBorder_MouseDown handler is updating TempFiles, but your ItemsControl is binding to Files.
I'm using MVVM with WPF and have a RadComboBox in my view that needs to be populated from my County table in my database. My viewmodel is as follows:
public class AddClientViewModel : BindableBase
{
private Client _client;
private Circuit _circuit;
private County _county;
private State _state;
private SubscriberSpecialty _subscriberSpecialty;
private IClientsRepository _repository = new ClientRepository();
private ICircuitRepository _circuitRepository = new CircuitRepository();
private ICountyRepository _countyRepository = new CountyRepository();
private IStateRepository _stateRepository = new StateRepository();
private ISubscriberSpecialty _subscriberSpecialtyRepository = new SubscriberSpecialtyRepository();
public AddClientViewModel()
{
SaveCommand = new RelayCommand(OnSave);
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public Client Client
{
get { return _client; }
set
{
if (value != _client)
{
_client = value;
PropertyChanged(this, new PropertyChangedEventArgs("Client"));
}
}
}
public Circuit Circuit
{
get { return _circuit; }
set
{
if(value != _circuit)
{
_circuit = value;
PropertyChanged(this, new PropertyChangedEventArgs("Circuit"));
}
}
}
public County County
{
get { return _county;}
set
{
if (value != _county)
{
_county = value;
PropertyChanged(this, new PropertyChangedEventArgs("County"));
}
}
}
public State State
{
get { return _state; }
set
{
if (value != _state)
{
_state = value;
PropertyChanged(this, new PropertyChangedEventArgs("State"));
}
}
}
public SubscriberSpecialty SubscriberSpecialty
{
get { return _subscriberSpecialty; }
set
{
if (value != _subscriberSpecialty)
{
_subscriberSpecialty = value;
PropertyChanged(this, new PropertyChangedEventArgs("SubscriberSpecialty"));
}
}
}
public Guid ClientId { get; set; }
public Guid CircuitId { get; set; }
public Guid CountyId { get; set; }
public Guid StateId { get; set; }
public Guid SubscriberSpecialtyId { get; set; }
public ICommand SaveCommand { get; set; }
public event Action<Client> AddClient = delegate { };
public async void LoadClient()
{
Client = await _repository.GetClientAsync(ClientId);
}
public async void LoadCircuit()
{
Circuit = await _circuitRepository.GetCircuitAsync(CircuitId);
}
public async void LoadCounty()
{
County = await _countyRepository.GetCountyAsync(CountyId);
}
public async void LoadState()
{
State = await _stateRepository.GetStateAsync(StateId);
}
public async void LoadSubscriberSpecialty()
{
SubscriberSpecialty = await _subscriberSpecialtyRepository.GetSubscriberSpecialtyAsync(SubscriberSpecialtyId);
}
private void OnAddClient()
{
AddClient(new Client {ClientId = Guid.NewGuid()});
}
private async void OnSave()
{
try
{
Client = await _repository.AddClientAsync(new Client());
}
catch (Exception ex)
{
MessageBox.Show("A handled exception just occurred: " + ex.Message, "Exception", MessageBoxButton.OK,
MessageBoxImage.Warning);
}
}
}
The interface has the following:
Task<County> GetCountyAsync(Guid countyId);
The repository class calls the interface as:
public Task<List<County>> GetCountiesAsync()
{
return _context.Counties.ToListAsync();
}
My view then uses the following syntax:
<telerik:RadComboBox x:Name="Countycombo"
Grid.Column="1" Grid.Row="3"
ItemsSource="{Binding County.CountyName}"
DisplayMemberPath="CountyName" Width="120"/>
I defined a DataContext in the layout as follows:
<UserControl.DataContext>
<viewModels:AddClientViewModel />
</UserControl.DataContext>
When I run the application, the RadComboBox doesn't grab the values from the County table, into which I've loaded several values for CountyName. How do I correct the above code snippets to ensure my County Names are populated?
Update: When I remove County from County.CountyName, I receive the message stating Cannot resolve property CountyName in DataContext MySolution.ViewModels.MyViewModel What additional work is needed in the viewmodel either in LoadCounty or other sections?
I would suggest the following:
Introduce the ViewModel property that will hold a list of County objects:
private List<County> _counties;
public List<County> Counties
{
get { return _counties;}
set
{
if (value != _counties)
{
_counties = value;
PropertyChanged(this, new PropertyChangedEventArgs("Counties"));
}
}
}
Bind a ComboBox ItemsSource to the Counties property, and a ComboBox SelectedItem property to the County property.
<telerik:RadComboBox x:Name="Countycombo"
Grid.Column="1" Grid.Row="3"
ItemsSource="{Binding Counties}"
SelectedItem="{Binding County}"
DisplayMemberPath="CountyName" Width="120"/>
And you need to a place where you will load the counties with a repository call to a GetCountiesAsync. The result should be set to the ViewModel Counties property.
public async void LoadCounties()
{
Counties = await _countyRepository.GetCountiesAsync();
}
Not sure what is the best place to make that call.
I am working on the ability to add and delete rows in an observable collection.
Since the original post I have created a test app that only has the ability to delete rows from the observable collection. I populate the database externally then open it in this just to test the delete function which doesn't work. It executes the RemoveAt line, deletes from the Observable Collection but the view does not update. Here is all my code:
Model:
public class TestModel : ObservableObject
{
#region Properties
private Double id;
public Double ID
{
get { return id; }
set
{
id = value;
RaisePropertyChangedEvent("ID");
}
}
private string type;
public string Type
{
get { return type; }
set
{
type = value;
RaisePropertyChangedEvent("Type");
}
}
private decimal amount;
public decimal Amount
{
get { return amount; }
set
{
amount = value;
RaisePropertyChangedEvent("Amount");
}
}
private string notes;
public string Notes
{
get { return notes; }
set
{
notes = value;
RaisePropertyChangedEvent("Notes");
}
}
#endregion
}
Viewmodel:
public class MainWindowViewModel : ObservableObject
{
#region GetData
public MainWindowViewModel()
{
Transactions = DatabaseFunctions.getTransactionData();
}
#endregion
#region ObservableCollections
private ObservableCollection<TestModel> transactions;
public ObservableCollection<TestModel> Transactions
{
get { return transactions; }
set
{
transactions = value;
RaisePropertyChangedEvent("Transactions");
}
}
#endregion
#region Properties
public static string SharedWith;
private Double id;
public Double ID
{
get { return id; }
set
{
id = value;
RaisePropertyChangedEvent("ID");
}
}
private string type;
public string Type
{
get { return type; }
set
{
type = value;
RaisePropertyChangedEvent("Type");
}
}
private decimal amount;
public decimal Amount
{
get { return amount; }
set
{
amount = value;
RaisePropertyChangedEvent("Amount");
}
}
private string notes;
public string Notes
{
get { return notes; }
set
{
notes = value;
RaisePropertyChangedEvent("Notes");
}
}
#endregion
public void DeleteTransactionRow(List<TestModel> SelectedTransaction, int SelectedIndex)
{
Transactions.RemoveAt(SelectedIndex);
}
View:
<Window x:Class="OCTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:Properties="clr-namespace:OCTest.Properties"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:xcdg="http://schemas.xceed.com/wpf/xaml/datagrid"
Title="Test" SizeToContent="WidthAndHeight"
xmlns:ViewModel="clr-namespace:OCTest.ViewModel">
<Window.DataContext>
<ViewModel:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<xcdg:DataGridControl x:Name="TransactionsDataGrid" Grid.Row="0" ItemsSource="{Binding Transactions, Mode=TwoWay}" AutoCreateColumns="False" SelectionMode="Single">
<xcdg:DataGridControl.ContextMenu>
<ContextMenu>
<MenuItem Header="Delete Row" Click="DeleteTransactionRow_Click"/>
</ContextMenu>
</xcdg:DataGridControl.ContextMenu>
<xcdg:DataGridControl.Columns>
<xcdg:Column Title="Type" FieldName="Type" ReadOnly="True"/>
<xcdg:Column Title="Amount" FieldName="Amount">
<xcdg:Column.CellContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding StringFormat={}{0:C}}"/>
</DataTemplate>
</xcdg:Column.CellContentTemplate>
</xcdg:Column>
<xcdg:Column Title="Notes" FieldName="Notes"/>
</xcdg:DataGridControl.Columns>
</xcdg:DataGridControl>
</Grid>
Code behind that deals with the delete command and passes needed information to the view model:
public partial class MainWindow : Window
{
MainWindowViewModel mainwindowviewmodel = new MainWindowViewModel();
public MainWindow()
{
InitializeComponent();
}
private void DeleteTransactionRow_Click(object sender, RoutedEventArgs e)
{
List<TestModel> selectedtransaction = TransactionsDataGrid.SelectedItems.Cast<TestModel>().ToList();
mainwindowviewmodel.DeleteTransactionRow(selectedtransaction, TransactionsDataGrid.SelectedIndex);
}
private void MouseRightButtonUpHandler(object sender, RoutedEventArgs e)
{
this.TransactionsDataGrid.SelectedItem = ((DataCell)sender).ParentRow.DataContext;
}
}
So hopefully someone can see why the RemoveAt doesn't update the view.
You need to set the DataContext of the MainWindow to your view model:
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
I'd also recommend looking into binding click events to commands in your view model rather than relying on the code behind.
My UI is not updating when more data is added to the ObservableCollection. The console output says A first chance exception of type 'System.NullReferenceException' occurred. Should I be using Inotifycollectionchanged instead? Here is some of the code:
<ListView x:Name="ListView2" ItemsSource="{Binding Source={x:Static d:GrabUserConversationModel._Conversation}, UpdateSourceTrigger=PropertyChanged}" SelectionChanged="ListView1_SelectionChanged">
UserConversationModel.cs
public class UserConversationModel : INotifyPropertyChanged
{
public UserConversationModel()
{
}
public string Name
{ get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string Obj)
{
if (PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(Obj));
}
}
}
MainWindow.xaml.cs
public partial class MainWindow
{
static GrabUserConversationModel grabUserConversationModel;
public MainWindow()
{
InitializeComponent();
...
}
static void AddData()
{
grabUserConversationModel.Conversation.Add(new UserConversationModel { Name = "TestName" });
}
GrabUserConversationModel.cs
class GrabUserConversationModel
{
public static ObservableCollection<UserConversationModel> _Conversation = new ObservableCollection<UserConversationModel>();
public ObservableCollection<UserConversationModel> Conversation
{
get { return _Conversation; }
set { _Conversation = value; }
}
...
your property ObservableCollection<UserConversationModel> Conversation is not implementing the INotifyPropertyChanged
public ObservableCollection<UserConversationModel> Conversation
{
get { return _Conversation; }
set { _Conversation = value; OnPropertyChanged("Conversation");}
}