How to Add CRUD Operations to a Second Entity in WPF - c#

I'm working on a WPF program, I use EF and MVVM. I have two entities : Student and Grade. Each student can have multiple grades. I have one window and one ViewModel(Student). I can add/update/delete student info using the program. When I add grades from database, I can see grades on the screen. I don't know how to modify my program to enable user enter,modify,delete grades on that screen. Can you give me some tips or advices about that? I'm open to new architecture suggestions. Should I add another user control or something like that? Here is my program currently looks like. Thanks.

YOu should start by modeling the viewmodels in a nice way (forget everything about database for now, this has nothing to do with viewmodels & views):
public sealed class MainViewModel : INotifyPropertyChanged
{
public ObservableCollection<StudentViewModel> Students {get; set;}
}
public sealed class StudentViewModel : INotifyPropertyChanged
{
public ObservableCollection<GradeViewModel> Grades {get; set;}
public StudentViewModel(){
Grades = new ObservableCollection<GradeViewModel>();
}
}
public sealed class GradeViewModel : INotifyPropertyChanged
{
public string Name { get; set; }
public int Value { get; set; }
}
Once you've modelled everything nicely, you'll eventually have list of students somewhere.

You should add another view for upgrading grades. I am assuming that your are familiar with how to switch views and viewmodels.
In your second view you should add a ComboBox/ListBox. Populate this with from students table.
In your xaml
<ComboBox ItemsSource={Binding Students} SelectedItem={Binding SelectedStudent} />
In your view model.
public ObservableCollection Students
{
get { return DatabaseContext.Students.ToList(); }
}
And when you select item in combobox, get grades for the selected student
private Student _selectedStudent;
public Student SelectedStudent
{
get { return _selectedStudent; }
set
{
_selectedStudent = value;
GetGradesList();
}
}
private void GetGradesList()
{
var grades = DatabaseContext.Grades.Where(g => g.Student = SelectedStudent);
// populate this to grid view like you have done in your current view.
// And for selected row in the grid you will get values in your textboxes, update those values,
// and then upgrade that to the database.
}
public void UpgradeGrade()
{
var grade = DatabaseContext.Grades.FirstOrDefault(g => g == SelectedGrade);
grade.Value = UpdatedValue;
DatabaseContext.SaveChanges();
}

Related

ObjectlistView Checkbox Issue

I have an issue trying to get a Checkbox working with ObjectListview.
My model looks like this:
public class object
{
public string name {get; set;}
public int age {get; set;}
public bool inuse {get; set;}
}
And I added a FastObjectListView via the Designer in Visual Studio to a Win Forms Application.
Then, I added the Columns and set the AspectName for each column to the Models Property (First column: AspectName: name, Second Column: AspectName: age, Third Column: AspectName: inuse).
Afterwards, I filled the ListView with this:
using (var context = new objectDb())
{
var objectlist = context.objects.ToList();
fastoLV_Clean.SetObjects(objectlist);
fastoLV_Clean.Refresh();
}
That works, and I can see my Database entries in the ListView.
Now I want to add a CheckBox column where someone can check or uncheck the items to delete them and I can not get the checkbox to work.
I have added a Column and set CheckBox to true, changed the CheckedAspectName of the ListView and now I can see the Checkboxes but nothing happens if I click them to check.
I think I'm on the completely wrong track, what do I have to do to make it work?
Thank you very much!!
I don't know a way with the ObjectListView to include any items which are not part of your model.
So then the simple way is to change your model to include a "Delete" property which you can then show in your ObjectListView.
Of course, this is not always possible! Especially if you are dealing with items that are written to/from Database or another persistence layer.
Then the trick is to write a derived class with you model being the base class and then you just add the delete column to this. But then you would need to convert from your Base to a derived class before showing in the ObjectListView.
The following code can help with that.
You keep your columns set-up as you have already done.
Assuming your (now base) class is defined like this
public class MyClass
{
public string name { get; set; }
public int age { get; set; }
public bool inuse { get; set; }
}
Your derived class inherits from this, adds the delete property and a new constructor
public class MySecondClass : MyClass
{
public bool Delete { get; set; }
public MySecondClass(MyClass other)
{
//Copy from MyClass
this.name = other.name;
this.age = other.age;
this.inuse = other.inuse;
//Set default for new properties
this.Delete = false;
}
}
Your code to retrieve the objects and set them then looks like this
using (var context = new objectDb())
{
var objectlist = context.objects.ToList();
//Now we need to convert to the derived class type
var secondlist = list.ConvertAll(x => new MySecondClass(x));
//Then we setobjects using this new list
fastoLV_Clean.SetObjects(secondlist);
}

