wpf mvvm bound view to model - wpf

I am trying to work MVVM. I created a model with 3 properties (ModelClass), and in my view model i have an observable collection of ModelClasses. In the view i bind a list to the observable collection in my VM. the list is a template of 3 textboxes.
how can i bind the textbox to a specific property in my model?
say the model class is called Person and has name, age and country as properties, the view model has a collection of persons, the view has a list bound to the person list (itemssource..). i don't know how to make a textbox in list bound to name, age and country of the person model.

As per your query,i build the sample app and it's perfectly working for me.Details are furnished below.
PersonViewModel.cs
internal class PersonViewModel{
public ObservableCollection<PersonModel> PersonCollection { get; set; }
public PersonViewModel()
{
//This data will load as the default person from the model attached to the view
PersonCollection = new ObservableCollection<PersonModel>();
PersonCollection.Add(new PersonModel { Name = "John", Age= 24, Country = "Canada"});
PersonCollection.Add(new PersonModel { Name = "David", Age = 25, Country = "United States"});
PersonCollection.Add(new PersonModel { Name = "Prajin", Age = 28, Country = "Japan"});
}
}
PersonView.xaml
<Window x:Class="MVVM.PersonView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MVVM Demostration" Height="297" Width="480"
xmlns:local="clr-namespace:MVVM.ViewModel">
<Window.DataContext>
<local:PersonViewModel />
</Window.DataContext>
<Grid Margin="10">
<ListBox ItemsSource="{Binding PersonCollection}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Path=Name}" Margin="5" Grid.Column="0" FontSize="14" />
<TextBlock Text="{Binding Path=Age}" Margin="5" Grid.Column="1" FontSize="14" />
<TextBlock Text="{Binding Path=Country}" Margin="5" Grid.Column="2" FontSize="14"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
Person.cs
internal class PersonModel : System.ComponentModel.INotifyPropertyChanged
{
private string name;
public string Name
{
get { return name; }
set
{
name = value;
OnPropertyChanged("Name");
}
}
private int age;
public int Age
{
get { return age; }
set
{
age = value;
OnPropertyChanged("Age");
}
}
private string country;
public string Country
{
get { return country; }
set
{
country = value;
OnPropertyChanged("Country");
}
}
#region INotifyPropertyChanged Members
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
#endregion
}

You need to change the ItemTemplate of the list to bind the the properties:
<ListView ItemsSource="{Binding PersonsList}" >
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}" VerticalAlignment="Center" FontSize="14" />
<TextBlock Text="{Binding Path=Age}" VerticalAlignment="Center" FontSize="14" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Create a ListBox and bind your collection of objects to its ItemsSource. Here, Persons is the collection from your ViewModel.
<ListBox ItemsSource="{Binding Persons}" />
Then create a DataTemplate for your object type.
<DataTemplate DataType="{x:Type local:Person} >
<TextBox Text="{Binding Name}"/>
<TextBox Text="{Binding Age}"/>
etc...
</DataTemplate>
Where "local" is an xml namespace alias for the namespace containing the Person type.
Put this DataTemplate in your window's resources (Window.Resources).

Related

How to update a listview from viewmodel binded model observable collection to itemsource

