Problem using QueryableDataServiceCollectionView with property HasChanges

2 posts, 0 answers
  1. Carlos
    Carlos avatar
    6 posts
    Member since:
    Jan 2011

    Posted 28 Feb 2012 Link to this post

    I've been implementing sucessfully the QueryableDataServiceCollectionView using a fully functional MVVM pattern.

    I have a gridview, pager and a set of buttons (Save, Reject, Refresh) implementing the ICommand (with the CanExecute) so that they can automatically deactivate/active based on the CollectionView status property HasChanges.

    Somehow, this works perfectly until i try to insert a new row using the RadGridView's builtin insert pivot row.

    After i insert a record using the above method, the HasChanges property on the QueryableDataServiceCollectionView never gets updated again, even if I update some records.

    My goal is to allow to user to produce a set of changes and then use the Submit or Reject changes in the CollectionView.

    Facts:
    All the CRUD operations are done using the RadGridView's builtin UI operations, there is no code involved for CRUD operations.
    This behavior is happening without ever calling SubmitChanges, so there is no server interaction involved in this process.
    I can successfully reproduce this behavior by executing the following steps:
    1. Edit a row using the RadGridView's builtin edit feature (double click on the cell) and commit the changes to the CollectionView by pressing Enter. Notice that the HasChanges property has changed to True.
    2. Undo the operation by calling RejectChanges on the CollectionView. Notice that the HasChanges property has changed to False.
    3. Insert a new row using the RadGridView's builtin insert feature (ShowInsertRow="True") and commit the changes to the CollectionView by pressing Enter. Notice that the HasChanges property has changed to True.
    4. Undo the operation by calling RejectChanges on the CollectionView. Notice that the HasChanges property has changed to False.
    5. After this step, the CollectionView is somehow broken because the HasChanges will never get changed again and will be allways  defaulted to False. You can confirm this by making any change on the RadGridView (updating, inserting or deleting).

    This is a issue in my case because it breaks the chain of my Save & Undo buttons making them to never get enabled again because the CanExecute event (ICommand) will allways return false, because HasChanges is allways false.

    Here is my ViewModel:

    public class ImdbTitlesViewModel : ViewModelBase
        {
            private ImdbDataContext _imdbContext;
     
            private QueryableDataServiceCollectionView<ImdbTitle> _imdbTitlesView;
            public QueryableDataServiceCollectionView<ImdbTitle> ImdbTitlesView
            {
                get { return _imdbTitlesView; }
            }
     
            public IEnumerable<ImdbTitle> ImdbTitles
            {
                get
                {
                    return this._imdbTitlesView;
                }
            }
     
            private ImdbTitle _selectedTitle;
            public ImdbTitle SelectedTitle
            {
                get { return this._selectedTitle; }
                set {
                    if (this._selectedTitle != value)
                    {
                        this._selectedTitle = value;
                        RaisePropertyChanged("SelectedTitle");
                    }
                }
            }
             
     
            private bool _isBusy;
            public bool IsBusy
            {
                get { return this._isBusy; }
                set
                {
                    if (this._isBusy != value)
                    {
                        this._isBusy = value;
                        RaisePropertyChanged("IsBusy");
                    }
                }
            }
     
            public RelayCommand Startup { get; set; }
            public RelayCommand Refresh { get { return LoadImdbTitles; } set { LoadImdbTitles = value; } }
            public RelayCommand LoadImdbTitles { get; set; }
            public RelayCommand SaveChanges { get; set; }
            public RelayCommand RejectChanges { get; set; }
     
            /// <summary>
            /// Initializes a new instance of the ImdbTitlesViewModel class.
            /// </summary>
            public ImdbTitlesViewModel()
            {
                Startup = new RelayCommand(OnStartup);
                LoadImdbTitles = new RelayCommand(OnLoadImdbTitles, CanLoadImdbTitles);
                SaveChanges = new RelayCommand(OnSaveChanges, CanSaveChanges);
                RejectChanges = new RelayCommand(OnRejectChanges, CanRejectChanges);
     
                if (IsInDesignMode)
                {
                    // Code runs in Blend --> create design time data.
                }
                else
                {
                    // Code runs "for real": Connect to service, etc...
                    this._imdbContext = new ImdbDataContext();
                    this._imdbTitlesView = new QueryableDataServiceCollectionView<ImdbTitle>(this._imdbContext, this._imdbContext.ImdbTitles);
                    this._imdbTitlesView.LoadedData += new EventHandler<Telerik.Windows.Controls.DataServices.LoadedDataEventArgs>(OnImdbTitlesViewLoadedData);
                    this._imdbTitlesView.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(OnImdbTitlesViewPropertyChanged);
                    this._imdbTitlesView.PageSize = 25;
                    this._imdbTitlesView.AutoLoad = true;
                    this._imdbTitlesView.SubmittedChanges += new EventHandler<Telerik.Windows.Controls.DataServices.DataServiceSubmittedChangesEventArgs>(OnImdbTitlesViewSubmittedChanges);
                }
            }
     
            void OnImdbTitlesViewSubmittedChanges(object sender, Telerik.Windows.Controls.DataServices.DataServiceSubmittedChangesEventArgs e)
            {
                if (e.HasError)
                {
                    e.MarkErrorAsHandled();
                    MessageBox.Show(e.Error.Message, "Error occurred", MessageBoxButton.OK);
                }
                else
                {
                    MessageBox.Show("Changes saved successfully!", "Notification", MessageBoxButton.OK);
                }
            }
     
            void OnImdbTitlesViewPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
            {
                if (e.PropertyName.Equals("IsBusy"))
                {
                    this.IsBusy = this._imdbTitlesView.IsBusy;
                }
     
                if(e.PropertyName.Equals("HasChanges"))
                {
                    SaveChanges.RaiseCanExecuteChanged();
                    RejectChanges.RaiseCanExecuteChanged();
                }
                 
            }
     
            void OnImdbTitlesViewLoadedData(object sender, Telerik.Windows.Controls.DataServices.LoadedDataEventArgs e)
            {
                if (e.HasError)
                {
                    e.MarkErrorAsHandled();
                    MessageBox.Show(e.Error.Message, "Error occurred", MessageBoxButton.OK);
                }
            }
     
            private void OnStartup()
            {
                // We dont need to startup because the DataView has the property AutoLoad = true
                //GetImdbTitles();
            }
     
            private bool CanLoadImdbTitles()
            {
                return !IsBusy;
            }
     
            private void OnLoadImdbTitles()
            {
                // Execute the query.
                //this.RejectChanges.Execute(null);
                this._imdbTitlesView.Load(true);
                //this._imdbTitlesView.Load(true);
            }
     
            private bool CanSaveChanges()
            {
                if (this.IsBusy)
                    return false;
     
                if (this._imdbTitlesView.HasChanges)
                    return true;
     
                return false;
            }
     
            private void OnSaveChanges()
            {
                this._imdbTitlesView.SubmitChanges();
            }
     
            private void OnRejectChanges()
            {
                this._imdbTitlesView.RejectChanges();
            }
     
            private bool CanRejectChanges()
            {
                if (this.IsBusy)
                    return false;
     
                if (this._imdbTitlesView.HasChanges)
                    return true;
                else
                    return false;
            }
     
     
            public override void Cleanup()
            {
                // Clean own resources if needed
                base.Cleanup();
            }
        }
  2. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2478 posts

    Posted 29 Feb 2012 Link to this post

    Hello,

    Unfortunately, we cannot tell what the problem is by looking that the provided source code.

    Can you send us a small runnable dummy project with Northwind or AdventureWorks DB that mimics your real-life project architecture? Once we have a runnable sample, we will attach it to our source code and debug it to see what exactly is going on.

    Alternatively, if that would be easier for you, you can use the dummy project that I have attached as a base to build upon and modify it so it does reproduce the behavior that you are faced with. It has a database and everything -- you only need to modify it to be like your real-life project.

    You will need to open a support ticket in order to be able to attach the file.

    We are looking forward to hearing from you.

    Greetings,
    Ross
    the Telerik team
    Sharpen your .NET Ninja skills! Attend Q1 webinar week and get a chance to win a license! Book your seat now >>
  3. DevCraft banner
Back to Top