How to data bind a list of objects with a list property?

I have an object Classroom with a list Student, as shown below.
public class Classroom
{
public int Number
{
get;
set;
}
public List<Student> StudentList
{
get;
set;
}
}
My Student object look like this.
public class Student
{
public string Name
{
get;
set;
}
}
I am using Xamarin Forms to develop a cross platform app that shows a ListView of all the classroom numbers, as a header for the cell, and underneath a list of the students name.
This is my C# for the ListView.ItemSource
List<Classroom> ClassroomList = new List<Classroom>();
ClassroomList.add(new Classroom());
ClassroomList.add(new Classroom());
ClassroomList.add(new Classroom());
MyListView.ItemSource = ClassroomList;
My problem is that each Classroom object has a different amount of students in the list. I don't know how to use data binding in XAML or C# to list all the classroom numbers with a "sub list" of all the students.
Here is an example of what I am looking for
If anyone can help me use my data model to bind the data in my ListView. It would help a lot.
I've been looking all over the place to find the answer and this is the closest answer is I found.

MvvmCross; How to RaisePropertyChange from another ViewModel

I have a ShoppingCart listView with items that is bound to ShopingCartViewModel. When I click to an item it takes me to the ItemInfoFragment which is bound to ItemInfoViewModel.
In ItemInfoFragment I have a button which deletes the item and removes it from the ShoppingCart listview.
My problem is; After i delete the item and press backbutton to return to my previously activity, the ShoppingCart listView still shows the Item that I deleted.
My Question is; How to RaisePropertyChange in ShoppingCartViewModel when i exit the ItemInfoFragment?
I believe you have a few options:
Shared Persistent Storage
If you use a storage/caching solution like SQLite or Realm etc. Which can be used to read and modify the same shopping cart data between pages. You can then use view life cycle events (OnResume[Android] or ViewWillAppear[iOS]) to retrieve the latest from the cache.
Alternatively if the shopping cart data size is small you could read/write it to MvvmCross Settings Plugin. You will just have to serialize and deserialize your objects as you can only save basic types like strings, bools, int etc.
Dependency Injection Shared Instance
You can create an in memory cache via using a shared class instance the can be shared between multiple ViewModels. This classes properties can bind directly to your various views. Any changes to the list will update all views that bind to it. One thing to note is that you will have to manually handle clean up if you require the memory space occupied by the this instance class.
Example:
Example model
public class ItemInfo
{
public int Id { get; set; }
public string Name { get; set; }
public double Price { get; set; }
}
Shared class instance and interface
public interface ISharedShoppingCart
{
MvxObservableCollection<ItemInfo> ShoppingCartItems { get; set; }
}
public class SharedShoppingCart : MvxNotifyPropertyChanged, ISharedShoppingCart
{
MvxObservableCollection<ItemInfo> _shoppingCartItems;
public MvxObservableCollection<ItemInfo> ShoppingCartItems
{
get { return _shoppingCartItems; }
set { SetProperty(ref _shoppingCartItems, value); }
}
}
Make sure to register the class and interface
public class App : MvxApplication
{
public override void Initialize()
{
/* Other registerations*/
Mvx.LazyConstructAndRegisterSingleton<ISharedShoppingCart, SharedShoppingCart>();
}
}
Example usage in shared ViewModels
public class ShopingCartViewModel : MvxViewModel
{
readonly ISharedShoppingCart _sharedShoppingChart;
public ShopingCartViewModel(ISharedShoppingCart sharedShoppingChart)
{
_sharedShoppingChart = sharedShoppingChart;
}
public MvxObservableCollection<ItemInfo> ShoppingCartItems
{
get { return _sharedShoppingChart.ShoppingCartItems; }
set { _sharedShoppingChart.ShoppingCartItems = value; }
}
}
public class ItemInfoViewModel : MvxViewModel
{
readonly ISharedShoppingCart _sharedShoppingCart;
public ItemInfoViewModel(ISharedShoppingCart sharedShoppingCart)
{
_sharedShoppingCart = sharedShoppingCart;
}
void RemoveItemFromCart(int id)
{
_sharedShoppingCart.ShoppingCartItems
.Remove(_sharedShoppingCart.ShoppingCartItems.Single(x => x.Id == id));
}
}
Pub/Sub
You could send messages back to the shopping cart ViewModel using the MvvmCross Messenger Plugin.

