I make a Notes application using WPF with MVVM.
I want to make a counter for each i
So there is a task list, every task has importance (none, regular and important).
I want to make listbox, that displays a count of tasks of each importances and bind it to view (counter for each importance), but i don`t know how. Something like that -
total - 10
none - 5
regular - 3
important - 2
Task Model:
public enum TaskStates
{
None,
Regular,
Important,
}
[DataContractAttribute]
public class Task : INotifyPropertyChanged
{
private string _name;
private string _desc;
private TaskStates taskState;
public SolidColorBrush TaskBG { get; set; }
[DataMember]
public DateTime CreationDate { get; private set; }
[DataMember]
public TaskStates TaskState
{
get { return taskState; }
set
{
TaskBG ??= new SolidColorBrush();
switch (value)
{
case TaskStates.None:
TaskBG.Color = Color.FromRgb(0, 113, 127);
break;
case TaskStates.Important:
TaskBG.Color = Color.FromRgb(180, 60, 60);
break;
case TaskStates.Regular:
TaskBG.Color = Color.FromRgb(53, 165, 75);
break;
}
taskState = value;
OnPropertyChanged(nameof(TaskState));
}
}
[DataMember]
public string Name
{
get => _name;
set
{
_name = value;
OnPropertyChanged(nameof(Name));
}
}
[DataMember]
public string Description
{
get => _desc;
set
{
_desc = value;
OnPropertyChanged(nameof(Description));
}
}
public Task(string name, string desc,DateTime creationDate, TaskStates taskState)
{
this._name = name;
this._desc = desc;
CreationDate = creationDate;
TaskState = taskState;
}
public Task()
{
}
protected void OnPropertyChanged ([CallerMemberName]string prop = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
public event PropertyChangedEventHandler PropertyChanged;
}
ViewModel:
class TaskViewModel : INotifyPropertyChanged
{
public Task CurrentTask { get; set; }
public DateTime CurrentDate { get; set; }
IDialogService dialogService;
IFileService fileService;
private TaskCommand addTask;
private TaskCommand removeTask;
private TaskCommand saveCommand;
private TaskCommand openCommand;
private TaskCommand clearCommand;
public ObservableCollection<Task> Tasks { get; set; }
#region Command Properties
public TaskCommand AddCommand
{
get
{
return addTask ?? (addTask = new TaskCommand(
new Action<object>(obj =>
{
Tasks.Add((obj as Task) ?? new Task("Header", "smth", CurrentDate, TaskStates.None));
})
));
}
}
public TaskCommand RemoveCommand
{
get
{
return removeTask ?? (removeTask = new TaskCommand(
new Action<object>(obj =>
{
if (CurrentTask != null)
Tasks.Remove(CurrentTask);
}), new Func<object, bool>(obj => Tasks.Count > 0)
));
}
}
public TaskCommand SaveCommand
{
get
{
return saveCommand ?? (saveCommand = new TaskCommand(
new Action<object>(obj =>
{
try
{
if (dialogService.SaveFileDialog() == true)
{
fileService.Save(Tasks.ToList(), dialogService.FilePath);
dialogService.ShowMessage("File Saved");
}
}
catch (Exception ex)
{
dialogService.ShowMessage(ex.Message);
}
}),
new Func<object, bool>(obj => Tasks.Count > 0)));
}
}
public TaskCommand OpenCommand
{
get
{
return openCommand ?? (openCommand = new TaskCommand(
new Action<object>(obj =>
{
try
{
if (dialogService.OpenFileDialog())
{
var newTasks = fileService.Open(dialogService.FilePath);
if (newTasks.Count > 0)
{
Tasks.Clear();
foreach (var item in newTasks)
{
Tasks.Add(item);
}
}
}
}
catch (Exception ex)
{
dialogService.ShowMessage(ex.Message);
}
}
)));
}
}
public TaskCommand ClearCommand
{
get
{
return clearCommand ?? (clearCommand = new TaskCommand
(
new Action<object>(obj =>
{
var res = MessageBox.Show("Are you Sure?", "Caution", MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (res == MessageBoxResult.Yes)
Tasks.Clear();
}),
new Func<object, bool>(obj => Tasks.Count > 0)
));
}
}
#endregion
public TaskViewModel()
{
CurrentDate = DateTime.Now;
fileService = new JsonFileService();
dialogService = new DefaultDialogService();
Tasks = new ObservableCollection<Task>()
{
new Task("Important", "Test",CurrentDate,TaskStates.Important),
new Task("Regular", "Test",CurrentDate,TaskStates.None),
};
}
protected void OnPropertyChanged([CallerMemberName] string prop = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
public event PropertyChangedEventHandler PropertyChanged;
}
Use a ValueConverter to accomplish this.
public class TaskStateCountConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
=> $"{parameter} - {((IEnumerable<Task>)value).Count(task => task.TaskState == (TaskStates)parameter)}";
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> throw new NotImplementedException();
}
XAML (you said you wanted a ListBox):
<Window.Resources>
<local:TaskStateCountConverter x:Key="TaskStateCountConverter"/>
</Window.Resources>
<Grid>
<ListBox>
<ListBoxItem Content="{Binding Tasks.Count}"/>
<ListBoxItem Content="{Binding Tasks,
Converter={StaticResource TaskStateCountConverter},
ConverterParameter={x:Static local:TaskStates.None}}"/>
<ListBoxItem Content="{Binding Tasks,
Converter={StaticResource TaskStateCountConverter},
ConverterParameter={x:Static local:TaskStates.Regular}}"/>
<ListBoxItem Content="{Binding Tasks,
Converter={StaticResource TaskStateCountConverter},
ConverterParameter={x:Static local:TaskStates.Important}}"/>
</ListBox>
</Grid>
ViewModel:
public class TaskViewModel : INotifyPropertyChanged
{
// unchanged parts skipped
public TaskViewModel()
{
Tasks.CollectionChanged += OnTasksChanged;
}
private void OnTasksChanged(object sender, EventArgs e)
=> OnPropertyChanged(nameof(Tasks));
It was a bit trickier than I first thought because the converted values are not updated without the event handler (Tasks.Count is).
And while you're about it, you could also create a ValueConverter for your coloring logic.
Edit
To update TaskViewModel from within CurrentTask:
public class TaskViewModel : INotifyPropertyChanged
{
// unchanged parts skipped
private Task _currentTask;
public Task CurrentTask
{
get => _currentTask;
set
{
if (value != _currentTask)
{
if (_currentTask != null)
{
_currentTask.PropertyChanged -= OnCurrentTaskChanged;
}
_currentTask = value;
_currentTask.PropertyChanged += OnCurrentTaskChanged;
}
}
}
private void OnCurrentTaskChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(Task.TaskState))
{
OnPropertyChanged(nameof(Tasks));
}
}
}
Related
I am trying to implement a searchbar using MVVM in Xamarin.forms. so far I have managed to borrow some code from around the internet and it seems to do go through the motions of the search. the only issue is I don't know what code to put in the command.
I would like the search bar to search recipeNames from a list of Recipes. this information is all stored on a local database and displayed using an observable collection.
please can you help me work it out.
XAML
<SearchBar x:Name="SearchBar"
Placeholder="Search"
SearchCommand="{Binding SearchCommand}"
SearchCommandParameter="{Binding Text, Source={x:Reference SearchBar}}"
Text="{Binding SearchText, Mode=TwoWay}">
<SearchBar.Behaviors>
<local:TextChangedBehavior />
</SearchBar.Behaviors>
</SearchBar>
<ListView x:Name="ListViewItems"
ItemsSource="{Binding Recipes}"
IsPullToRefreshEnabled="True"
Refreshing="ListViewItems_Refreshing"
SelectedItem="{Binding SelectedRecipe}">
Text changed Behaviour
class TextChangedBehavior: Behavior<Xamarin.Forms.SearchBar>
{
protected override void OnAttachedTo(Xamarin.Forms.SearchBar bindable)
{
base.OnAttachedTo(bindable);
bindable.TextChanged += Bindable_TextChanged;
}
protected override void OnDetachingFrom(Xamarin.Forms.SearchBar bindable)
{
base.OnDetachingFrom(bindable);
bindable.TextChanged -= Bindable_TextChanged;
}
private void Bindable_TextChanged(object sender, TextChangedEventArgs e)
{
((Xamarin.Forms.SearchBar)sender).SearchCommand?.Execute(e.NewTextValue);
}
}
and viewModel
public class RecipeListViewModel : ObservableCollection<Recipe>
{
private ObservableCollection<Recipe> Recipes {get; set;}
public INavigation Navigation { get; internal set; }
public ICommand NewAddPage { get; protected set; }
public RecipeListViewModel(INavigation navigation)
{
this.Navigation = navigation;
Recipes = new ObservableCollection<Recipe>();
this.NewAddPage = new Command(async () => await CreateNewAddPage());
Init();
}
// Gets all recipes from the database and adds them to the observable collection
private void Init()
{
var enumarator = App.RecipeDbcontroller.GetRecipe();
if (enumarator == null)
{
App.RecipeDbcontroller.SaveRecipe(new Recipe { RecipeName = "Moussaka", Serves = 6, PrepTime = "30", CookTime = "2 Hours", MealType = "Dinner" });
enumarator = App.RecipeDbcontroller.GetRecipe();
}
while (enumarator.MoveNext())
{
//cleans database of all empty records
if (enumarator.Current.RecipeName == null || enumarator.Current.CookTime == null)
{
App.RecipeDbcontroller.DeleteRecipe(enumarator.Current.RecipeID);
}
else
Add(enumarator.Current);
}
}
private ICommand _searchCommand;
public ICommand SearchCommand
{
get
{
return _searchCommand ?? (_searchCommand = new Command<string>((text) =>
{
**// THIS IS WHAT I DON'T KNOW WHAT TO DO**
}));
}
}
private string _searchText { get; set; }
public string SearchText
{
get { return _searchText; }
set
{
if (_searchText != value)
{
_searchText = value;
}
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
RecipeDatabaseController Class
public RecipeDatabaseController()
{
this.database = DependencyService.Get<ISQLite>().GetConnection();
this.database.CreateTable<Recipe>();
}
//Recipe CRUD
public IEnumerator<Recipe> GetRecipe()
{
lock (locker)
{
if (database.Table<Recipe>().Count() == 0)
{
return null;
}
else
{
return this.database.Table<Recipe>().GetEnumerator();
}
}
}
public IEnumerator<Recipe> GetRecipeBySearchTerm(text)
{
var enumarator = GetRecipe();
lock (locker)
{
while (enumarator.MoveNext)
{
if(enumarator.Current.RecipeName.Contains(text)
return this.
}
}
}
public int SaveRecipe(Recipe recipe)
{
lock (locker)
{
if (recipe.RecipeID != 0)
{
this.database.Update(recipe);
return recipe.RecipeID;
}
else
{
return this.database.Insert(recipe);
}
}
}
public int DeleteRecipe(int Id)
{
lock (locker)
{
return this.database.Delete<Recipe>(Id);
}
}
right so the search command ought to look like this.
public ICommand SearchCommand => _searchCommand ?? (_searchCommand = new Command<string>((text) =>
{
if (text.Length >=1)
{
Recipes.Clear();
Init();
var suggestions = Recipes.Where(c => c.RecipeName.ToLower().StartsWith(text.ToLower())).ToList();
Recipes.Clear();
foreach (var recipe in suggestions)
Recipes.Add(recipe);
}
else
{
Recipes.Clear();
Init();
ListViewVisible = true;
SuggestionsListViewVisible = false;
}
}));
using System.Linq;
//Recipe CRUD
public IEnumerable<Recipe> GetRecipe()
{
lock (locker)
{
return this.database.Table<Recipe>();
}
}
public IEnumerable<Recipe> GetRecipeBySearchTerm(string text)
{
var recipes = GetRecipe();
lock (locker)
{
return recipes.Where(m => m.RecipeName.ToLower().Contains(text));
}
}
Add the using System.Linq reference
Change those two methods and return IEnumerable
Note. RecipeName is the property you want to filter your Recipe with.
And your search command as below
private ICommand _searchCommand;
public ICommand SearchCommand
{
get
{
return _searchCommand ?? (_searchCommand = new Command<string>((text) =>
{
var filteredRecipes = App.RecipeDbcontroller.GetRecipeBySearchTerm(text);
recipes.Clear();
foreach(var recipe in filteredRecipes )
recipes.Add(recipe);
}));
}
}
I have not tested this code, so not sure where I might get errors, but you can work out the rest because the logic is given to you
Good luck
Like the title says, I try to notify a property change, the method RaisePropertyChanged is called coorectly, but PropertyChanged is always null.
Here the shortened class:
public class BluetoothManager : INotifyPropertyChanged {
private string selectedBluetoothResult;
private List<string> foundDevices = new List<string>(5);
public List<DeviceInformation> penDevices = new List<DeviceInformation>();
private GattCharacteristic TxCharacteristic;
public string SelectedBluetoothResult {
get {
return selectedBluetoothResult;
}
set {
selectedBluetoothResult = value;
RaisePropertyChanged();
}
}
public List<string> FoundDevices {
get
{
return foundDevices;
}
set
{
foundDevices = value;
RaisePropertyChanged();
}
}
public BluetoothManager() {
StartScanWatcher();
}
public void StartScanWatcher() {
Debug.WriteLine("Starting device watcher...");
String query = "";
//query for Bluetooth LE devices
query += "System.Devices.DevObjectType:=5 AND System.Devices.Aep.ProtocolId:=\"{BB7BB05E-5972-42B5-94FC-76EAA7084D49}\"";
//query for devices with controllers' name
query += " AND (System.ItemNameDisplay:=\"" + DeviceName + "\" )";
var deviceWatcher = DeviceInformation.CreateWatcher(query); //, requestedProperties, DeviceInformationKind.AssociationEndpoint);
deviceWatcher.Added += DeviceWatcher_OnAdded;
deviceWatcher.EnumerationCompleted += DeviceWatcher_OnEnumComplete;
deviceWatcher.Removed += DeviceWatcher_Removed;
deviceWatcher.Stopped += DeviceWatcher_Stopped;
deviceWatcher.Updated += DeviceWatcher_Updated;
deviceWatcher.Start();
Debug.WriteLine(" StartScanWatcher end");
}
private void DeviceWatcher_OnAdded(DeviceWatcher sender, DeviceInformation deviceInfo) {
Debug.WriteLine(" DeviceWatcher_OnAdded Start");
lock (foundDevices) {
if (foundDevices.Contains(deviceInfo.Id)) {
return;
}
foundDevices.Add(deviceInfo.Id);
RaisePropertyChanged("FoundDevices");
}
Debug.WriteLine($"[{deviceInfo.Name}] DeviceWatcher_OnAdded...");
if (SelectedBluetoothResult == null)
{
SelectedBluetoothResult = deviceInfo.Id;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
Debug.WriteLine("<<<<<<<<<<<<< BluetoothManager, PropertyChanging: " + propertyName);
if (PropertyChanged == null) {
Debug.WriteLine("============ BluetoothManager, PropertyChanged == null, " + propertyName);
return;
}
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
Debug.WriteLine(">>>>>>>>>>>>> BluetoothManager, PropertyChanged: " + propertyName);
}
}
}
And here the Binding in the XAML file:
<ListView ItemsSource="{Binding BluetoothManager.FoundDevices}" SelectedItem="{Binding BluetoothManager.SelectedBluetoothResult}">
<ListView.Resources>
<DataTemplate x:Key="BluetoothDeviceTemplate">
<TextBlock Text="{Binding Path=Sap}"/>
</DataTemplate>
</ListView.Resources>
</ListView>
The binding seems to work properly.
Please note, that the class in which the PropertyChanged is always null is not the DataContext auf the XAML file. Does that mean I have to work differently with the PropertyChange notification?
Thanks in advance.
EDIT:
The complete MainWindowViewModel:
public class MainWindowViewModel : INotifyPropertyChanged {
private ObservableCollection<BookGame> _booksToDisplay = new ObservableCollection<BookGame>();
private ObservableCollection<BookGame> _games = new ObservableCollection<BookGame>();
private string _url;
private int[] _newBooksMID;
private int[] _oldBooksMID;
private Dictionary<int, int> _newBooksVersionNumbers;
private Dictionary<int, int> _oldBooksVersionNumbers;
private Dictionary<int, int> _allVersionNumbers;
private List<BookGame> _booksToAdd;
private long _downloadSpeed;
private bool _isDownloading = true;
private bool _toggleLastSearchKeyWasReturn = false;
string _volumeLabel = "";
FileInfo[] _filesTxt = { };
FileInfo[] _filesAll = { };
private string _folderPath = "";
private string _driveName = null;
List<BookGame> _allGames;
List<BookGame> _allBooks;
List<MP3> _allMP3;
long lengthAllBooks = 0;
long lengthAllGames = 0;
int _percentDownloaded = 100;
private long _amountBytesToDownload;
private long _amountBytesDownloaded;
private long _amountMBytesToDownload;
private long _amountMBytesDownloaded;
private int _downloadTime;
//private bool _isDownloadAborted;
ServerCommi serverComm;
public BluetoothManager BluetoothManager { get; set; }
public MainWindowViewModel() {
DownloadTime = 0;
AmountBytesToDownload = 0;
AmountBytesDownloaded = 0;
DriveInfo drive = null;
foreach (DriveInfo driveInfo in DriveInfo.GetDrives()) {
if (driveInfo.IsReady && driveInfo.VolumeLabel == _volumeLabel) {
drive = driveInfo;
_driveName = drive.Name;
_folderPath = _driveName + _folderPath;
}
}
DirectoryInfo di = new DirectoryInfo(_folderPath);
if (di.Exists)
{
_filesTxt = di.GetFiles("*.txt");
FilesAll = di.GetFiles("*.*");
foreach (FileInfo file in _filesTxt)
{
try
{
Convert.ToInt32(file.Name.Split('_')[0]);
AddBookGameToList(file);
}
catch (Exception e)
{
}
}
}
SearchResults = new ObservableCollection<ResultItem>();
MenuCommand = new RelayCommand(o => {
Debug.WriteLine("Menu Command " + o);
SwitchBooks(o);
});
SearchReturnKeyCommand = new RelayCommand(o => {
Debug.WriteLine("00000000000000000000000000000000 SearchReturnKeyCommand " + o);
SearchActivated();
});
BrowserCommand = new RelayCommand(o => {
Debug.WriteLine("Browser Command main" + o);
CallBrowser("");
});
DeleteCommand = new RelayCommand(o => {
Debug.WriteLine("Delete Command main" + o);
});
ToggleDownloadsCommand = new RelayCommand(o => {
Debug.WriteLine(" |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ");
Debug.WriteLine(" |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ");
Debug.WriteLine("ToggleDownloadsCommand Command main" + o);
ToggleDownloads();
});
_viewModelMusic = new ViewModelMusic(_driveName);
AllGames = Games.ToList<BookGame>();
AllBooks = BooksToDisplay.ToList<BookGame>();
AllMP3 = _viewModelMusic.Mp3s.ToList<MP3>();
long lengthAllMP3 = 0;
foreach (MP3 mp3 in AllMP3) {
lengthAllMP3 += mp3.LengthValue;
}
_viewModelBooks = new ViewModelBooks(BooksToDisplay);
_viewModelGames = new ViewModelGames(Games);
_viewModelFiles = new ViewModelFiles(FilesAll);
_viewModelLumi = new ViewModelLumi(drive, lengthAllBooks, lengthAllGames, lengthAllMP3);
_viewModelOverview = new ViewModelOverview(AllBooks, AllGames, AllMP3);
_screens[0] = _viewModelOverview;
_screens[1] = _viewModelBooks;
_screens[2] = _viewModelGames;
_screens[3] = _viewModelMusic;
_screens[4] = _viewModelFiles;
_screens[5] = _viewModelVideos;
_screens[6] = _viewModelAdults;
_screens[7] = _viewModelLumi;
SearchText = "";
SelectedItem = _viewModelBooks;
Debug.WriteLine("CALLING ServerCommi! Ring, ring!");
serverComm = new ServerCommi(this);
//serverComm.DownloadBooksAsync();
BluetoothManager = new BluetoothManager();
}
private void ToggleDownloads() {
IsDownloading = !IsDownloading;
serverComm.ToggleDownloads(IsDownloading);
_viewModelBooks.ToggleDownloads(IsDownloading);
}
internal void DownloadStateChange(int mid, int newState) {
_viewModelBooks.DownloadStateChange(mid, newState);
}
// params bool[] isDownload : varargs
// returns the mid
public int AddBookGameToList(FileInfo file, bool isDownload = false) {
BookGame bg = new BookGame(file);
if (isDownload) {
bg.DownloadState = 2;
if (bg.Mid == serverComm.DownloadingMID) {
bg.DownloadState = 1;
}
}
if (bg.Group.StartsWith("B")) {
bg.Group = "Bücher";
}
switch (bg.Group) {
case "Bücher":
if (isDownload) {
BooksToDisplay.Insert(0, bg);
} else {
BooksToDisplay.Add(bg);
}
lengthAllBooks += bg.LengthValue;
break;
case "Spiele":
Games.Add(bg);
lengthAllGames += bg.LengthValue;
break;
default:
Debug.WriteLine("Default: " + bg.Title);
break;
}
return bg.Mid;
}
private void CallBrowser(string url) {
Debug.WriteLine("Url: " + Url);
try {
System.Diagnostics.Process.Start(Url);
} catch (System.ComponentModel.Win32Exception noBrowser) {
if (noBrowser.ErrorCode == -2147467259)
MessageBox.Show(noBrowser.Message);
} catch (System.Exception other) {
MessageBox.Show(other.Message);
}
}
string _searchText;
public string SearchText {
get {
return _searchText;
}
set {
Debug.WriteLine("SearchText Value= " + value);
if (!_toggleLastSearchKeyWasReturn) {
_searchText = value;
SearchResults.Clear();
List<ResultItem> _allBooksRI = new List<ResultItem>();
List<ResultItem> _allBooksHelperList = _allBooks.ToList<ResultItem>();
List<ResultItem> _allGamesHelperList = _allGames.ToList<ResultItem>();
List<ResultItem> _allMP3HelperList = _allMP3.ToList<ResultItem>();
if (SelectedItem != null && SelectedItem.Equals(_viewModelGames)) {
AddAllResultItemsIf(SearchResults, _allGamesHelperList, _searchText);
AddAllResultItemsIf(SearchResults, _allBooksHelperList, _searchText);
AddAllResultItemsIf(SearchResults, _allMP3HelperList, _searchText);
Debug.WriteLine("===================================== Games - " + SearchResults);
Debug.WriteLine("SelectedItem - " + SelectedItem);
} else if (SelectedItem != null && SelectedItem.Equals(_viewModelMusic)) {
AddAllResultItemsIf(SearchResults, _allMP3HelperList, _searchText);
AddAllResultItemsIf(SearchResults, _allGamesHelperList, _searchText);
AddAllResultItemsIf(SearchResults, _allBooksHelperList, _searchText);
Debug.WriteLine("====================================== Music " + SearchResults);
Debug.WriteLine("SelectedItem - " + SelectedItem);
} else {
AddAllResultItemsIf(SearchResults, _allBooksHelperList, _searchText);
AddAllResultItemsIf(SearchResults, _allGamesHelperList, _searchText);
AddAllResultItemsIf(SearchResults, _allMP3HelperList, _searchText);
Debug.WriteLine("====================================== Books " + SearchResults);
}
if (SearchResults.Count == 0) {
SearchResults.Add(new ErrorResultItem("Error", "Nichts passendes gefunden."));
}
} else {
_toggleLastSearchKeyWasReturn = false;
}
}
}
private ObservableCollection<ResultItem> AddAllResultItemsIf(ObservableCollection<ResultItem> searchResults, List<ResultItem> toAdd, string searchText) {
foreach (ResultItem item in toAdd) {
if (item.Title.ToLower().Contains(_searchText.ToLower())) {
searchResults.Add(item);
}
}
return searchResults;
}
public ObservableCollection<ResultItem> SearchResults {
get; set;
}
ResultItem _selectedResult;
public ResultItem SelectedResult {
get {
return _selectedResult;
}
set {
_selectedResult = value;
SearchItemSelected(value);
}
}
private void SearchItemSelected(ResultItem value) {
switch (value.Group) {
case "Bücher":
SelectedItem = _viewModelBooks;
break;
case "Spiele":
SelectedItem = _viewModelGames;
break;
case "Musik":
SelectedItem = _viewModelMusic;
break;
default:
Debug.WriteLine("Search Item Selected, jumped to default: " + value);
break;
}
Unmark(Marked);
Mark(value);
}
ResultItem _marked;
internal void Mark(ResultItem value) {
Marked = value;
value.Marked = true;
}
internal void Unmark(ResultItem value) {
Marked = null;
if (value != null) {
value.Marked = false;
}
}
public ResultItem Marked {
get => _marked;
set => _marked = value;
}
private bool _isSearchResult;
public bool IsSearchResult {
get {
return _isSearchResult;
}
set {
_isSearchResult = value;
Debug.WriteLine("IsSearchResult= " + value);
RaisePropertyChanged();
}
}
private void SearchActivated() {
_toggleLastSearchKeyWasReturn = true;
SelectedItem = _viewModelOverview;
IsSearchResult = true;
}
private object _selectedItem;
public object SelectedItem {
get {
return _selectedItem;
}
set {
_selectedItem = value;
Debug.WriteLine("SELECTED_ITEM SETTER: " + value);
Unmark(Marked);
IsSearchResult = false;
if (SearchText != null) {
SearchText = SearchText;
}
RaisePropertyChanged();
}
}
ViewModelOverview _viewModelOverview;
ViewModelBooks _viewModelBooks;
ViewModelGames _viewModelGames;
ViewModelMusic _viewModelMusic;
ViewModelFiles _viewModelFiles;
ViewModelVideos _viewModelVideos = new ViewModelVideos();
ViewModelAdults _viewModelAdults = new ViewModelAdults();
ViewModelLumi _viewModelLumi;
object[] _screens = new object[8];
public object[] Screens {
get {
return _screens;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged([CallerMemberName] string propertyName = null) {
if (PropertyChanged == null)
return;
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public ICommand MenuCommand {
get; set;
}
public ICommand SearchReturnKeyCommand {
get; set;
}
public ICommand BrowserCommand {
get; set;
}
public ICommand ToggleDownloadsCommand {
get; set;
}
public RelayCommand DeleteCommand {
get;
private set;
}
public List<BookGame> AllGames {
get => _allGames;
set => _allGames = value;
}
public List<BookGame> AllBooks {
get => _allBooks;
set => _allBooks = value;
}
public List<MP3> AllMP3 {
get => _allMP3;
set => _allMP3 = value;
}
public ViewModelBooks ViewModelBooks {
get => _viewModelBooks;
set => _viewModelBooks = value;
}
public ObservableCollection<BookGame> BooksToDisplay {
get => _booksToDisplay;
set => _booksToDisplay = value;
}
public ObservableCollection<BookGame> Games {
get => _games;
set => _games = value;
}
public string Url {
get {
return _url;
}
set {
_url = value;
RaisePropertyChanged();
}
}
public int[] NewBooksMID {
get => _newBooksMID;
set => _newBooksMID = value;
}
public int[] OldBooksMID {
get => _oldBooksMID;
set => _oldBooksMID = value;
}
public Dictionary<int, int> NewBooksVersionNumbers {
get => _newBooksVersionNumbers;
set => _newBooksVersionNumbers = value;
}
public Dictionary<int, int> OldBooksVersionNumbers {
get => _oldBooksVersionNumbers;
set => _oldBooksVersionNumbers = value;
}
public Dictionary<int, int> AllVersionNumbers {
get => _allVersionNumbers;
set => _allVersionNumbers = value;
}
public int[] OldBooksMID1 {
get => _oldBooksMID;
set => _oldBooksMID = value;
}
public List<BookGame> BooksToAdd {
get => _booksToAdd;
set => _booksToAdd = value;
}
public FileInfo[] FilesAll {
get => _filesAll;
set => _filesAll = value;
}
public string FolderPath {
get => _folderPath;
set => _folderPath = value;
}
public int PercentDownloaded {
get {
return _percentDownloaded;
}
set {
_percentDownloaded = value;
RaisePropertyChanged();
}
}
public long DownloadSpeed {
get {
return _downloadSpeed;
}
set {
_downloadSpeed = value;
RaisePropertyChanged();
}
}
public long AmountBytesToDownload {
get {
return _amountBytesToDownload;
}
set {
_amountBytesToDownload = value;
Debug.WriteLine("Property Changed: " + "AmountBytesToDownload");
AmountMBytesToDownload = value / 1024 / 1024;
}
}
public long AmountBytesDownloaded {
get {
return _amountBytesDownloaded;
}
set {
_amountBytesDownloaded = value;
AmountMBytesDownloaded = value / 1024 / 1024;
}
}
public int DownloadTime {
get {
return _downloadTime;
}
set {
_downloadTime = value;
RaisePropertyChanged();
}
}
/*
public bool IsDownloadAborted {
get {
return _isDownloadAborted;
}
set {
_isDownloadAborted = value;
RaisePropertyChanged();
}
}
*/
public long AmountMBytesDownloaded {
get {
return _amountMBytesDownloaded;
}
set {
_amountMBytesDownloaded = value;
RaisePropertyChanged();
}
}
public long AmountMBytesToDownload {
get {
return _amountMBytesToDownload;
}
set {
_amountMBytesToDownload = value;
RaisePropertyChanged();
}
}
public bool IsDownloading {
get {
return _isDownloading;
}
set {
_isDownloading = value;
RaisePropertyChanged();
}
}
internal void SwitchBooks(object o) {
Debug.WriteLine("SwitchBooksEx " + o);
if (o.ToString().Equals("Tessloff.ViewModelBooks")) {
((ViewModelBooks)_screens[0]).SwitchView();
Debug.WriteLine("SwitchBooksIn " + o);
}
}
}
public class CommandViewModel {
private MainWindowViewModel _viewmodel;
public CommandViewModel(MainWindowViewModel viewmodel) {
_viewmodel = viewmodel;
Debug.WriteLine("LALALALALA");
MenuCommand = new RelayCommand(o => {
Debug.WriteLine("CommandViewModel " + o);
_viewmodel.SwitchBooks(o);
});
DeleteCommand = new RelayCommand(o => {
Debug.WriteLine("Delte Command CVM" + o);
});
}
public ICommand MenuCommand {
get; set;
}
public ICommand DeleteCommand {
get; set;
}
public string Title {
get;
private set;
}
}
public class RelayCommand : ICommand {
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
#region Constructors
public RelayCommand(Action<object> execute)
: this(execute, null) {
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute) {
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameter) {
return _canExecute == null ? true : _canExecute(parameter);
}
public event EventHandler CanExecuteChanged {
add {
CommandManager.RequerySuggested += value;
}
remove {
CommandManager.RequerySuggested -= value;
}
}
public void Execute(object parameter) {
_execute(parameter);
}
#endregion // ICommand Members
}
public class ViewModelAdults {
public ViewModelAdults() {
Title = "Erwachsene";
ImgUrl = "/Resources/Erwachsene.png";
}
public string Title {
get; set;
}
public string ImgUrl {
get;
private set;
}
}
Edit tl;dr:
Why do all "direct" properties of MainWindowViewModel update great (like MainWindowViewModel.IsSearchResult), but the two "indirect" properties don't (MainWindowViewModel.BluetoothManager.SelectedBluetoothResult).
List.add() doesnt trigger PropertyChange. You need to use ObservableCollection or raise PropertyChange yourself after adding item.
I have a MVVM program with a model:
public class Deelnemer
{
public int Id { get; set; }
public string Voornaam { get; set; }
public string Werkplek { get; set; }
public List<string> Aanwezig { get; set; }
public Deelnemer()
{
}
}
In my View I have a listBox in which I want to be able to select multiple values (days to put in the list aanwezig).
<ListBox Name="listDagdelen" SelectionMode="Multiple" ItemsSource="{Binding Dagdelen}" SelectedItem="{Binding SelectedDagdeel, Mode=TwoWay}">
The ViewModel looks as follows:
class DeelnemerViewModel : INotifyPropertyChanged
{
#region Private Variables
private readonly Deelnemer dlnObject;
private readonly ObservableCollection<Deelnemer> deelnemers;
private readonly DeelnemerManager deelnemerManager;
private readonly ICommand addDeelnemerCmd;
private readonly ICommand deleteDeelnemerCmd;
#endregion
public ObservableCollection<string> Dagdelen { get; private set; }
#region constructor
public DeelnemerViewModel()
{
Dagdelen = new ObservableCollection<string>() { "maandagochtend", "maandagmiddag", "dinsdagochtend", "dinsdagmiddag", "woensdagochtend", "woensdagmiddag", "donderdagochtend", "donderdagmiddag", "vrijdagochtend", "vrijdagmiddag" };
dlnObject = new Deelnemer();
deelnemerManager = new DeelnemerManager();
deelnemers = new ObservableCollection<Deelnemer>();
addDeelnemerCmd = new RelayCommand(Add, CanAdd);
deleteDeelnemerCmd = new RelayCommand(Delete, CanDelete);
}
#endregion
#region Properties
private string _selectedDagdeel = null;
public string SelectedDagdeel
{
get { return _selectedDagdeel; }
set
{
_selectedDagdeel = value;
dlnObject.Aanwezig.Add(value);
OnPropertyChanged("SelectedDagdeel");
}
}
public int Id
{
get { return dlnObject.Id; }
set
{
dlnObject.Id = value;
OnPropertyChanged("Id");
}
}
public string Voornaam
{
get { return dlnObject.Voornaam; }
set
{
dlnObject.Voornaam = value;
OnPropertyChanged("Voornaam");
}
}
public string Werkplek
{
get { return dlnObject.Werkplek; }
set
{
dlnObject.Werkplek = value;
OnPropertyChanged("Werkplek");
}
}
public List<string> Aanwezig
{
get { return dlnObject.Aanwezig; }
set
{
dlnObject.Aanwezig = value;
OnPropertyChanged("Aanwezig");
}
}
public ObservableCollection<Deelnemer> Deelnemers { get { return deelnemers; } }
public Deelnemer SelectedDeelnemer
{
set
{
Id = value.Id;
Voornaam = value.Voornaam;
Werkplek = value.Werkplek;
Aanwezig = value.Aanwezig;
}
}
#endregion
#region Commands
public ICommand AddDeelnemerCmd { get { return addDeelnemerCmd; } }
public ICommand DeleteDeelnemerCmd { get { return deleteDeelnemerCmd; } }
#endregion
public bool CanAdd(object obj)
{
//Enable the Button only if the mandatory fields are filled
if (Voornaam != string.Empty && Werkplek != string.Empty)
return true;
return false;
}
public void Add(object obj)
{
var deelnemer = new Deelnemer { Voornaam = Voornaam, Werkplek = Werkplek, Aanwezig = Aanwezig };
if (deelnemerManager.Add(deelnemer))
{
Deelnemers.Add(deelnemer);
//string txt = string.Join(String.Empty,Aanwezig);
//MessageBox.Show(txt);
//ResetDeelnemer();
}
else
MessageBox.Show("Vul correcte waardes in!");
}
#region DeleteCommand
private bool CanDelete(object obj)
{
//Enable the Button only if the patients exist
if (Deelnemers.Count > 0)
return true;
return false;
}
private void Delete(object obj)
{
//Delete patient will be successfull only if the patient with this ID exists.
if (!deelnemerManager.Remove(Id))
MessageBox.Show("Deelnemer met dit id bestaat niet!");
else
{
//Remove the patient from our collection as well.
deelnemers.RemoveAt(GetIndex(Id));
ResetDeelnemer();
MessageBox.Show("Deelnemer succesvol verwijderd !");
}
}
#endregion
#region Private Methods
private void ResetDeelnemer()
{
Id = 0;
Voornaam = string.Empty;
Werkplek = string.Empty;
Aanwezig.Clear();
}
private int GetIndex(int Id)
{
for (int i = 0; i < Deelnemers.Count; i++)
if (Deelnemers[i].Id == Id)
return i;
return -1;
}
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
I can't figure out how I should use the List with the listbox values. How do I add (multiple) listbox values to the object's List?
The current code throws a nullreferenceexception at
dlnObject.Aanwezig.Add(value);
You must initialize the Aanwezig property of the Deelnemer object before you can add any values to it, either in the contructor of DeelnemerViewModel:
dlnObject = new Deelnemer();
dlnObject.Aanwezig = new List<string();
...or in the constructor of the Deeelnemer class:
public Deelnemer()
{
Aanwezig = new List<string();
}
IDataErrorInfo: Validating when page submitted
Here I am using IdataErrorInfo
Below is My MainwindowViewModel Class COde
using System;
using System.Collections.Generic;
using System.Linq;
using System.ComponentModel;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using MahApps.Metro;
using MetroDemo.Models;
using System.Windows.Input;
using MahApps.Metro.Controls;
using MahApps.Metro.Controls.Dialogs;
namespace MetroDemo
{
public class AccentColorMenuData
{
public string Name { get; set; }
public Brush BorderColorBrush { get; set; }
public Brush ColorBrush { get; set; }
private ICommand changeAccentCommand;
public ICommand ChangeAccentCommand
{
get { return this.changeAccentCommand ?? (changeAccentCommand = new SimpleCommand { CanExecuteDelegate = x => true, ExecuteDelegate = x => this.DoChangeTheme(x) }); }
}
protected virtual void DoChangeTheme(object sender)
{
var theme = ThemeManager.DetectAppStyle(Application.Current);
var accent = ThemeManager.GetAccent(this.Name);
ThemeManager.ChangeAppStyle(Application.Current, accent, theme.Item1);
}
}
public class AppThemeMenuData : AccentColorMenuData
{
protected override void DoChangeTheme(object sender)
{
var theme = ThemeManager.DetectAppStyle(Application.Current);
var appTheme = ThemeManager.GetAppTheme(this.Name);
ThemeManager.ChangeAppStyle(Application.Current, theme.Item2, appTheme);
}
}
public class MainWindowViewModel : INotifyPropertyChanged, IDataErrorInfo
{
private readonly IDialogCoordinator _dialogCoordinator;
int? _integerGreater10Property;
int? _emptystring;
private bool _animateOnPositionChange = true;
public MainWindowViewModel(IDialogCoordinator dialogCoordinator)
{
_dialogCoordinator = dialogCoordinator;
SampleData.Seed();
// create accent color menu items for the demo
this.AccentColors = ThemeManager.Accents
.Select(a => new AccentColorMenuData() { Name = a.Name, ColorBrush = a.Resources["AccentColorBrush"] as Brush })
.ToList();
// create metro theme color menu items for the demo
this.AppThemes = ThemeManager.AppThemes
.Select(a => new AppThemeMenuData() { Name = a.Name, BorderColorBrush = a.Resources["BlackColorBrush"] as Brush, ColorBrush = a.Resources["WhiteColorBrush"] as Brush })
.ToList();
Albums = SampleData.Albums;
Artists = SampleData.Artists;
FlipViewTemplateSelector = new RandomDataTemplateSelector();
FrameworkElementFactory spFactory = new FrameworkElementFactory(typeof(Image));
spFactory.SetBinding(Image.SourceProperty, new System.Windows.Data.Binding("."));
spFactory.SetValue(Image.HorizontalAlignmentProperty, HorizontalAlignment.Stretch);
spFactory.SetValue(Image.StretchProperty, Stretch.Fill);
FlipViewTemplateSelector.TemplateOne = new DataTemplate()
{
VisualTree = spFactory
};
FlipViewImages = new string[] { "http://trinities.org/blog/wp-content/uploads/red-ball.jpg", "http://savingwithsisters.files.wordpress.com/2012/05/ball.gif" };
RaisePropertyChanged("FlipViewTemplateSelector");
BrushResources = FindBrushResources();
CultureInfos = CultureInfo.GetCultures(CultureTypes.InstalledWin32Cultures).ToList();
}
public string Title { get; set; }
public int SelectedIndex { get; set; }
public List<Album> Albums { get; set; }
public List<Artist> Artists { get; set; }
public List<AccentColorMenuData> AccentColors { get; set; }
public List<AppThemeMenuData> AppThemes { get; set; }
public List<CultureInfo> CultureInfos { get; set; }
public int? IntegerGreater10Property
{
get { return this._integerGreater10Property; }
set
{
if (Equals(value, _integerGreater10Property))
{
return;
}
_integerGreater10Property = value;
RaisePropertyChanged("IntegerGreater10Property");
}
}
public string FirstName { get; set; }
public string LastName { get; set; }
DateTime? _datePickerDate;
public DateTime? DatePickerDate
{
get { return this._datePickerDate; }
set
{
if (Equals(value, _datePickerDate))
{
return;
}
_datePickerDate = value;
RaisePropertyChanged("DatePickerDate");
}
}
bool _magicToggleButtonIsChecked = true;
public bool MagicToggleButtonIsChecked
{
get { return this._magicToggleButtonIsChecked; }
set
{
if (Equals(value, _magicToggleButtonIsChecked))
{
return;
}
_magicToggleButtonIsChecked = value;
RaisePropertyChanged("MagicToggleButtonIsChecked");
}
}
private bool _quitConfirmationEnabled;
public bool QuitConfirmationEnabled
{
get { return _quitConfirmationEnabled; }
set
{
if (value.Equals(_quitConfirmationEnabled)) return;
_quitConfirmationEnabled = value;
RaisePropertyChanged("QuitConfirmationEnabled");
}
}
}
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises the PropertyChanged event if needed.
/// </summary>
/// <param name="propertyName">The name of the property that changed.</param>
protected virtual void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public string this[string columnName]
{
get
{
if (columnName == "IntegerGreater10Property" && this.IntegerGreater10Property < 10)
{
return "Number is not greater than 10!";
}
if (columnName == "DatePickerDate" && this.DatePickerDate == null)
{
return "No Date given!";
}
if (columnName == "FirstName")
{
if (string.IsNullOrEmpty(FirstName) || FirstName.Length < 3)
return "Please Enter First Name";
}
if (columnName == "LastName")
{
if (string.IsNullOrEmpty(FirstName) || FirstName.Length < 3)
return "Please Enter Last Name";
}
return null;
}
}
public string Error { get { return string.Empty; } }
private ICommand singleCloseTabCommand;
public ICommand SingleCloseTabCommand
{
get
{
return this.singleCloseTabCommand ?? (this.singleCloseTabCommand = new SimpleCommand
{
CanExecuteDelegate = x => true,
ExecuteDelegate = async x =>
{
await ((MetroWindow) Application.Current.MainWindow).ShowMessageAsync("Closing tab!", string.Format("You are now closing the '{0}' tab", x));
}
});
}
}
private ICommand neverCloseTabCommand;
public ICommand NeverCloseTabCommand
{
get { return this.neverCloseTabCommand ?? (this.neverCloseTabCommand = new SimpleCommand { CanExecuteDelegate = x => false }); }
}
private ICommand showInputDialogCommand;
public ICommand ShowInputDialogCommand
{
get
{
return this.showInputDialogCommand ?? (this.showInputDialogCommand = new SimpleCommand
{
CanExecuteDelegate = x => true,
ExecuteDelegate = x =>
{
_dialogCoordinator.ShowInputAsync(this, "From a VM", "This dialog was shown from a VM, without knowledge of Window").ContinueWith(t => Console.WriteLine(t.Result));
}
});
}
}
private ICommand showProgressDialogCommand;
public ICommand ShowProgressDialogCommand
{
get
{
return this.showProgressDialogCommand ?? (this.showProgressDialogCommand = new SimpleCommand
{
CanExecuteDelegate = x => true,
ExecuteDelegate = x => RunProgressFromVm()
});
}
}
}
}
Below is My XAml Code
<TextBox Name="txt_LA_FirstName"
Controls:TextBoxHelper.Watermark="First Name"
Controls:TextBoxHelper.UseFloatingWatermark="True"
ToolTip="First Name" Grid.Row="2" Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Stretch" Text="{Binding FirstName, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged, NotifyOnValidationError=True}" />
Event Is Raising at form load and showing Error Messages Before user enters any Value> I need to show Error Messages After Button Click Event
The reason why you get it is because the content of your indexer is static, so the View validates it during the binding. You should only validate it when the property changes though.
You should have a backing field for an errors dictionary with Add/Remove methods, like this
private readonly Dictionary<string, string> errors = new Dictionary<string, string>();
public string this[string columnName]
{
get
{
string errorMessage = null;
if (errors.TryGetValue(columnName, errorMessage)
{
return errorMessage;
}
return null;
}
}
protected void AddErrorMessage(string columnName, string errorMessage)
{
errors[columnName] = errorMessage;
}
protected void RemoveErrorMessage(string columnName)
{
if(errors.ContainsKey(columnName))
{
errors.Remove(columnName);
}
}
protected virtual void Validate()
{
errors.Clear();
if (this.IntegerGreater10Property < 10)
{
this.AddErrorMessage("IntegerGreater10Property", "Number is not greater than 10!");
}
if (this.DatePickerDate == null)
{
this.AddErrorMessage("DatePickerDate", "No Date given!");
}
if (string.IsNullOrEmpty(FirstName) || FirstName.Length < 3)
{
this.AddErrorMessage("FirstName", "Please Enter First Name");
}
if (string.IsNullOrEmpty(LastName) || Lastname.Length < 3)
{
this.AddErrorMessage("LastName", "Please Enter Last Name");
}
}
And then remove them with RemoveMessage("FirstName") or clear it up on validation. You should implement this in a base class of course, to avoid repeating this code over and over again and just override the Validate() method in your ViewModel classes
Thanks for your reply
Even I tried it I had solved it By creating on Bool variable and declaring it as False in MainwindowView model class.
Unless and Until the Bool Variable is True it wont Validates
On Button click Event I will Enable the bool Variable to True then It starts Validates on Button CLick
if (Enable)
{
if (columnName == "IntegerGreater10Property" && this.IntegerGreater10Property < 10)
{
return "Number is not greater than 10!";
}
if (columnName == "DatePickerDate" && this.DatePickerDate == null)
{
return "No Date given!";
}
if (columnName == "FirstName")
{
if (string.IsNullOrEmpty(FirstName) || FirstName.Length < 3)
return "Please Enter First Name";
}
if (columnName == "LastName")
{
if (string.IsNullOrEmpty(FirstName) || FirstName.Length < 3)
return "Please Enter Last Name";
}
}
return null;
Below is My Button click Event (On Button I will Enable the Bool Property to true
var x = (MainWindowViewModel)DataContext;
x.Enable = true;
var binding = txt_LA_FirstName.GetBindingExpression(TextBox.TextProperty);
binding.UpdateSource();
My Model :
public class Cont: INotifyPropertyChanged
{
private string _Contact;
public string Contact
{
get { return _Contact; }
set
{
_Contact= value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Contact"));
}
}
private string _Age;
public string Age
{
get { return _Age; }
set
{
_Age = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Age"));
}
}
My CollectionViewSource:
<CollectionViewSource x:Key="group" Source="{Binding GroupedCollection}" ItemsPath="{Binding GroupedCollection[0].ContactColl}" IsSourceGrouped="True">
</CollectionViewSource>
My GridView :
<GridView ItemsSource="{Binding Source={StaticResource group}}"
SelectionMode="None" ScrollViewer.VerticalScrollBarVisibility="Hidden">
<GridView.GroupStyle>
<GroupStyle HidesIfEmpty="True">
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock x:Name="keyHeader" Text="{Binding Key}"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</GridView.GroupStyle>
<GridView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Contact}"/>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
My ViewModel :
public class ContVM: INotifyPropertyChanged
{
public ObservableCollection<Cont> Contlist = new ObservableCollection<Cont>();
private ObservableCollection<ContItem> _GroupedCollection;
public ObservableCollection<ContItem> GroupedCollection
{
get
{
if (_GroupedCollection == null)
{
_GroupedCollection= new ObservableCollection<ContItem>();
}
return _GroupedCollection;
}
set
{
_GroupedCollection = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("GroupedCollection"));
}
}
public void SetInitialCollection()
{
var keyList = Contlist .GroupBy(c => c.Age).Select(g => g.Key);
foreach (var key in keyList)
{
var contItem= new ContItem();
contItem.Key = key;
var contList = Contlist.Where(c => c.Age == key);
foreach (var item in contList )
{
contItem.ContactColl.Add(item);
}
GroupedCollection.Add(contItem);
}
}
public void AddNew(Cont Item)
{
var contItem = GroupedCollection.FirstOrDefault(g => g.Key == Item.Age );
if ( contItem != null )
{
contItem.ContactColl.Add(Item);
}
else
{
contItem = new ContItem();
contItem.Key = Item.Age ;
GroupedCollection.Add(contItem);
}
}
public void Delete(Cont Item)
{
var contItem = GroupedCollection.FirstOrDefault(g => g.Key == Item.Age);
if (contItem != null)
{
if (contItem.ContactColl.Contains(Item))
{
contItem.ContactColl.Remove(Item);
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class ContItem : INotifyPropertyChanged
{
public string Key { get; set; }
private ObservableCollection<Cont> _ContactColl;
public ObservableCollection<Cont> ContactColl
{
get
{
if (_ContactColl== null)
{
_ContactColl= new ObservableCollection<Cont>();
}
return _ContactColl;
}
set
{
_ContactColl= value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("ContactColl"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
My code-behind cs :
ContVM vm = new ContVM();
private void Add_new_cont_Click(object sender, RoutedEventArgs e)
{
// a button that add a new data from 2 textbox
Cont item = new Cont();
item.Contact = textbox1.Text;
item.Contact = textbox2.Text;
vm.AddNew(item); // error line
}
Already tried this, but I get error 0xC0000005: Access violation reading location 0x00000004. and it redirect me at line which is vm.AddNew(item);. All these codes have been updated/guided by #Bells.
Each time when a new item is being added to the collection, you are regrouping the items and resetting the source. That will cause the grid view to reset its position. In order to maintain the position, you have to assign a source collection initially as the ItemsSource and manipulations should be done on the same collection.
public class Cont
{
public string Contact { get; set; }
public string Age { get; set; }
}
public class ContItem
{
public string Key { get; set; }
private ObservableCollection<Cont> _contactColl;
public ObservableCollection<Cont> ContactColl
{
get
{
if (_contactColl == null)
_contactColl = new ObservableCollection<Cont>();
return _contactColl;
}
}
}
public class TestClass
{
public ObservableCollection<Cont> Contlist = new ObservableCollection<Cont>();
private ObservableCollection<ContItem> _groupedCollection;
public ObservableCollection<ContItem> GroupedCollection
{
get
{
if (_groupedCollection == null)
_groupedCollection = new ObservableCollection<ContItem>();
return _groupedCollection;
}
}
public void SetInitialCollection()
{
//Add the existing items in ContactList (if any) to a grouped collection.
var keyList = Contlist.GroupBy(c => c.Age).Select(g => g.Key);
foreach (var key in keyList)
{
var contItem = new ContItem();
contItem.Key = key;
var contList = Contlist.Where(c => c.Age == key);
foreach (var item in contList)
{
contItem.ContactColl.Add(item);
}
GroupedCollection.Add(contItem);
}
}
public void AddNewItem()
{
var cont = new Cont();
cont.Age = "32";
cont.Contact = "";
var contItem = GroupedCollection.FirstOrDefault(g => g.Key == cont.Age);
if (contItem != null)
{
contItem.ContactColl.Add(cont);
}
else
{
contItem = new ContItem();
contItem.Key = cont.Age;
contItem.ContactColl.Add(cont);
GroupedCollection.Add(contItem);
}
}
public void DeleteItem(Cont cont)
{
var contItem = GroupedCollection.FirstOrDefault(g => g.Key == cont.Age);
if (contItem != null)
{
if (contItem.ContactColl.Contains(cont))
{
contItem.ContactColl.Remove(cont);
}
}
}
}
<CollectionViewSource x:Key="group" Source="{Binding GroupedCollection}"
ItemsPath="ContactColl" IsSourceGrouped="True" />