I have a list view, which further contains textbox in datatemplate having columns Name, Address, Country.
Now this listview's Itemsource is binded to observable collection of a model in my viewmodel class.
I am updating the ObjModel (of typeObservableCollection<Model>) by making name and address empty, on some condition in VM, and i can see the values of name, empty in my ObjModel object in viewmodel class.
But those changes are not reflected in UI (ListView), Am i missing something , How to update the listview.
My View is something like this:
<DataTemplate x:Key="EquipmentItemTemplate">
<Grid
x:Name="ListItem"
Height="40"
ZIndex="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="250" />
<ColumnDefinition Width="250" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Path=Name}" />
<TextBlock Grid.Column="1" Text="{Binding Path=Address}" />
<TextBlock Grid.Column="2" Text="{Binding Path=Country}" />
</Grid>
</DataTemplate>
<ListView x:Name="MaintenanceElements"
ItemContainerStyle="{StaticResource SequenceListItemStyle}"
ItemTemplate="{StaticResource EquipmentItemTemplate}"
ItemsSource="{Binding EquipmentMaintenanceEntityItemsCollection}"
SelectedItem="{Binding SelectedMaintenanceElement}">
<ListView.View>
<GridView AllowsColumnReorder="False">
<GridViewColumn
Width="200"
local:GridViewSort.PropertyName="Name"
Header="Name" />
<GridViewColumn
Width="250"
local:GridViewSort.PropertyName="Address"
Header="Address" />
<GridViewColumn
Width="250"
local:GridViewSort.PropertyName="Country"
Header="Country" />
</GridView>
</ListView.View>
</ListView>
View Model contains:
public ObservableCollection<Model> ObjModel { get; set; }
Some where on some condition i do
ObjModel[0].Name= string.Empty;
It do not update in ListView because its itemsource is binded to Model object observable collection, how to update ListView from here?
My model is:
public class EquipmentMaintenanceModel : ChangeTracker, INotifyPropertyChanged
{
private string name;
private string address;
private string country;
public string Name
{
get { return this.name; }
set { this.name = value; }
}
public string Address
{
get { return this.address; }
set { this.address = value; }
}
public string Country
{
get { return this.country; }
set { this.country = value; }
}
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
In your model, you need to fire PropertyChanged when you set the value of a property. eg:
public string Name
{
get { return this.name; }
set
{
if(this.name != value)
{
this.name = value;
OnPropertyChanged(Name);
}
}
}

How to bind each button's image source from ListView to individual item from observable collection?

I have a Listview of Buttons like this:
<ListView ItemsSource="{Binding TestList}">
<ListViewItem >
<ListView.ItemTemplate>
<DataTemplate>
<Button Name="test" Grid.Row="0" Grid.Column="10" Grid.ColumnSpan="4" Grid.RowSpan="4" VerticalAlignment="Center" Background="Transparent">
<Button.Template>
<ControlTemplate>
<Grid>
<Image Source="?????????????????????????????????????????????"/>
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
</ListView.ItemTemplate>
And the code behind like this:
public class CodeBehind
{
private ObservableCollection<string> testList;
public ObservableCollection<string> TestList
{
get { return testList; }
set
{
testList = value;
}
}
public CodeBehind()
{
dummyModelList = new ObservableCollection<string>() { "/Assets/Image1", "/Assets/Image2", "/Assets/Image3"};
}
}
How to bind each button's image source to individual item from observable collection? I want to do this only in XAML.
What you are looking for is ItemTemplate (Gets or sets the DataTemplate used to display each item.) instead of buttons' template itself.
You use the ItemTemplate to specify the visualization of the data objects. If your ItemsControl is bound to a collection object and you do not provide specific display instructions using a DataTemplate, the resulting UI of each item is a string representation of each object in the underlying collection.
So when you set the ItemTemplate to an instance of DataTemplate, the DataTemplate will be used to render the items. The DataContext of the DataTemplate will be implicitly set to individual items in the bound collection, so you can bind the Image.Source to the DataContext itself using {Binding .} or {Binding}
<ListView ItemsSource="{Binding TestList}">
<ListView.ItemTemplate>
<DataTemplate>
<Button Name="test" Grid.Row="0" Grid.Column="10" Grid.ColumnSpan="4" Grid.RowSpan="4" VerticalAlignment="Center" Background="Transparent">
<Image Source="{Binding .}" />
</Button>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
You should create a DataTemplate to show any controls in your ItemTemplate of ListView:
<ListView Name="listView">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Margin="2">
<Grid.RowDefinitions>
<RowDefinition Height="Auto*"/>
<RowDefinition Height="Auto*"/>
</Grid.RowDefinitions>
<Button>
<Image Source="{Binding AddressImage}" Width="20" Grid.Row="0" />
</Button>
<TextBlock Grid.Row="1" TextAlignment="Left" FontWeight="Light"
VerticalAlignment="Center" Text="{Binding UserName}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
Model:
public class Person
{
public int IdUser {get; set;}
public string UserName {get; set;}
public string AddressImage {get; set;}
}
code-behind:
public MainWindow()
{
PopulateCollection();
}
private void PopulateCollection()
{
ObservableCollection<Person> personColl = new ObservableCollection<Person>();
for (int i = 0; i <= 10; i++)
{
//here you can set any image what you want
personColl.Add(new Person() { IdUser=i, UserName="I am " + i.ToString(),
AddressImage="Assets/1.png"});
//Assets is a folder with images
}
listView.ItemsSource=personColl;
}

Observablecollection<T> in Listbox is not updating

I have a Silverlight Listbox bound to Observablecollection which shows up fine (first time) but when I try to update it through the code behind, the change does not reflect in UI. I have used MVVM pattern . Pl have a look and at view and viewmodel.
<UserControl x:Class="GridOperations.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:GridOperations.ViewModel"
mc:Ignorable="d"
d:DesignHeight="700" d:DesignWidth="700">
<UserControl.DataContext>
<local:ShowUserListViewModel/>
</UserControl.DataContext>
<UserControl.Resources>
<local:ShowUserListViewModel x:Key="viewModelKey"/>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<ListBox VerticalAlignment="Top" Margin="0,20,0,0" x:Name="lstBox" ItemsSource="{Binding UserList}" Width="700" Height="150" Background="AliceBlue">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<TextBlock Margin="30,0,0,0" Text="{Binding UserId}" HorizontalAlignment="Left"/>
<TextBlock Margin="30,0,0,0" Text="{Binding Path=UserName, Mode=TwoWay}" Grid.Column="1" HorizontalAlignment="Left"/>
<Button Margin="30,0,0,0" Grid.Column="2" Content="Edit" Height="20" HorizontalAlignment="Left" Command="{Binding Source={StaticResource viewModelKey}, Path=UpdateUserCommand}" />
<Button Margin="10,0,0,0" Grid.Column="3" Content="Del" Height="20" HorizontalAlignment="Left" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>
public class ShowUserListViewModel : INotifyPropertyChanged
{
public ShowUserListViewModel()
{
mUserList = new ObservableCollection<User>();
mUserList.Add(new User() { UserId = Guid.NewGuid().ToString(), UserName = "testuser1", IsAdmin = true });
mUserList.Add(new User() { UserId = Guid.NewGuid().ToString(), UserName = "testuser2", IsAdmin = true });
this.UpdateUserCommand = new DelegateCommand(this.UpdateUser);
}
public ICommand UpdateUserCommand { get; private set; }
private ObservableCollection<User> mUserList;
public ObservableCollection<User> UserList
{
get
{
return mUserList;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void UpdateUser()
{
this.UserList.Add(new User() { UserId = Guid.NewGuid().ToString(), UserName = "test", IsAdmin = false });
}
}
Firstly, you should avoid putting a collection property writable. IE, replace this code :
private ObservableCollection<User> mUsers = new ObservableCollection<User>();
public ObservableCollection<User> Users
{
get
{
return mUsers;
}
set
{
mUsers = value;
RaisePropertyChangedEvent("Users");
}
}
by this code :
private ObservableCollection<User> mUsers = new ObservableCollection<User>();
public ObservableCollection<User> Users
{
get
{
return mUsers;
}
}
Then, when you add an item to the collection, you don't have to tell the collection has changed (the collection property hasn't changed in itself), but it's the responsibility of the collection itself (via INotifyCollectionChanged) :
So, the "onclick" method can be simply like this:
private void OnClick(User userInstanceToEdit)
{
this.Users.Add(new User() { UserId = Guid.NewGuid().ToString(), UserName = "test", IsAdmin = false });
}
Finally, I would replace the ListBox.ItemsSource declaration with this one, as two ways is not required :
<ListBox x:Name="lstBox" ItemsSource="{Binding Users}"
Got a hint from this post Stackoverflow
Basically I need to use Datacontext with Listbox control (even after setting the Datacontext at UserControl level). The only line that required change is:
<ListBox x:Name="lstBox" ItemsSource="{Binding Users}" DataContext="{StaticResource viewModelKey}" Width="750" Background="AliceBlue">

How to read all textbox values(Map to database fileds) and store them in Database in WPF - MVVM

I am new to WPF and MVVM. After a long invention i got to know how to retrieve data from database and bind it to a itemcontrol/listview/gridview.
But my problem i am not getting how to read bunch of textbox values and store as a new record in database.
Here is my sample code..
View
<ItemsControl ItemsSource="{Binding AllEmployees}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBox Width="100" Text="{Binding FirstName}" Margin="4"/>
<TextBox Width="100" Text="{Binding LastName}" Margin="4"/>
<TextBox Width="100" Text="{Binding Age}" Margin="4"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!-- For new Employee Details -->
<StackPanel>
<TextBox x:Name="FirstName"/>
<TextBox x:Name="LastName"/>
<TextBox x:Name="Age"/>
<Button Content="New" Command="{Binding NewEmployeeCommand}"/>
</StackPanel>
My cs file is
public ObservableCollection<DataAccess.Employee> AllEmployees
{
get;
private set;
}
public EmployeeListViewModel(EmployeeRepository employeeRepository)
{
if (employeeRepository == null)
{
throw new ArgumentNullException("employeeRepository");
}
_employeeRepository = employeeRepository;
this.AllEmployees = new ObservableCollection<DataAccess.Employee>
(_employeeRepository.ListAll());
}
Now how could i store a new employee Firstname, Lastname, Age in database by reading those text boxes..
How to write function for NewEmployeeCommand event to read the textboxes( mapping of textboxes to appropriate datafileds in database) and store the data in database.
Thanks a Lot !
if you're trying to use MVVM just need to:
Create your ViewModel to contain all the properties your View needs
Bind to those properties in Xaml
For example:
public class EmployeeListViewModel
{
public ObservableCollection<Employee> AllEmployees {get;private set;}
public string FirstName {get;set;}
public string LastName {get;set;}
public int? Age {get;set;}
public ICommand NewEmployeeCommand {get;set;}
//You need to connect to this method by using a Delegate/RelayCommand see link below
public void AddNewEmployee()
{
//Add your real code here to actually insert into the db
var result = InsertEmployeeIntoDatabase(FirstName,LastName,Age);
//You probably want to add this new employee to the list now ;)
AllEmployees.Add(result);
//Now you probably want to reset your fields
FirstName = null;
LastName = null;
Age = null;
}
}
Click here for an implementation of a delegate command
And then just edit your xaml like this:
<ItemsControl ItemsSource="{Binding AllEmployees}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBox Width="100" Text="{Binding FirstName}" Margin="4"/>
<TextBox Width="100" Text="{Binding LastName}" Margin="4"/>
<TextBox Width="100" Text="{Binding Age}" Margin="4"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!-- For new Employee Details -->
<StackPanel>
<TextBox Text={Binding FirstName}"/>
<TextBox Text={Binding LastName}"/>
<TextBox Text={Binding Age}"/>
<Button Content="New" Command="{Binding NewEmployeeCommand}"/>
</StackPanel>
You could pass references in the command parameter:
<StackPanel>
<TextBox x:Name="FirstName"/>
<TextBox x:Name="LastName"/>
<Button Content="New" Command="{Binding NewEmployeeCommand}">
<Button.CommandParameter>
<x:Array Type="{x:Type TextBox}">
<x:Reference Name="FirstName"/>
<x:Reference Name="LastName"/>
</x:Array>
</Button.CommandParameter>
</Button>
</StackPanel>
Depending on what kind of event you use you case the parameter and get the values:
TextBox[] textBoxes = e.Parameter as TextBox[]; //RoutedEvent
TextBox[] textBoxes = parameter as TextBox[]; //If the executed handler provides the parameter
string firstName = textBoxes[0].Text;
string lastName = textBoxes[1].Text;
//create entry; store in DB
Via binding:
<Button.CommandParameter>
<local:MyEntry FirstName="{Binding ElementName=FirstName, Path=Text}"
LastName="{Binding ElementName=LastName, Path=Text}"/>
</Button.CommandParameter>
MyEntry entry = parameter as MyEntry;
//store in DB
you dont read the Textbox values. you need a NewEmployeeViewModel and bind the TextBoxes to the properties.
EDIT:
just create a class with INotifyPropertyChanged and the Properties you need.
public class NewEmployee : INotifyPropertyChanged
{
public string FirstName
{
get{return this._firstname;}
set{this._firstname = value;
OnPropertyChanged("FirstName");}
}
//... other properties
}
xaml
<StackPanel DataContext={Binding MyNewEmployeeProperty}>
<TextBox x:Name="FirstName" Text={Binding FirstName}/>
<TextBox x:Name="LastName" Text={Binding LastName}/>
<TextBox x:Name="Age" Text={Binding Age}/>
<Button Content="New" Command="{Binding NewEmployeeCommand}"/>
</StackPanel>
I got the Correct answer.
My New entry form in xmal Should be
<StackPanel>
<TextBox Text={Binding Employee.FirstName}"/>
<TextBox Text={Binding Employee.LastName}"/>
<TextBox Text={Binding Employee.Age}"/>
<Button Content="New" Command="{Binding NewEmployeeCommand}"/>
</StackPanel>
My cs file
public class EmployeeListViewModel : INotifyPropertyChanged
{
Employee _employee;
RelayCommand _addNewEmployee;
EmployeeRepository _employeeRepository;
public Employee Employee
{
get
{
return _employee;
}
set
{
_employee = value;
OnPropertyChanged("Employee");
}
}
public void NewEmployeeCommand()
{
if(_addNewEmployee == null)
{
_addNewEmployee = new RelayCommand( param => NewEmployeeCommandExecute(),
param => NewEmployeeCommandCanExecute
);
}
}
void NewEmployeeCommandExecute()
{
_employeeRepository.Add(Employee);
_employeeRepository.Save();
}
bool NewEmployeeCommandCanExecute
{
get
{
return true;
}
}
}

databinding a Dataset to a listbox...Datatemplate needs to display from multiple columns

I was trying to figure this out for quite some time.I want a Databind a listbox with a Dataset.Simple as it can get.But my problem is that i want my datatemplate to display Columns from two tables,in the dataset.I have tried many samles..but everything i found just gives the dataset as datacontext and gives a single table as itemsource.But my condition is that i want more than one table in my datatemplate..
For eg:
<DataTemplate x:Key="EmployeeDataTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Margin="5" BorderBrush="Black" BorderThickness="1">
<Image Source="{Binding Path=Mast/Image}" Stretch="Fill" Width="50" Height="50" />
</Border>
<StackPanel Grid.Column="1" Margin="5">
<StackPanel Orientation="Horizontal" TextBlock.FontWeight="Bold" >
<TextBlock Text="{Binding Path=Mast/Firstname}" />
<TextBlock Text="{Binding Path=Mast/Lastname}" Padding="3,0,0,0"/>
</StackPanel>
<TextBlock Text="{Binding Path=Details/Age}" />
<TextBlock Text="{Binding Path=Details/Role}" />
</StackPanel>
</Grid>
</DataTemplate>
Any way to do this..? I am confused...!
I tried giving the Dataset as datacontext and Itemsource as {Binding} But only one row is displayed...
You should create a view model class that exposes three properties:
MasterTable of type IEnumerable<MasterTableRow>
SelectedMaster of type DataRowView
MasterDetails of type IEnumerable<DetailsTableRow>
In your view model, put your instance of your DataSet, and return the appropriate values for the properties. To wrap it all up, you should implement INotifyPropertyChanged and fire change notifications for SelectedMaster and MasterDetails whenever SelectedMaster changes.
Remember to set the view model as the DataContext for the bindings.
Here's how it might look like:
public partial class ViewModel : INotifyPropertyChanged
{
DataSet1 ds;
DataRowView selectedMaster;
public IEnumerable<DataSet1.MasterTableRow> MasterTable
{
get { return ds.MasterTable; }
}
public DataRowView SelectedMaster
{
get { return selectedMaster; }
set
{
if (selectedMaster != value)
{
selectedMaster = value;
OnPropertyChanged("MasterDetails");
OnPropertyChanged("SelectedMaster");
}
}
}
public IEnumerable<DataSet1.DetailsTableRow> MasterDetails
{
get
{
var masterRow = (DataSet1.MasterTableRow)SelectedMaster.Row;
return masterRow.GetDetailsTableRows();
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string prop)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
#endregion
}
In XAML, the bindings might look like this:
<ListBox ItemsSource="{Binding MasterTable}"
SelectedItem="{Binding SelectedMaster}"
ItemTemplate="{StaticResource MasterTemplate}"/>
<ListBox ItemsSource="{Binding MasterDetails}"
ItemTemplate="{StaticResource DetailsTemplate}"/>

Resources