This is a migrated thread and some comments may be shown as answers.

GUI doesn't reflect changes

5 Answers 109 Views
GridView
This is a migrated thread and some comments may be shown as answers.
Clas Ericson
Top achievements
Rank 2
Clas Ericson asked on 26 Sep 2011, 09:43 AM
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
 
 
    }
}






5 Answers, 1 is accepted

Sort by
0
Clas Ericson
Top achievements
Rank 2
answered on 26 Sep 2011, 09:45 AM
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
0
Dimitrina
Telerik team
answered on 28 Sep 2011, 01:37 PM
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 >>

0
Clas Ericson
Top achievements
Rank 2
answered on 28 Sep 2011, 01:52 PM
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
0
Clas Ericson
Top achievements
Rank 2
answered on 04 Oct 2011, 01:46 PM
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
0
Accepted
Nedyalko Nikolov
Telerik team
answered on 07 Oct 2011, 01:10 PM
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 >>

Tags
GridView
Asked by
Clas Ericson
Top achievements
Rank 2
Answers by
Clas Ericson
Top achievements
Rank 2
Dimitrina
Telerik team
Nedyalko Nikolov
Telerik team
Share this question
or