how to databind the datagrid Mvvm - c#

Kinda stuck on this mvvm binding the datagrid with my viewmodel im making a ObservableCollection and then pass the dummy data into the gridview but im not sure how to do it I started doing it without mvvm and had no problem now im kinda stuck and unsure how to do it
this is my View - UserView.xaml
Grid>
<DataGrid x:Name="dt_Users" ItemsSource="{Binding UserItems}" HorizontalAlignment="Left" Height="100" Margin="161,222,0,0" VerticalAlignment="Top" Width="585" />
</Grid>
My viewmodel - userviewmodel.cs
public class UserViewModel: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
ObservableCollection<User> UserItems = new ObservableCollection<User>();
private void fetchgrid()
{
UserItems.Add(new User { firstname = "h", lastname = "h" });
UserItems.Add(new User { firstname = "h", lastname = "h" });
}
}
and my model - user.cs
public class User
{
public string firstname { get; set; }
public string lastname { get; set; }
}
what am I overseeing?

Make sure to assign an instance of UserViewModel to the DataContext of your view.
Either in XAML, for example:
<Window>
<Window.DataContext>
<UserViewModel />
</Window.DataContext>
<Grid>
<DataGrid ... />
</Grid>
</Window>
or in code-behind, for example in the constructor:
public MainWindow()
{
InitializeComponent();
this.DataContext = new UserViewModel();
}
Alternatively define the instance of the view model in App.xaml and use {StaticResource} to set the DataContext in XAML.
There are many ways to do it.

You can only bind to public properties so the first thing to do is to change the UserItems field into a property in the view model:
public class UserViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<User> UserItems { get; } = new ObservableCollection<User>();
...
}
Then you also need to set the DataContext of the view to an instance of your view model class. The easiest way to do this is to add the following line to the constructor of the view in UserView.xaml.cs:
public UserView()
{
InitializeComponent();
DataContext = new UserViewModel(); //<-- add this
}

Related

How to bind simple string object to a Label Text in Xamarin Forms?

