GUI doesn't reflect changes

6 posts, 1 answers
  1. Clas Ericson
    Clas Ericson avatar
    31 posts
    Member since:
    Sep 2011

    Posted 26 Sep 2011 Link to this post

    Hello,
    I'm stuck with a problem that I can't solve although spending hours of googling for tips on the web. It's a MVVM application where the
     viewmodel has a list of entities and an implementation of INotifyPropertyChanged. The view uses RadGridView to display the entities.
     
    The problem occurs when I'm setting a property in one of the entities in the code of the viewmodel. The property gets the value correctly but the GUI doesn't update. I'm thankful for any help I can get, have been spending hours trying to solve this wiht no success. For code excerpts, se below.

    Regards, Clas


    VIEW
    <UserControl
        x:Class="X.Y.Z.MonthReport.MonthReportView"
        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" Background="White">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
     
            
            <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>
    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.BeginningEdit += new EventHandler<GridViewBeginningEditRoutedEventArgs>(MonthReportGrid_OnBeginningEdit);
                this.MonthReportGrid.CellEditEnded += new EventHandler<GridViewCellEditEndedEventArgs>(MonthReportGrid_OnCellEditEnded);
                this.MonthReportGrid.SelectionChanged += new EventHandler<SelectionChangeEventArgs>(MonthReportGrid_OnSelectionChanged);
            }
     
         
            void MonthReportGrid_OnBeginningEdit(object sender, GridViewBeginningEditRoutedEventArgs e)
            {
                // ...
            }
     
            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();
                }
            }
     
            public void MonthReportGrid_OnSelectionChanged(object sender, SelectionChangeEventArgs e)
            {
                // ...
            }
        }
    }

    VIEWMODEL
    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 bool isLoading;
            public bool IsLoading
            {
                get { return isLoading; }
                set {
                    if (isLoading != value)
                    {
                        isLoading = value;
                        Notify(() => this.IsLoading);
                    }
                }
            }
            private DateTime? _newReadingDate;
            public DateTime? NewReadingDate
            {
                get { return _newReadingDate; }
                set { _newReadingDate = value; }
            }
            private MonthReportEntity _selectedItem;
            public MonthReportEntity SelectedItem
            {
                get { return _selectedItem; }
                set
                {
                    if (_selectedItem != value)
                    {
                        _selectedItem = value;
                        Notify(() => this.SelectedItem);
                    }
                }
            }
            private MonthReportEntityCollection _selectList;
            public MonthReportEntityCollection SelectList
            {
                get { return _selectList; }
                set
                {
                    if (_selectList != value)
                    {
                        _selectList = value;
                        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.IsLoading = true;
            }
     
            public void GetMonthReport()
            {
                dataService.GetMonthReport();
            }
     
            public void SaveMonthReport()
            {
                dataService.SaveMonthReport(SelectList);           
            }
     
            public void UpdateAutomaticReadingDate()
            {
                if (SelectedItem.curDate.HasValue)
                    NewReadingDate = SelectedItem.curDate;
            }
             
            public void SetAutomaticReadingDate()
            {
                if ((NewReadingDate.HasValue) && (!SelectedItem.curDate.HasValue))
                {
                    SelectedItem.curDate = NewReadingDate;
                    Notify(() => this.SelectedItem.curDate);
                }
            }
     
     
            void OnGetMonthReportComplete(object sender, MonthReportEventArgs e)
            {
                if (!e.HasError)
                    this.SelectList = e.MonthReportData;
                else
                {
                    this.SelectList = null;
                    eventAggregator.GetEvent<ErrorEvent>().Publish(new ErrorEvent
                    {
                        Message = e.Error.ToString(),
                        ExceptionObj = e.Error
                    });
                }
     
                this.IsLoading = false;
            }
     
            void OnSaveMonthReportComplete(object sender, MonthReportEventArgs e)
            {
                if (!e.HasError)
                {
                    // Is it really necessary to re-set the SelectList with the same data again?
                    //this.SelectList = e.MonthReportData;
                    eventAggregator.GetEvent<MessageShowEvent>().Publish(new MessageShowEvent
                        {
                            MessageTitle = "Bekräftelse",
                            Message = "Månadsrapporteringen sparades."
                        });
                }
                else
                {
                    this.SelectList = null;
                    eventAggregator.GetEvent<ErrorEvent>().Publish(new ErrorEvent
                    {
                        Message = e.Error.ToString(),
                        ExceptionObj = e.Error
                    });
                }
     
                this.IsLoading = false;
            }
     
            #region ICleanable
            public override void Clean()
            {
                base.Clean();
                //Remove existing references, for example eventhandlers added in constructor
                //Unsubscribe to subscribed events.
            }
            #endregion
        }
    }

    VIEWMODEL BASE CLASS
    using System;
    using System.ComponentModel;
    using System.Linq.Expressions;
    using System.Windows;
    using System.Windows.Threading;
    using X.Y.Z.Common.Extensions;
     
    namespace X.Y.Z.Common
    {
        public class ViewModel<TView> : ViewModel
        {
            public TView View { get; protected set; }
     
            public override void Clean()
            {
                base.Clean();
     
                if (View is FrameworkElement)
                {
                    FrameworkElement element = View as FrameworkElement;
                    element.RemoveFromParentPanel();
                }
            }
        }
     
        public class ViewModel : INotifyPropertyChanged, IDisposable, ICleanable
        {
            public ViewModel() { }
     
            public void Notify<TValue>(Expression<Func<TValue>> propertySelector)
            {
                if (PropertyChanged != null)
                {
                    var memberExpression = propertySelector.Body as MemberExpression;
                    if (memberExpression != null)
                    {
                        NotifyPropertyChanged(memberExpression.Member.Name);
                    }
                }
            }
     
            public event PropertyChangedEventHandler PropertyChanged = delegate { };
            protected void NotifyPropertyChanged(string propertyName)
            {
                PropertyChangedEventHandler handler = this.PropertyChanged;
                if (handler != null)
                {
                    var e = new PropertyChangedEventArgs(propertyName);
                    handler(this, e);
                }
            }
     
            public void Dispose()
            {
                this.Clean();
            }
     
            public virtual void Clean()
            {
            }
     
    #if DEBUG
            ~ViewModel()
            {
                string msg = string.Format("{0} ({1}) Finalized", this.GetType().Name, this.GetHashCode());
                System.Diagnostics.Debug.WriteLine(msg);
            }
    #endif
     
     
        }
    }






  2. Clas Ericson
    Clas Ericson avatar
    31 posts
    Member since:
    Sep 2011

    Posted 26 Sep 2011 Link to this post

    Oops, I forgot to mention that it is the property curDate that I have problem with. I'm setting the property in SetAutomaticReadingDate in the ViewModel.

    Regards, Clas
  3. Dimitrina
    Admin
    Dimitrina avatar
    3769 posts

    Posted 28 Sep 2011 Link to this post

    Hi Clas,

    When you change a property value from code behind, then the item is not updated because a notification for a CollectionChanged is not received by the GridView. 

    You would better call the SetAutomaticReadingDate on a property level, i.e. in the setter of the property curValue, when its value is changed. 

    Would this approach be better for your scenario or do you want to do the editing in the CellEditEnded event handler? Please note that if you change any item value from code behind(instead from the UI), the CellEditEnded event handler will not be fired.

    Best wishes,
    Didie
    the Telerik team

    Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

  4. Clas Ericson
    Clas Ericson avatar
    31 posts
    Member since:
    Sep 2011

    Posted 28 Sep 2011 Link to this post

    Didie,
    thank you for your reply! It sounds like your suggestion of using the setter of curValue is a better idea. I give it a try and then post the result here!

    Regards, Clas
  5. Clas Ericson
    Clas Ericson avatar
    31 posts
    Member since:
    Sep 2011

    Posted 04 Oct 2011 Link to this post

    Hi Didie,
    I've tried your ideas and they would work if it wasn't for the fact that changes in the entity affects other parts of the system. I need to maintain the entity untouched.
    The basic idea is that when a user edits a lot of entities in a radgridview, he shouldn't need to edit both curDate and curValue for every entity, only curValue and curDate is set automatically to the date he entered in the first entity edited.

    One solution in this case would be to write a wrapper for the entity, adding a new setter that handles this.
    Is there another way to solve this without the need write a new wrapper? Any magical method in RadGridView?

    Regards, Clas
  6. Answer
    Nedyalko Nikolov
    Admin
    Nedyalko Nikolov avatar
    871 posts

    Posted 07 Oct 2011 Link to this post

    Hi Clas,

    Indeed a wrapper class will be the best approach. You could use RadGridView.CellEditEnded event also to populate all data and raise proper NotifyPropertyChanged events (for the UI).

    Best wishes,
    Nedyalko Nikolov
    the Telerik team

    Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

Back to Top