So I have an ObservableCollection<Term>
on a view model, with Term
items having some data properties.
Term
class:
using SQLite;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Neomilano.Model
{
public class Term
{
public int ID { get; set; }
public string Name { get; set; }
public DateTimeOffset TermStart { get; set; }
public DateTimeOffset TermEnd { get; set; }
public string DurationText
{
get
{
return "from " + TermStart.Date.ToString("MMMM d, yyyy") + " to " + TermEnd.Date.ToString("MMMM d, yyyy");
}
}
public Term()
{
}
}
}
This is the ViewModel in question that has the ObservableCollection (ProjectBaseViewModel
implements MVVM Light's ViewModelBase
and has few properties that I don't think is necessary here):
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Threading;
using Microsoft.Practices.ServiceLocation;
using Neomilano.Model;
using Neomilano.Services;
using SQLite;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using Windows.Foundation;
namespace Neomilano.ViewModel
{
public partial class TermsViewModel : ProjectBaseViewModel
{
public IDialogService DialogService
{
get { return ServiceLocator.Current.GetInstance<IDialogService>(); }
}
#region properties
private ObservableCollection<Term> _terms;
public ObservableCollection<Term> Terms {
get { return this._terms; }
private set { Set(() => Terms, ref _terms, value); }
}
#endregion
#region commands
private RelayCommand<int> _deleteTermCommand;
public RelayCommand<int> DeleteTermCommand
{
get
{
return _deleteTermCommand ??
(_deleteTermCommand = new RelayCommand<int>(async id => {
var t = "Are you sure you want to delete this term?";
var c = "This cannot be undone.";
var ct = "delete";
bool deleteConfirmed = await DialogService.ShowConfirmationDialogAsync(t, c, ct);
if (deleteConfirmed == true)
await ExecuteDeleteTermAsync(id);
}));
}
}
#endregion
public TermsViewModel()
{
if (IsInDesignMode)
{
List<Term> t = new List<Term>();
t.Add(new Term() { ID=1, Name = "Sample Term 1", TermStart = DateTimeOffset.Parse("October 1, 2013"), TermEnd = DateTimeOffset.Parse("December 17, 2013") });
t.Add(new Term() { ID=2, Name="Sample Term 2", TermStart=DateTimeOffset.Parse("January 1, 2014"), TermEnd=DateTimeOffset.Parse("April 30, 2014") });
Terms = new ObservableCollection<Term>(t);
}
}
/// <summary>
/// Gets the list of Terms from the database and adds it to the Terms property of the ViewModel.
/// </summary>
/// <returns></returns>
public async Task GetTermsListAsync()
{
IsProgressEnabled = true;
List<Term> list = new List<Term>();
await Task.Run(() =>
{
using (var db = new SQLiteConnection(app.DBFileName))
{
list = db.Table<Term>().ToList<Term>();
db.Close();
}
});
Terms = new ObservableCollection<Term>(list);
IsProgressEnabled = false;
}
/// <summary>
/// Returns the term ID of the selected term from the view.
/// </summary>
/// <param name="clickedItem"></param>
/// <returns></returns>
public Term ReturnSelectedTerm(object clickedItem)
{
return (Term)clickedItem;
}
public async Task ExecuteDeleteTermAsync(int termId)
{
IsProgressEnabled = true;
Term t = new Term();
await Task.Run(() =>
{
using (var db = new SQLiteConnection(app.DBFileName))
{
var q = db.Table<Term>().Where(tr => tr.ID.Equals(termId));
t = q.First<Term>();
db.Delete<Term>(termId);
db.Close();
}
});
var target = Terms.Single<Term>(tr => tr.ID.Equals(termId));
Terms.Remove(target);
IsProgressEnabled = false;
}
}
}
THE ISSUE: Sure, adding and removing terms from another page work well. When the Term
items get updated, however, only the title gets updated.
Here's what's on the view:
<ListView
x:Name="TermsListView"
ItemsSource="{Binding Terms}"
IsItemClickEnabled="True"
ItemClick="OnTermItemClick">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Holding="OnTermItemHold">
<FlyoutBase.AttachedFlyout>
<MenuFlyout x:Name="TermItemContextMenu">
<MenuFlyoutItem Text="edit" Command="{Binding TermsViewModel.NavigateToFormCommand, Mode=OneWay, Source={StaticResource Locator}}" CommandParameter="{Binding}" />
<MenuFlyoutItem Text="delete" Command="{Binding TermsViewModel.DeleteTermCommand, Mode=OneWay, Source={StaticResource Locator}}" CommandParameter="{Binding ID}" />
</MenuFlyout>
</FlyoutBase.AttachedFlyout>
<TextBlock Text="{Binding Name}" Style="{ThemeResource ListViewItemTextBlockStyle}"/>
<TextBlock Text="{Binding DurationText}" Style="{ThemeResource ListViewItemSubheaderTextBlockStyle}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Term.DurationText
does not update when the two date values have changed. If the edit page is to be seen however, the change is reflected in the DatePickers.
Is there any way that I can make DurationText
update accordingly through Binding in such a way that it works with an implementation using MVVM Light?
I have tried this but the error says:
The type 'Neomilano.Model.Term' cannot be used as type parameter 'T' in the generic type or method 'Neomilano.Common.ItemsChangeObservableCollection'. There is no implicit reference conversion from 'Neomilano.Model.Term' to 'System.ComponentModel.INotifyPropertyChanged'.
Project specs: * This is a WinRT app * It's a universal Windows App, but I'm working on Windows Phone first right now. I think this doesn't matter because of the convergence bet. WinRT and WinPRT, but who knows this might be the problem * As I have mentioned, I'm using MVVM Light(libraries only).
So, in general, how can I update the contents of an item of an ObservableCollection in WinRT using MVVM Light?
Thank you for your help!