【问题标题】:Updating items at UI on an ObservableCollection using MVVM Light使用 MVVM Light 在 ObservableCollection 上更新 UI 上的项目
【发布时间】:2014-05-10 19:18:41
【问题描述】:

所以我在视图模型上有一个ObservableCollection<Term>,其中Term 项具有一些数据属性。

Term类:

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()
        {
        }
    }
}

这是有问题的具有 ObservableCollection 的 ViewModel(ProjectBaseViewModel 实现了 MVVM Light 的 ViewModelBase,并且我认为这里不需要的属性很少):

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;
        }
    }
}

问题:当然,从另一个页面添加和删除术语效果很好。但是,当 Term 项目更新时,只有标题会更新。

这是视图中的内容:

            <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 不会在两个日期值发生更改时更新。但是,如果要查看编辑页面,则更改会反映在 DatePickers 中。

有什么方法可以让DurationText 通过绑定进行相应更新,以便与使用MVVM Light的实现一起工作?

I have tried this 但错误提示:

类型“Neomilano.Model.Term”不能用作类型参数“T” 在泛型类型或方法中 'Neomilano.Common.ItemsChangeObservableCollection'。没有 从 'Neomilano.Model.Term' 到的隐式引用转换 'System.ComponentModel.INotifyPropertyChanged'。

项目规格: * 这是一个 WinRT 应用程序 * 这是一个通用的 Windows 应用程序,但我现在首先在 Windows Phone 上工作。我认为这无关紧要,因为有收敛赌注。 WinRT 和 WinPRT,但谁知道这可能是问题所在 * 正如我所提到的,我正在使用 MVVM Light(仅限库)。

那么,一般来说,如何使用 MVVM Light 在 WinRT 中更新 ObservableCollection 项的内容

感谢您的帮助!

【问题讨论】:

    标签: c# windows-runtime winrt-xaml mvvm-light


    【解决方案1】:

    你不需要链接文章中的任何内容,你只需要Term 来实现INotifyPropertyChangedObservableCollection&lt;T&gt; 只通知绑定到它的ListView 集合的内容 发生了变化,即通知添加、删除、移动、替换、重置等。它不监视其中的项目更改集合。

    在这种情况下,您可以使Term 派生自ViewModelBase,因为它包含INotifyPropertyChanged 的实现。

    下面是一个例子。请注意TermStart 的设置器如何也引发DurationText 的更改事件,因为此属性是依赖的。

    public class Term : ViewModelBase
    {
        private int _id;
        private string _name;
        private DateTimeOffset _termStart;
        private DateTimeOffset _termEnd;
    
        public int Id
        {
            get { return _id; }
            set
            {
                if (value == _id) return;
                _id = value;
                RaisePropertyChanged("Id");
            }
        }
    
        public string Name
        {
            get { return _name; }
            set
            {
                if (value == _name) return;
                _name = value;
                RaisePropertyChanged("Name");
            }
        }
    
        public DateTimeOffset TermStart
        {
            get { return _termStart; }
            set
            {
                if (value.Equals(_termStart)) return;
                _termStart = value;
                RaisePropertyChanged("TermStart");
                RaisePropertyChanged("DurationText");
            }
        }
    
        public DateTimeOffset TermEnd
        {
            get { return _termEnd; }
            set
            {
                if (value.Equals(_termEnd)) return;
                _termEnd = value;
                RaisePropertyChanged("TermEnd");
                RaisePropertyChanged("DurationText");
            }
        }
    
        public string DurationText
        {
            get { return "from " + TermStart.Date.ToString("MMMM d, yyyy") + " to " + TermEnd.Date.ToString("MMMM d, yyyy"); }
        }
    }
    

    【讨论】:

    • 我还能继续使用Set(() =&gt; Property, ref _oldProperty, value); 吗?
    • 当然 - 在示例中使用它而不是 check / set / raise。 Set 方法在后台执行此操作。
    猜你喜欢
    • 1970-01-01
    • 2023-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-18
    相关资源
    最近更新 更多