Direct binding between UI and Model issue within MVVM application

I have a WPF application with MVVM.As I understood, the main goal of MVVM is to separate between logic layer and UI layer.
I have this Model class :
public class User
{
public string Login{get;set;}
public string Pwd{get;set;}
public List<User> GetUsers()
{
//
}
}
in my ViewModel, I instanciate a User object and an ObservableCollection of User
public class UserVM
{
public User _User{get;set;}
public ObservableCollection<User> liste{get; private set;}
public UserVM()
{
_User = new User("TODO","PWD2");
liste = new ObservableCollection(_User.GetUsers);
}
}
I feel that I bind directly a UI properties to a model object,So I need To know :
When I bind UI properties to the object _User properties, did I respect the MVVM architecture?
When I bind a listview datasource to liste, did I respect the MVVM architecture?
For the first question, if it is not suitable for MVVM, is it better to expose the model's properties instead of declaring the class?
For the second question, if it is not suitable for MVVM, How can I fix it ?
Thanks,
It looks like your User class has a tree-like structure in that it contains a List of User objects which themselves may contain a List of User objects...
The problem here is that your view model class contains User objects. Only the UserVM model would contain an ObservableCollection for example.
A simple fix would be: EDIT user.GetUsers() doesn't return a List<UserVM>
public class UserVM
{
public string Login { get; set; }
public string Pwd { get; set; }
public ObservableCollection<UserVM> Users { get; private set; }
public UserVM(User user)
{
Login = user.Login;
Pwd = user.Pwd;
Users = new ObservableCollection<UserViewModel>(
user.GetUsers().Select(subUser => new UserViewModel(subUser)));
}
}
You may also want to implement INotifyPropertyChanged so that the view gets notifications that the view model has changed.

WPF MVVM design ViewModel

I have the following model:
public class Person
{
public string LastName{get;set;}
public City City {get;set;}
}
public class City
{
public string Name {get;set;}
}
I have two Views:
One for display all Persons with LastName and the Name of the city in
a DataGrid(AllPersonsViewModel)
One for adding a new Person (PersonViewModel)
My AllPersonsViewModel:
public class AllPersonViewModel : ViewModel
{
public ObservableCollection<PersonViewModel> PersonViewModels {get;set;}
}
I started with the following PersonViewModel:
public class PersonViewModel : ViewModel
{
private Person _person;
public string Name
{
get { return _person.Name;}
set { _person.Name = value; RaisePropertyChange("Name");}
}
public string CityName
{
get { return _person.City.Name;}
}
}
Then I added the properties for adding a new Person. In the View I need a Textbox for the PersonName and a Combobox for selection of a City:
public class PersonViewModel : ViewModel
{
private Person _person;
public string Name
{
get { return _person.Name;}
set { _person.Name = value; RaisePropertyChange("Name");}
}
public string CityName
{
get { return _person.City.Name;}
}
public City SelectedCity
{
get { return _person.City;}
set { _person.City = value; RaisePropertyChange("SelectedCity");}
}
public ObservableCollection<City> Cities {get;set;}
}
Is this the right approach? It seems a little bit redundant to me. In the Grid of AllPersonsView I could also bind directly to the "SelectedCity.Name" instead of the extra property CityName. The grid is also readonly.
you have multiple problems;
1 - you do not need to declare an observable collection of viewmodels in AllPersonViewModel. Just declare an ObservableCollection of Person.
2 - do not add the CityName property; not needed as you have stated.
3- do not add the Name property. Bind the textbox to Name property of the Person.
Your question really boils down "is it OK to expose my model directly to the view?" Some purist will say no while other will say that having a view model that wraps a model without adding any new functionality is redundant.
In my opinion it depends on the task at hand but "skiping" a view model may come back and bite you later when you need to add additional state that doesn't belong in the model. If in doubt use a view model but for instance when exposing simple model objects in a list you often don't need the extra layer the view model provides.
In your case you have opted for the "purist" solution and because your model object doesn't support INotifyPropertyChanged you can't get rid of the view model if a model property is changed by multiple sources. But instead of providing a CityName property you could bind to SelectedCity.Name. WPF supports property navigation in data binding expressions.
For more insight into this topic you can google mvvm expose model.

Categories

Resources