I am new in Xamarin development, so please bear if the question seems too simple. I am having a simple single string object in my C# code (Code behind). I want to bind it to a Label in XAML so that whenever the string changes, it reflects in XAML Page.
Here is my C# code
public string Name { get; set; }
public HomePage()
{
InitializeComponent();
BindingContext = this;
Name = "John";
}
Here is my XAML code
<Label Text="{Binding Name}" />
How can I do it. Am I doing anything wrong?
It is important you learn about MVVM pattern and how to perform the data binding. You can see this link: https://learn.microsoft.com/en-us/xamarin/xamarin-forms/xaml/xaml-basics/data-bindings-to-mvvm.
Basically, you can do this:
Create a ViewModel for your HomePage.
public class HomePageViewModel : INotifyPropertyChanged
{
private string name;
public string Name
{
get
{
return name;
}
set
{
name = value;
OnPropertyChanged(nameof(Name));
}
}
public HomePageViewModel()
{
// some initialization code here ...
Name = "John";
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Attach now your ViewModel to the HomePage View
public HomePageView()
{
InitializeComponent();
BindingContext = new HomePageViewModel();
}
Then in your XAML, you can have your binding like this:
<Label Text="{Binding Name}" />
Then whenever the Name changes in the ViewModel, it will be reflected in the XAML view.

How do I use the same ViewModel for two views?

So I have my MainWindow.xaml which has the DataContext of BaseViewModel set like this.
<Window.DataContext>
<viewModel:BaseViewModel/>
</Window.DataContext>
it works fine, for instance when I select an item in my ListView it binds and updates my ImageView in my MainWindow.
<ListView Background="Transparent"
ItemsSource="{Binding ImageGridViewModel.Images}"
SelectedItem="{Binding ImageGridViewModel.SelectedImage}">
And then it updates like so
<Image Source="{Binding ImageGridViewModel.SelectedImage}"
Margin="20">
Perfect no issues.
However, I recently added a second view which is a Window called WatermarkWindow and I set the DataContext just like I did with the MainWindow, in the XAML like so.
<Window.DataContext>
<viewModel:BaseViewModel/>
</Window.DataContext>
And then the binding for the Image control on that new Window
<Image Source="{Binding ImageGridViewModel.SelectedImage}"
Margin="20">
However when I open that window, the Image control's source is not bound to the property, the property actually returns NULL and I think I know why that is, I think it's because in my BaseViewModel I am instantiating a new instance of that ViewModel evertime it gets called.
The reason to why I am doing it that way is because I wanted to instantiate a instance of it so I can actually use it to bind stuff. Rather than it being null.
If that's not the issue then I'm still really eager to learn and understand what the issue is.
What's the proper way of setting up a BaseViewModel that contains all the extra ViewModels?
public class BaseViewModel : ObservableObject
{
public ImageGridViewModel ImageGridViewModel { get; set; } = new ImageGridViewModel();
}
And the ObservableObject
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
And not that it matters because I know the properties work fine, here's the ViewModel.
public class ImageGridViewModel
{
public string ImagePath { get; set; }
public string SelectedImage { get; set; }
public ObservableCollection<string> Images { get; set; }
...
What I ended up doing was setting the DataContext property to this where I instantiate the new WawtermarkWindow
if (wmw == null)
{
wmw = new WatermarkWindow();
wmw.DataContext = this;
}

Why does my application enter break mode when I bind to a collection?

I am currently trying to bind the ItemSource of my ItemsControl but for some reason it's throwing an issue saying that the application has entered break mode and I have no idea what the cause is, I really want to understand why it's entering breakmode, I tried debugging but it didn't really get me very far.
The goal was to create a custom UserControl and then being able to add them to a ObservableCollectionwith a button click. So creating a new one when the button has been clicked, unfortunatly I didn't get that far because this started happening.
So my question is, Why is it throwing that issue, is it something where it doesnt like the binding?
<ItemsControl ItemsSource="{Binding UserViewModel.Users}">
<controls:UserCard/>
</ItemsControl>
And I've setup the DataContext like so
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new BaseViewModel();
}
}
And for the BaseViewModel, it looks like this
public class BaseViewModel : ObservableObject
{
public UserViewModel UserViewModel { get; set; } = new UserViewModel();
}
And the UserViewModel looks like this
public class UserViewModel : ObservableObject
{
public ObservableCollection<User> Users { get; set; } = new ObservableCollection<User>();
public UserViewModel()
{
}
}
With an ObservableObject like so
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
The UserCard control goes in the ItemTemplate of the ItemsControl:
<ItemsControl ItemsSource="{Binding UserViewModel.Users}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<controls:UserCard/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

Binding not working properly in treeview WPF

I have an Employee Class as shown below:
public class Employee : INotifyPropertyChanged
{
public Employee()
{
_subEmployee = new ObservableCollection<Employee>();
}
public string Name { get; set; }
public ObservableCollection<Employee> SubEmployee
{
get { return _subEmployee; }
set
{
_subEmployee = value;
NotifiyPropertyChanged("SubEmployee");
}
}
ObservableCollection<Employee> _subEmployee;
public event PropertyChangedEventHandler PropertyChanged;
void NotifiyPropertyChanged(string property)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
I am creating a collection of employee class in Main window constructor
and adding it to an observable collection of employee as shown below:
public partial class MainWindow : Window
{
public ObservableCollection<Employee> Emp { get; private set; }
public MainWindow()
{
InitializeComponent();
Emp = new ObservableCollection<Employee>();
Emp.Add(new Employee(){Name = "Anuj"});
Emp.Add(new Employee() { Name = "Deepak" });
Emp.Add(new Employee() { Name = "Aarti" });
Emp[0].SubEmployee.Add(new Employee(){Name = "Tonu"});
Emp[0].SubEmployee.Add(new Employee() { Name = "Monu" });
Emp[0].SubEmployee.Add(new Employee() { Name = "Sonu" });
Emp[2].SubEmployee.Add(new Employee() { Name = "Harsh" });
Emp[2].SubEmployee.Add(new Employee() { Name = "Rahul" });
Emp[2].SubEmployee.Add(new Employee() { Name = "Sachin" });
this.DataContext = this;
}
}
I have set the DataContext as self.
Now, in xaml file I have created a hierarchical template of treeview and binded data as shown below:
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TreeView ItemsSource="{Binding}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding SubEmployee}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
</Window>
Now when I keep, TreeView ItemsSource="{Binding Emp}" , binding works properly
and I can see the tree view structure after running the code.
However when I keep TreeView ItemsSource="{Binding}", I see no result after running the code.
To my understanding, keeping ItemSource = "{Binding}" means I am binding to the evaluated value of the current datacontext.
As my datacontext is set to self, ItemSource = "{Binding}" should mean I am binding to the only property of DataContext i.e. Emp and I should get proper result.
Please help me in understanding the problem I am getting in keeping binding as
ItemSource = "{Binding}".
"To my understanding, keeping ItemSource = "{Binding}" means I am binding to the evaluated value of the current datacontext."
Correct AND that is the issue. ItemsSource expects the binding source to be of type IEnumerable but you are binding to Window.
"...should mean I am binding to the only property of DataContext i.e. Emp and I should get proper result."
No. No such "single property" assumption exists in WPFs binding conventions.
Change...
this.DataContext = this;
To...
this.DataContext = Emp;
Or, alternatively, change binding in XAML and specify the correct member on the DataContext to bind to using Path...
<TreeView ItemsSource="{Binding Path=Emp}">

Moving behind code to a viewModel class binding

After learning about ObservableCollection and INotifyPropertyChanged, I'm trying use them to divide my code into MVVM.
But I'm having some trouble with binding outside of code-behind class.
My app have three boxes that let you input a person's name, income, age. Then it will display them on a DataGrid.
xaml:
<Window x:Class="myApp.MainWindow"
[...]
<Grid>
<DataGrid x:Name="peopleDisplay">
</DataGrid>
</Grid>
</Window>
in MainWindow.xaml.cs (no structure)
public partial class MainWindow : Window
{
private ObservableCollection<Person> peopleList = new ObservableCollection<Person>();
public MainWindow()
{
InitializeComponent();
peopleDisplay.ItemsSource = peopleList;
}
private void btnAddProduct_Click(object sender, RoutedEventArgs e)
{
peopleList.Add(new Person { personName = nameBox.text, income = incomebox.text, age = ageBox.text });
}
[...]
}
class People : INotifyPropertyChanged
{
private string personName;
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
public string PersonName {
get
{
return this.personName;
}
set
{
if( this.personName != value)
{
this.PersonName = value;
this.NotifyPropertyChanged("PersonName");
}
}
}
public int age { get; set; }
public double income { get; set; }
}
My main questions:
so now Im trying to do two things: add a new function that will calculate the total income of everyone, move the ObservableCollection above to a viewModel class
now in the new viewModel class I have the ObservableCollection personList (instead of inside behind code), but is it wrong to put the calculation method and the properties here too? If I put the calculation properties here this viewModel will be inheriting INotifyPropertyChanged, so when a the totalIncome properties changes it will change the UI automatically. it makes no sense to put it in the person model though, cause that class represent one person.
How do I bind this people List in viewModel to the xaml? If the list is in code-behind I can just do peopleDisplay.ItemsSource = peopleList;, but this viewModel is a class and not a ObservableCollection object, I cant set it to the dataGrid's ItemsSource. Is there a way to bind it in the viewModel class? Im in the progress of learning mvvm so I might be doing something wrong here too. Please advice
Your Model class is People. like below:
public class People : INotifyPropertyChanged
{
private string personName;
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public string PersonName
{
get
{
return this.personName;
}
set
{
if( this.personName != value)
{
this.PersonName = value;
this.NotifyPropertyChanged();
}
}
}
public int Age { get; set; }
public double Income { get; set; }
}
Your ViewModel like below:
public class PeopleViewModel
{
Public List<People> ListOfPeople { get; set; }
}
ViewModel can implement INotifyPropertyChanged interface to Notify the View.
Now you can set the data context as PeopleViewModel and bind your ListOfPeople to your DataGrid.
Set DataContext for your View you can do it from XAML or code behind.
Set ItemsSource for your DataGrid in your View .
XAML:
<Window x:Class="myApp.MainWindow" DataContext="{Binding PeopleViewModel }">
<Grid>
<DataGrid x:Name="peopleDisplay" ItemSource={Binding ListOfPeople}>
......
</DataGrid>
</Grid>
</Window>
Reference 1
Reference 2
1) I dont see any problem with your approach, but, what would happen if someday you want to test the method that calculate the "TotalIncome"? You could separate the calculation in an helper class.
2) First of all, you have to expose the collection in your ViewModel, using public properties. With that being said, you have to declare the binding in your xaml file.
<DataGrid x:Name="peopleDisplay"
ItemsSource="{Binding MyPropertyOnViewModel}">
</DataGrid>
Dont forget to set the DataContext of your window with your viewmodel.

Categories