【问题标题】:GUI/view doesn't notice about changes in the viewmodel. Who should notify?GUI/视图不会注意到视图模型的变化。谁应该通知?
【发布时间】:2011-09-28 06:45:39
【问题描述】:

我是 Silverlight 的新手,对通知机制有疑问。我的解决方案是这样堆叠的 MVVM 应用程序:

VIEW 包含一个绑定到viewmodel中的一个集合的RadGridView,数据是一个entitycollection。 GridView 的 SelectedItem 绑定到 viewmodel 中的相应属性。

视图模型 保存 GridView 绑定到的下面的属性并实现 INotifyPropertyChanged。 •SelectList - 一个继承ObservableCollection 的实体集合。当 SelectList 被设置时,它会运行一个通知调用。 •SelectedItem - 一个也为自己的属性实现INotifyPropertyChanged 的​​实体。设置 SelectedItem 后,它会运行通知调用。

我的问题是,谁应该发出通知调用,以便 GridView 知道值已更改?有时,实体中的属性会直接在视图模型中以编程方式设置。就目前而言,尽管属性正确获取了新值,但 GUI 中没有发生任何事情。

尊敬的同学

-- 使用代码更新 --------------

查看

<UserControl 
    xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
    x:Class="X.Y.Z.MonthReport.MonthReportView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:toolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot">
        <telerik:RadGridView x:Name="MonthReportGrid"
                             Grid.Row="1"
                             ItemsSource="{Binding SelectList}"
                             SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
                             AutoGenerateColumns="False">
            <telerik:RadGridView.Columns>
                <!-- The other columns have been cut out of this example -->
                <telerik:GridViewDataColumn DataMemberBinding="{Binding curDate, Mode=TwoWay, TargetNullValue=''}" DataFormatString="{} {0:d}" Header="Avläst datum" UniqueName="curDate" IsVisible="True" IsReadOnly="False">
                    <telerik:GridViewDataColumn.CellEditTemplate>
                        <DataTemplate>
                            <telerik:RadDateTimePicker SelectedValue="{Binding curDate, Mode=TwoWay, TargetNullValue=''}" InputMode="DatePicker" DateTimeWatermarkContent="ÅÅÅÅ-MM-DD" />
                        </DataTemplate>
                    </telerik:GridViewDataColumn.CellEditTemplate>
                </telerik:GridViewDataColumn>
                <telerik:GridViewDataColumn DataMemberBinding="{Binding curValue, Mode=TwoWay, TargetNullValue=''}" Header="Avläst värde" UniqueName="curValue" IsVisible="True" IsReadOnly="False" />
        </telerik:RadGridView>
    </Grid>
</UserControl>

查看.CS

using System;
using System.Collections.Generic;
using System.Windows.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Windows.Controls;
using Telerik.Windows.Controls;
using Telerik.Windows.Controls.GridView;


namespace X.Y.Z.MonthReport
{

    public partial class MonthReportView : UserControl, IMonthReportView
    {
        /// <summary>
        /// ViewModel attached to the View
        /// </summary>
        public IMonthReportViewModel Model
        {
            get {   return this.DataContext as IMonthReportViewModel; }
            set {   this.DataContext = value; }
        }

        public MonthReportView()
        {
            InitializeComponent();
            this.MonthReportGrid.CellEditEnded += new EventHandler<GridViewCellEditEndedEventArgs>(MonthReportGrid_OnCellEditEnded);
        }


        public void MonthReportGrid_OnCellEditEnded(object sender, GridViewCellEditEndedEventArgs e)
        {
            if (e.Cell.Column.UniqueName == "curValue")
            {
                // ...
                this.Model.SetAutomaticReadingDate();
            }

            if (e.Cell.Column.UniqueName == "curDate")
            {
                this.Model.UpdateAutomaticReadingDate();
            }
        }
    }
}

视图模型

using System;
using Microsoft.Practices.Prism.Events;
using Microsoft.Practices.Prism.Modularity;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Prism.Commands;


namespace X.Y.Z.MonthReport
{
    public class MonthReportViewModel : ViewModel<IMonthReportView>, IMonthReportViewModel
    {
        private readonly IEventAggregator eventAggregator;
        private readonly IMonthReportService dataService;
        private readonly IMonthReportController dataController;


        private DateTime? _newReadingDate;
        public DateTime? NewReadingDate
        {
            get { return _newReadingDate; }
            set { _newReadingDate = value; }
        }

