So I'm trying to learn the MVVM design patter in WPF, I want to do the following:
In external class I've got a ObservableCollection _students that is bound to a listview on the WPF window using MVVM design pattern. The listview shows only the Student's name and Age.
public class Student
{
public string Name { get; set; }
public int Age { get; set; }
public string Course { get; set; }
public DateTime JoiningDate { get; set; }
}
public class ViewModel : INotifyPropertyChanged
{
private ObservableCollection<Student> _students;
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged!=null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public ObservableCollection<Student> Students
{
get
{
return _students;
}
set
{
_students = value;
NotifyPropertyChanged("Students");
}
}
All good, but I want to put a TextBox and set it to show the listview's selected item's course property. This means I must
get the listview's selected index (ok)
bind the textbox.Text property to Students[that index].Course
I'm stuck at 2. Any help?
i would solve this by another way.
Take a look at this post
.
Another way would be that your ViewModel contains a Student-property(e.g. SelectedStudent) which is bind to the SelctedItem of the listView. Then you can handel this by
{Binding Path=SelectedStudent.Course}
Assume you bind the listview to a collection of type SampleData like below:
SampleData.cs
public class SampleData
{
public int Id { get; set; }
public string Text { get; set; }
public decimal Value { get; set; }
}
Then you bind the ListView ItemsSource to a collection. WIt does not matter if you bind ItemsSource property to a property on ViewModel or you bind it in code-behind like the code below.
var source = new List<SampleData>();
source.Add(new SampleData() { Id = 1, Text = "AAA" });
source.Add(new SampleData() { Id = 2, Text = "BBB" });
source.Add(new SampleData() { Id = 3, Text = "CCC" });
source.Add(new SampleData() { Id = 4, Text = "DDD" });
source.Add(new SampleData() { Id = 5, Text = "EEE" });
You can bind TextBox's Text property to the SelectedItem directly on the View.
<StackPanel Orientation="Horizontal">
<ListView x:Name="listView1" />
<TextBox Text="{Binding ElementName=listView1, Path=SelectedItem.Text}" />
</StackPanel>
Related
I need help. I am kind of new in MVVM
I've got event datagrid selecteditem event in MVVM. After creating a source for grid items(void search) I need populate combobox source as a filter values based on main collection. I used(before MVVM) to work with event subscribing, but can not achieve it here. If I put OnPropertChanged("FamilyFilter") at the end of main function combobox selectionchanged event start working immediately. Here is the code:
public IList<Object> somelist { get; set; }
public CollectionView Items { get; set; }
private string _selectedFamily;
public string SelectedFamily
{ get { return _selectedFamily; }
set { Items = <<filteredcollection>>; OnPropertyChanged("SelectedFamily"); }
}
private List<string> _familyEntries;
public List<string> FamilyEntries
{ get { return _familyEntries; }
set { _familyEntries = value; OnPropertyChanged("FamilyEntries");}
}
public void search()
{
Items = (CollectionView)CollectionViewSource.GetDefaultView(somelist);
_familyEntries = somelist.GroupBy(x => x.Family).Select(z => z.First().Family).ToList();
OnPropertyChanged("Items");
OnPropertyChanged("FamilyEntries");
}
<ComboBox ItemsSource="{Binding FamilyEntries}" SelectedValue="{Binding SelectedFamily}"/>
I am trying to change the selected item of a combo box based on a change in another combo box. The situation is complicated by the fact that both combo boxes appear in a list of item templates.
The XAML is as follows:
<ListBox ItemsSource="{Binding AncillaryExtendedPropertyViewModels}" ItemTemplateSelector="{StaticResource templateSelector}"/>
<DataTemplate x:Key="EnumDataTemplate"> <Grid Margin="4"
MinHeight="25"> <ComboBox SelectedItem="{Binding ExtendedPropertyEnum,
UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
ItemsSource="{Binding ExtendedPropertyEnumList}"
DisplayMemberPath="Value"/> </Grid> </DataTemplate>
The data context of the view containing the XAML is set to AncillaryBaseViewModel. The following is a cut down version of AncillaryBaseViewModel.
public class AncillaryBaseViewModel : ComplexOrderItemViewModel, IDataErrorInfo
{
private ObservableCollection<ExtendedPropertyViewModel> _ancillaryExtendedPropertyViewModels;
public ObservableCollection<ExtendedPropertyViewModel> AncillaryExtendedPropertyViewModels
{
get { return _ancillaryExtendedPropertyViewModels; }
set
{
_ancillaryExtendedPropertyViewModels = value;
OnPropertyChanged("AncillaryExtendedPropertyViewModels");
}
}
and the ExtendedPropertyViewModel class....
public class ExtendedPropertyViewModel : DataTemplateSelector
{
private ExtendedProperty _extendedProperty;
public DataTemplate DefaultnDataTemplate { get; set; }
public DataTemplate BooleanDataTemplate { get; set; }
public DataTemplate EnumDataTemplate { get; set; }
public ExtendedPropertyEnum ExtendedPropertyEnum
{
get
{ return ExtendedProperty.ExtendedPropertyEnum; }
set
{
if (ExtendedProperty.ExtendedPropertyEnum != value)
{
_extendedProperty.ExtendedPropertyEnum = value;
AncillaryBaseViewModel parent = RequestParent();
if (parent != null)
{
parent.AncillaryExtendedPropertyViewModels[7].ExtendedPropertyEnum =
ExtendedProperty.ExtendedPropertyEnum.DependentExtendedPropertyEnums[0];
}
parent.OrderItem.SetStockCode();
PropertyChanged += parent.OnExtendedPropertyChanged;
OnPropertyChanged();
}
}
}
and the ExtendedProperty class....
public class ExtendedProperty : ViewModelBase
{
private ExtendedPropertyEnum _extendedPropertyEnum;
public int ExtendedPropertyID { get; set; }
public int OrderItemTypeID { get; set; }
public string DisplayName {get; set;}
public ObservableCollection<ExtendedPropertyEnum> ExtendedPropertyEnumList { get; set; }
public string Value { get; set; }
public ExtendedPropertyEnum ExtendedPropertyEnum
{
get
{
return _extendedPropertyEnum;
}
set
{
_extendedPropertyEnum = value;
OnPropertyChanged("ExtendedPropertyEnum");
}
}
}
To summarise, when I change the value of combo box A through the UI, this calls the ExtendedPropertyEnum setter within ExtendedPropertyViewModel, which changes the ExtendedPropertyEnum bound to another combo box B, which is in the same list. I would expect this to update the value displayed in combo box B accordingly, which it does not.
As an aside, changing the value of combo box A does update a label that is not within a data template. The XAML for this label is....
<Label Content="{Binding StockCode}" MinWidth="100"/>
This is updated by the following code within AncillaryBaseViewModel....
public void OnExtendedPropertyChanged(object sender, EventArgs args)
{
OnPropertyChanged("StockCode");
}
I thought I could change this to the following to force the combo boxes in the list to update.
public void OnExtendedPropertyChanged(object sender, EventArgs args)
{
OnPropertyChanged("StockCode");
OnPropertyChanged("AncillaryExtendedPropertyViewModels");
}
However, this did not work either.
Any help greatly appreciated.
Roger.
if I understand the question correctly then you are expecting a changed value within an observablecollection to be reflected within the UI. This will not happen. observablecollections raise notifyproperty events when the collection itself changes, not when values within them change. You'll either need to raise a notifyproperty event on the value changing or remove the item from the list and add it back with a new value.
I want to add a space in my column header when I am binding my ObservableCollection to a Grid in MVVM way. Here is my code..
public class Site
{
public string Name { get; set; }
public string Description { get; set; }
[Description("Short Code")]
[Bindable(true)]
public string ShortCode { get; set; }
public static ObservableCollection<Site> GetSampleData()
{
var sites = new ObservableCollection<Site>();
sites.Add(new Site { Name = "New Delhi", Description = "New Delhi Railway Station", ShortCode = "NDLS"});
sites.Add(new Site { Name = "Mumbai", Description = "Mumbai Railway Station", ShortCode = "MUM"});
return sites;
}
}
// Here is my View Model Class
public class SiteViewModel
{
public ObservableCollection<Site> SitesDataCollection { get; set; }
public SiteViewModel()
{
SitesDataCollection = Site.GetSampleData();
}
}
// This is the View Class
public partial class SiteView : UserControl
{
public Sites()
{
InitializeComponent();
DataContext = new SiteViewModel();
}
}
My XAML is
<Grid>
<DockPanel>
<DataGrid Name="SiteGrid" ItemsSource="{Binding Path=SitesDataCollection}">
</DataGrid>
</DockPanel>
</Grid>
Basically in my Grid, for the third column , the header is ShortCode, I want to display "Short Code", How can I do that.
I tried adding
[Description("Short Code")] and
[Bindable(true)]
but didn't got the desired result.
In the AutoGeneratingColumn event handler of the DataGrid, access the DataGridColumn properties by referencing the DataGridAutoGeneratingColumnEventArgs.Column property. Use this to edit the column header.
private void DataGrid_OnAutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
e.Column.Header = Regex.Replace(e.Column.Header.ToString(), "(\\B[A-Z])", " $1");
}
This will add spaces into your property names where there are upper case letters.
I have a WPF application using MVVM. I have some user controls that should show a Person FirstName, LastName and email in 3 Textbox controls using simple databinding.
The User Control has a simple combobox where the user selects the ID for the user and therefore the Person Record with that ID should be loaded (its data fetched from the database) and then the FirstName, LastName and Email will display in the textBoxes.
I have a Usercontrol with the combobox for the IDs and 3 textboxes for the three properties, a ViewModel Class and a Model class (person Class) with the three properties (FirstName, LastName and Email).
What is the simplest way to implement this behavior using MVVM(preferably)? any samples?
I'm guessing here since your question is a little vague that you're not quite sure how to hook the pieces together. For simplicity's sake let us hook the ViewModel directly to the user control and get it all binding.
As long as your view model is populated with the correct set of People, all the binding below will handle the data and show the correct data. Take note of the two-way binding for the selected item in the combobox. That allows WPF to send back the new selected item to the viewmodel.
In the UserControl's code behind:
public MyUserControl()
{
DataContext = new MyViewModel();
}
In the UserControl's XAML:
<ComboBox ItemsSource="{Binding AllPeople}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" />
<TextBox Text="{Binding SelectedItem.LastName}" />
<TextBox Text="{Binding SelectedItem.FirstName}" />
<TextBox Text="{Binding SelectedItem.EmailName}" />
Your ViewModel:
private IEnumerable<Person> _allPeople;
public IEnumerable<Person> AllPeople
{
get { return _allPeople; }
set
{
if (_allPeople != value)
{
_allPeople = value;
NotifyPropertyChanged("AllPeople");
}
}
}
private Person _selectedItem;
public Person SelectedItem
{
get { return _selectedItem; }
set
{
if (!_selectedItem != value)
{
_selectedItem = value;
NotifyPropertyChanged("SelectedItem");
}
}
}
private void NotifyPropertyChanged(string propertyName)
{
if ( PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName);
}
}
}
public class Person
{
public int PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
}
I want to have a User Control that takes a collection of People (property "Data") and displays them in a list box.
When I run my app nothing shows in the listbox. Can you please point out what I'm doing wrong?
Thanks!!!
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public override string ToString()
{
return Name + "(" + Age + ")";
}
}
User Control:
(uc1.xaml.cs)
public partial class uc1
{
public static readonly DependencyProperty DataProperty = DependencyProperty.Register("Data", typeof (List<Person>), typeof (uc1));
public List<Person> Data
{
get { return (List<Person>) GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
public uc1()
{
InitializeComponent();
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
DataContext = Data;
}
}
(uc1.xaml)
<ListBox ItemsSource="{Binding Name}" />
The ItemsSource property controls the list of items that are displayed in the listbox. If you want the ListBox to display one line for each person, you need to set the ItemsSource to bind directly to the DataContext. Then you use the DisplayMemberPath property to control which property of the Person class to show.
Here's my sample code that works for me.
The person class is the same.
The Window1.xaml.cs:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
List<Person> Data = new List<Person>();
Data.Add(new Person { Name = "Test 1", Age = 5 });
Data.Add(new Person { Name = "Test 2", Age = 10 });
this.DataContext = Data;
}
}
The Window1.xaml
<ListBox ItemsSource="{Binding}" DisplayMemberPath="Name" />