I have the following code in xaml:
<ListView Name="listView1" IsSynchronizedWithCurrentItem="True" >
<ListView.View>
<GridView>
<GridViewColumn Header="MyList">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Cost, Mode=TwoWay}"></TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
on my code behind I have:
public partial class LogIn : UserControl, INotifyCollectionChanged
{
class Product
{
public string Name { get; set; }
public int Cost { get; set; }
}
ObservableCollection<Product> MyList = new ObservableCollection<Product>()
{
new Product(){ Cost=14},
new Product(){ Cost=15},
new Product(){ Cost=5},
new Product(){ Cost=20}
};
event NotifyCollectionChangedEventHandler INotifyCollectionChanged.CollectionChanged
{
add { throw new NotImplementedException(); }
remove { throw new NotImplementedException(); }
}
// constructor
public LogIn()
{
InitializeComponent();
listView1.DataContext = MyList;
}
private void button5_Click(object sender, RoutedEventArgs e)
{
this.MyList[0].Cost = 123456789;
// !!!!!!!!!!!!!!! I want the listview to update when I press this button
}
The listview does not change when I update on the last method. what do I have to do so that I can update the listview cost values with code behind?
Edit
Thanks to SLaks I made the following changes to my Product class and it worked.
public class Product : INotifyPropertyChanged
{
private int _cost;
public string Name { get; set; }
public int Cost
{
get
{
return _cost;
}
set
{
_cost = value;
OnPropertyChanged("Cost");
}
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private void OnPropertyChanged(string propertyName)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
also I added the following line in the constructor of the usercontroll:
listView1.ItemsSource = MyList;
You need to implement INotifyPropertyChanged in the Product class so that WPF knows when your properties change.
Related
I created a POC in WPF MVVM architecture.In which I used combo box control you can see in the code below.
<ComboBox Name="DeptCombo" Grid.Row="4" Grid.Column="1" ItemsSource="{Binding DepartmentList,Mode=TwoWay}" SelectedItem="{Binding Path=CurrentDepartment,Mode=TwoWay}" DisplayMemberPath="DepartmentName">
</ComboBox>
here CurrentDepartment is a property of Department class.
Everything is fine, I filled that combo , saved that combo value in the database, But the only problem I was facing is, I am not able to set the saved database value in that combo.I don't get any solution regarding that.Please help me.
Add UpdateSourceTrigger
SelectedItem="{Binding Path=CurrentDepartment,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Еhe code should look something like this:
class ViewModel: INotifyPropertyChanged
{
public ObservableCollection<string> Datas { get; set; } = new ObservableCollection<string>()
{
"FF", "AA", "BB"
};
private string currentItem;
public string CurrentItem
{
get => currentItem;
set
{
currentItem = value;
OnPropertyChanged("CurrentItem");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName]string prop="")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}
}
Example with Model:
PersonModel.cs:
class PersonModel
{
public string Name { get; set; }
public int Age { get; set; }
}
ViewModel.cs:
class ViewModel: INotifyPropertyChanged
{
public ObservableCollection<PersonModel> Datas { get; set; } = new ObservableCollection<PersonModel>()
{
new PersonModel(){Age = 10, Name="Tom"},
new PersonModel(){Age = 10, Name="Mark"},
};
private PersonModel currentItem;
public PersonModel CurrentItem
{
get => currentItem;
set
{
currentItem = value;
OnPropertyChanged("CurrentItem");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName]string prop="")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}
}
MainWindow.xaml:
<ComboBox ItemsSource="{Binding Datas}" SelectedItem="{Binding CurrentItem, UpdateSourceTrigger=PropertyChanged}"
Height="100" Width="100">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Age}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Department Class :
public class Department : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertychanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private int id;
public int Id
{
get { return id; }
set { id = value; OnPropertychanged("Id"); }
}
private string departmentName;
public string DepartmentName
{
get { return departmentName; }
set { departmentName = value; OnPropertychanged("DepartmentName"); }
}
private bool isActive;
public bool IsActive
{
get { return isActive; }
set { isActive = value; OnPropertychanged("IsActive"); }
}
}
View Model :
private ObservableCollection<Department> departmentList;
public ObservableCollection<Department> DepartmentList
{
get { return departmentList; }
set { departmentList = value; OnPropertyChanged("DepartmentList"); }
}
private Department currentDepartment;
public Department CurrentDepartment
{
get { return currentDepartment; }
set { currentDepartment = value; OnPropertyChanged("CurrentDepartment"); }
}
private void DepartmentPop()
{
DepartmentList = new ObservableCollection<Department>
(objDepartmentService.GetAll());
}
I am trying to bind a ComboBox, which have two categories (Letters
and Numbers), to a Listbox.
Here is the C# Code:
class LibraryViewModel
{
public LetterOrNumberList lib { get; }
public LibraryViewModel()
{
lib = new LetterOrNumberList();
}
}
public class LetterOrNumberList : ObservableCollection<LettersAndNumbers>
{
public LetterOrNumberList()
{
LettersAndNumbers Letters = new LettersAndNumbers();
Letters.Title = "Letters";
ObservableCollection<IExample> letters = new ObservableCollection<IExample>();
letters.Add(new LetterA());
letters.Add(new LetterB());
Letters.LetterOrNumber = letters;
LettersAndNumbers Numbers = new LettersAndNumbers();
Numbers.Title = "Numbers";
ObservableCollection<IExample> numbers = new ObservableCollection<IExample>();
numbers.Add(new Number1());
numbers.Add(new Number2());
Numbers.LetterOrNumber = numbers;
this.Add(Letters);
this.Add(Numbers);
}
}
public class LettersAndNumbers
{
public string Title { get; set; }
public ObservableCollection<IExample> LetterOrNumber { get; set; }
}
public class LetterA : PropertyChangeClass, IExample
{
public string value
{
get
{
return _value;
}
set
{
_value = value;
OnPropertyChanged(nameof(value));
}
}
public LetterA()
{
value = "A";
}
private string _value;
}
public class LetterB : PropertyChangeClass, IExample
{
//principial the same like class Letter A
//...
}
public class Number1 : PropertyChangeClass, IExample
{
//principial the same like class Letter A
//...
}
public class Number2 : PropertyChangeClass, IExample
{
//principial the same like class Letter A
//...
}
public class PropertyChangeClass
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string PropertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
}
}
public interface IExample
{
string value { get; set; }
}
And in XAML I tried it as following:
<ComboBox Width="100" Name="cBox" Text="Bitte auswählen" ItemsSource="{Binding lib}" DisplayMemberPath="Title"/>
<ListBox Grid.Row="2" ItemsSource="{Binding cBox}" DisplayMemberPath="value"/>
The ComboBox binding works but I dont know how to bind the
nested "value" property in dependency of the selecteditem.
ComboBox
Maybe someone know how to solve. Thanks!
EDIT:
I've a library of different products. Each product is to a category assigned. At the end the user should select a category and the products of it will be dynamically displayed (in a ListBox). I ve sorted it in a observablecollection that Ive the same structure like the code above.
I finally get it:
<ListBox Grid.Row="2" ItemsSource="{Binding ElementName=cBox, Path=SelectedItem.LetterOrNumber, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Path=value}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In the shown code i need to know the coding to be replaced in place of question mark in the code. I need to delete,edit and update the item in the list view without writing any code in code behind. I only want to do these operations by bindin view with view model through Icommand
This a class in my model Playlist.cs
namespace MvvmDemo.Models
{
public class Playlist
{
public string Title { get; set; }
}
}
This is a class in my viewmodel PlaylistsViewModel.cs
namespace MvvmDemo.ViewModels
{
public class PlaylistsViewModel
{
public ObservableCollection Playlists { get; private set; } = new ObservableCollection();
public ICommand AddPlaylistCommand { get; private set; }
public ICommand DeletePlaylistCommand { get; private set; }
public ICommand EditPlaylistCommand { get; private set; }
public PlaylistsViewModel()
{
AddPlaylistCommand = new Command(AddPlaylist);
DeletePlaylistCommand = new Command(DeletePlaylist);
}
public void AddPlaylist()
{
var newPlaylist = "Playlist " + (Playlists.Count + 1);
Playlists.Add(new Playlist { Title = newPlaylist });
}
public void DeletePlaylist()
{
????????????????
}
public void EditPlaylist()
{
????????????????
}
}
}
you have to make the command is parameterised and pass binding data through the parameter.
and from that data you can get the index value of selected.using that remove the item from the list.
Playlists.RemoveAt("INDEX_NUMBER");
To update it in the view use "INotifyProperty" also
If you want to delete and edit item in ListView, firstly, you should need to use ICommand, then you could need to use INotifyPropertyChanged to implement Inotify.
I do one sample that you can take a look. Choosing one Item and long press with the left mouse button, you will see two ways, delete Item and Edit Item action.
<ContentPage.Content>
<StackLayout>
<ListView
x:Name="mylistview"
ItemsSource="{Binding lists}"
SelectedItem="{Binding selecteditem}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.ContextActions>
<MenuItem
Command="{Binding BindingContext.DeletePlaylistCommand, Source={x:Reference Name=mylistview}}"
IsDestructive="true"
Text="Delete Item" />
<MenuItem
Command="{Binding BindingContext.EditPlaylistCommand, Source={x:Reference Name=mylistview}}"
IsDestructive="true"
Text="Edit Item" />
</ViewCell.ContextActions>
<StackLayout Padding="15,0">
<Label Text="{Binding Title}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Page19 : ContentPage, INotifyPropertyChanged
{
public ObservableCollection<Playlist> lists { get; set; }
//public RelayCommand1 AddPlaylistCommand { get; set; }
public RelayCommand DeletePlaylistCommand { get; set; }
public RelayCommand EditPlaylistCommand { get; set; }
private Playlist _selecteditem;
public Playlist selecteditem
{
get { return _selecteditem; }
set
{
_selecteditem = value;
RaisePropertyChanged("selecteditem");
}
}
public Page19 ()
{
InitializeComponent ();
lists = new ObservableCollection<Playlist>()
{
new Playlist(){Id=1,Title="list 1"},
new Playlist(){Id=2, Title="list 2"},
new Playlist(){Id=3,Title="list 3"},
new Playlist(){Id=4,Title="list 4"},
new Playlist(){Id=5,Title="list 5"},
new Playlist(){Id=6,Title="list 6"},
};
DeletePlaylistCommand = new RelayCommand(DeletePlaylist);
EditPlaylistCommand = new RelayCommand(EditPlaylist);
selecteditem = lists[0];
this.BindingContext = this;
}
public void AddPlaylist()
{
}
public void DeletePlaylist()
{
Playlist item = selecteditem;
lists.Remove(item);
}
public void EditPlaylist()
{
Playlist item = selecteditem;
int id = item.Id;
foreach(Playlist playl in lists.Where(a=>a.Id==id))
{
playl.Title = "chenge title";
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class Playlist: INotifyPropertyChanged
{
private int _Id;
public int Id
{
get { return _Id; }
set
{
_Id = value;
RaisePropertyChanged("Id");
}
}
private string _Title;
public string Title
{
get { return _Title;}
set
{
_Title = value;
RaisePropertyChanged("Title");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Here is the RelayCommd:
public class RelayCommand : ICommand
{
readonly Action _execute;
public RelayCommand(Action execute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_execute();
}
}
You can use observablecollection. It will reflect add,remove operation of item to the listview. And for editing item you have to raise property changed for all property you are editing.To simplify that property changed you can implement property changed event to your Playlist model class.
Like
public void DeletePlaylist()
{
Playlists.Remove(newPlaylist);
}
public void EditPlaylist()
{
newPlaylist.Title="Refreshed Playlist"
}
public class Playlist:INotifyPropertyChanged
{
private string title;
public string Title
{
get{return title;}
set{title=value;
NotifyPropertyChanged();}
}
}
I really don't know how to get this working. I have an ObservableCollection with "BackupItems" in it. This collection is shown in a ListView. When I add/remove or change the "BackupItems" the User Interface doesn't update! Can you help me please? Have I set the Binding wrong? :(
This is my code: (shortened to the relevant things)
BackupWindow XAML:
<ListView ItemsSource="{Binding Path=BackupItems}" SelectedItem="{Binding Path=SelectedBackupItem}">
<ListView.View>
<GridView>
<GridViewColumn Header="Name">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=Name}"></TextBox>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Location">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0" Text="{Binding Path=Location"} />
<Button Grid.Column="1" Content="Browse..." Click="BrowseButton_Click"></Button>
</Grid>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Date of last Backup" DisplayMemberBinding="{Binding Path=LastBackup}" />
<GridViewColumn Header="Date Created" DisplayMemberBinding="{Binding Path=Created}" />
<GridViewColumn Header="Date Modified" DisplayMemberBinding="{Binding Path=Modified}" />
</GridView>
</ListView.View>
</ListView>
Code Behind:
public partial class BackupWindow : Window
{
public BackupWindow()
{
InitializeComponent();
BackupViewModel backupViewModel = new BackupViewModel();
DataContext = backupViewModel;
}
private void CreateBackupItemButton_Click(object sender, RoutedEventArgs e)
{
BackupViewModel backupViewModel = (BackupViewModel)DataContext;
BackupItem newBackupItem = new BackupItem();
newBackupItem.Created = DateTime.Now;
newBackupItem.Modified = DateTime.Now;
newBackupItem.Name = "";
newBackupItem.Location = "";
backupViewModel.BackupItems.Add(newBackupItem);
}
private void DeleteBackupItemButton_Click(object sender, RoutedEventArgs e)
{
BackupViewModel backupViewModel = (BackupViewModel)DataContext;
backupViewModel.BackupItems.Remove(backupViewModel.SelectedBackupItem);
}
private void BrowseButton_Click(object sender, RoutedEventArgs e)
{
UIElement browseButton = (UIElement)sender;
DependencyObject grid = VisualTreeHelper.GetParent(browseButton);
DependencyObject currentbackupItem = VisualTreeHelper.GetParent(grid);
System.Windows.Controls.ContentPresenter a = (System.Windows.Controls.ContentPresenter)currentbackupItem;
BackupItem currentBackupItem = (BackupItem)a.Content;
BackupViewModel backupViewModel = (BackupViewModel)DataContext;
// The VistaFolderBrowserDialog is from "Ooki.Dialogs"
VistaFolderBrowserDialog folderBrowserDialog = new VistaFolderBrowserDialog();
bool? result = folderBrowserDialog.ShowDialog();
if (result == true) // If the user presses Open in the dialog
{
// Find the currentBackupItem in the List
for (int i = 0; i < backupViewModel.BackupItems.Count; i++)
{
if (currentBackupItem == backupViewModel.BackupItems[i])
{
backupViewModel.BackupItems[i].Location = folderBrowserDialog.SelectedPath;
backupViewModel.BackupItems[i].Modified = DateTime.Now;
break;
}
}
}
}
}
ViewModel
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
BackupViewModel:
public class BackupViewModel : ViewModel
{
public ObservableCollection<BackupItem> BackupItems
{
get
{
return BackupItemLibrary.GetInstance().BackupItems;
}
}
}
BackupItemLibrary
public class BackupItemLibrary
{
private static BackupItemLibrary instance;
public static BackupItemLibrary GetInstance()
{
if (instance == null)
{
instance = new BackupItemLibrary();
}
return instance;
}
public static void SetInstance(BackupItemLibrary newBackupItemLibrary)
{
instance = newBackupItemLibrary;
}
private BackupItemLibrary()
{
}
public string FileName { get; set; }
private ObservableCollection<BackupItem> backupItems = new ObservableCollection<BackupItem>();
public ObservableCollection<BackupItem> BackupItems
{
get
{
return backupItems;
}
set
{
backupItems = value;
}
}
BackupItem:
public class BackupItem
{
public string Name { get; set; }
public string Location { get; set; }
public DateTime LastBackup { get; set; }
public DateTime Created { get; set; }
public DateTime Modified { get; set; }
}
The solution for the problem:
Class BackupItem must implement INotifyPropertyChanged (e.g by also deriving from ViewModel), and fire the PropertyChanged event when a property value is set (at least when the Location or Modified properties are set).
Code:
public class BackupItem : INotifyPropertyChanged
{
private string name;
public string Name
{
get
{
return name;
}
set
{
name = value;
OnPropertyChanged("Name");
}
}
private string location;
public string Location
{
get
{
return location;
}
set
{
location = value;
OnPropertyChanged("Location");
}
}
private DateTime lastBackup;
public DateTime LastBackup
{
get
{
return lastBackup;
}
set
{
lastBackup = value;
OnPropertyChanged("LastBackup");
}
}
private DateTime created;
public DateTime Created
{
get
{
return created;
}
set
{
created = value;
OnPropertyChanged("Created");
}
}
private DateTime modified;
public DateTime Modified
{
get
{
return modified;
}
set
{
modified = value;
OnPropertyChanged("Modified");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I am new to mvvm. I have a listbox in my silverlight application which is binded to a observable collection in view model i want to make the listbox with first item selected. I tired this but it doesnt work.
<ListBox Height="431" Canvas.Left="17" Canvas.Top="77" Width="215" FontSize="13" ItemsSource="{Binding Path=Categorys, Mode=TwoWay}" DataContext="{Binding}" SelectedItem="{Binding CurrentCategory, Mode=TwoWay}" ItemTemplate="{StaticResource CategoryDataTemplate}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" Name="lst_category">
then i added this in mainpage load of mainpage viewmodel
CurrentCategory = Categorys[0];
Can any one Help me
Do the following steps:
Make sure that the collection Categorys is filled already. You might need to use AsycCTP, Asynchronous Programming with Async and Await or some other mechanism to first wait for the collection to be filled.
The await operator is applied to a task in an asynchronous method to suspend the execution of the method until the awaited task completes. The task represents ongoing work.
Implement INotifyPropertyChanged in ViewModel exposing the Property, CurrentCategory and raise the event of PropertyChanged from within the Setter of the Property.
private Category _currentCategory = null;
public Category CurrentCategory
{
get { return _currentCategory; }
set
{
if (_currentCategory != value)
{
_currentCategory = value;
// Update bindings
RaisePropertyChanged("CurrentCategory");
}
}
}
Now you can use the same piece of code:
CurrentCategory = Categorys[0];
Try using ICollectionView and IsSynchronizedWithCurrentItem. The CollectionView has all the functionality you need. For example MoveToFirst().
Xaml:
<ListBox ItemsSource="{Binding Categories}"
DisplayMemberPath="Name"
IsSynchronizedWithCurrentItem="True" />
ViewModel:
public class ViewModel :INotifyPropertyChanged
{
private ObservableCollection<Category> _categories = new ObservableCollection<Category>();
private Category _currentCategory;
public ObservableCollection<Category> Categories
{
get { return _categories; }
set { _categories = value; OnPropertyChanged("Categories");}
}
public Category CurrentCategory
{
get { return _currentCategory; }
set { _currentCategory = value; OnPropertyChanged("CurrentCategory");}
}
public ICollectionView CategoriesView { get; private set; }
public ViewModel()
{
Categories.Add(new Category{Id = Guid.NewGuid(), Name = "Cat1"});
Categories.Add(new Category{Id = Guid.NewGuid(), Name = "Cat2"});
Categories.Add(new Category{Id = Guid.NewGuid(), Name = "Cat3"});
CategoriesView = CollectionViewSource.GetDefaultView(Categories);
CategoriesView.CurrentChanged += OnCategoriesChanged;
CategoriesView.MoveCurrentToFirst();
}
private void OnCategoriesChanged(object sender, EventArgs e)
{
var selectedCategory = CategoriesView.CurrentItem as Category;
if (selectedCategory == null) return;
CurrentCategory = selectedCategory;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Category
{
public Guid Id { get; set; }
public string Name { get; set; }
}
You Should Try This Way also.................
List c = new List
CurrentCategory = c.firstOrDefault()