        //Holds the selected entity
        private MonthReportEntity _selectedItem;
        public MonthReportEntity SelectedItem
        {
            get { return _selectedItem; }
            set
            {
                if (_selectedItem != value)
                {
                    _selectedItem = value;
                    //The INotifyPropertyChanged implementation inherited from ViewModel-base.
                    Notify(() => this.SelectedItem);
                }
            }
        }

        //The entitycollection
        private MonthReportEntityCollection _selectList;
        public MonthReportEntityCollection SelectList
        {
            get { return _selectList; }
            set
            {
                if (_selectList != value)
                {
                    _selectList = value;
                    //The INotifyPropertyChanged implementation inherited from ViewModel-base.
                    Notify(() => this.SelectList);
                }
            }
        }

        public MonthReportViewModel(IMonthReportView view,
            IEventAggregator eventAggregator, IMonthReportService dataService, IMonthReportController dataController)
        {
            this.InitializeCommands();
            this.eventAggregator = eventAggregator;
            this.dataController = dataController;
            this.dataService = dataService;
            this.View = view;
            this.View.Model = this;

            dataService.onGetMonthReportComplete += new EventHandler<MonthReportEventArgs>(OnGetMonthReportComplete);
            dataService.onSaveMonthReportComplete += new EventHandler<MonthReportEventArgs>(OnSaveMonthReportComplete);

            InitializeData();
        }

        public void InitializeCommands()
        {
            // ...
        }

        public void InitializeData()
        {
            GetMonthReport();
        }

        //This function is not working as I want it to.
        //The gridview doesn't notice the new value.
        //If a user edits the grid row, he should not need to
        //add the date manually, Therefor I use this code snippet.
        public void SetAutomaticReadingDate()
        {
            if ((NewReadingDate.HasValue) && (!SelectedItem.curDate.HasValue))
            {
                SelectedItem.curDate = NewReadingDate;
                //The INotifyPropertyChanged implementation inherited from ViewModel-base.
                Notify(() => this.SelectedItem.curDate);
            }
        }

        public void GetMonthReport()
        {
            dataService.GetMonthReport();
        }

        public void SaveMonthReport()
        {
            dataService.SaveMonthReport(SelectList);            
        }

        void OnGetMonthReportComplete(object sender, MonthReportEventArgs e)
        {
            // ...
        }

        void OnSaveMonthReportComplete(object sender, MonthReportEventArgs e)
        {
            // ...       
        }

        #region ICleanable
        public override void Clean()
        {
            base.Clean();
        }
        #endregion
    }
}

【问题讨论】:

  • 您是否在后台线程中操作视图模型?如果没有,请发布一些代码

标签: silverlight silverlight-4.0 mvvm


【解决方案1】:

如果你这样绑定

<telerik:GridViewDataColumn DataMemberBinding="{Binding curValue, Mode=TwoWay, TargetNullValue=''}" Header="Avläst värde" UniqueName="curValue" IsVisible="True" IsReadOnly="False" />

你只需要查看绑定就知道你必须在哪里调用 PropertyChanged 并且你的绑定说:

具有“curValue”属性的类必须实现 INotifyProperyChanged 才能通知视图。

  public void SetAutomaticReadingDate()
    {
        if ((NewReadingDate.HasValue) && (!SelectedItem.curDate.HasValue))
        {
            //this is enough if the class of SelectedItem implements INotifyPropertyChanged
            //and the curDate Poperty raise the event 
            SelectedItem.curDate = NewReadingDate;               
        }
    }

顺便说一句,将属性命名为 curDate 的代码样式不好!应该是 CurDate,带有 camlCase 的属性伤害了我的眼睛 :)

【讨论】:

  • 谢谢!它为我解决了这个问题,但一开始没有。 INotifyPropertyChanged 的​​实现在基础实体中不正确,所以我也遇到了问题。
【解决方案2】:

您的“MonthReportEntityCollection”必须实现接口“INotifyCollectionChanged”,以允许通知 UI 有关集合更改(项目添加/删除)。 您的“MonthReportEntity”必须实现接口“INotifyPropertyChanged”以允许通知 UI 实体的属性更改。 其他东西看起来是正确的。

【讨论】:

  • 集合应该是一个 ObservableCollection,它会通知集合的变化,不需要创建一个新的集合。
  • 对齐,并不总是 ObservableCollection 提供足够的功能(例如,对于分页支持,最好使用 PagedCollectionView)。如果您需要任何具有附加功能的自定义集合,则必须实现 INotifyCollectionChanged 以通知 UI 有关集合更改。毫无疑问,您可以从 ObservableCollection 继承您的自定义集合。
猜你喜欢
  • 2013-07-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-27
相关资源
最近更新 更多