Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to defer data querying in Silverlight ViewModel class?

I have a Silverlight app with several graphs and a date control on the top which allows the user to set the date range (e.g. July 1 - Sep 30).

Basically, when a user modifies the date range, a command is executed which sets the ViewModel's DateRange property to the new value. The DateRange's setter calls a RunQueries method which queries for new data and sets the Data1 and Data2 properties with the results of those queries. The graphs that are bound to Data1 or Data2 get updated accordingly. See below for the basic ViewModel code; take note of the RunQueries method call in the DateRange setter.

Now in reality, there are already more than two data collections and more keep on being added as the app is expanded. In addition, not all graphs are visible at once; at times only one graph is visible. But when the user changes the date range, all the queries to obtain all the data that is needed for any of the graphs are rerun with the new start and end dates. This seems very inefficient to me - perhaps only one query needs to be run!

So my question is - how do I implement delayed data querying in my ViewModel class?

Here are two ideas I've been considering:

  • Keep track of which data collections are up-to-date and then check in the data getter method if a query needs to be run.
  • Break out the ViewModel into several subclasses, one for each graph, and have each ViewModel manage its own data with the base class keeping track of the DateRange.

Both ideas seem complex to implement and I've been wondering - is there a standard approach to this? Am I missing something in the MVVM design pattern that addresses this issue?


Here's the very simplified version of my ViewModel class:

public class MyViewModel: INotifyPropertyChanged
{
    private ObservableCollection<MyData> _Data1;
    private ObservableCollection<MyData> _Data2;
    private MyDateRange _DateRange;

    public ObservableCollection<MyData> Data1
    {
        get 
        { 
            return _Data1; 
        }
        set
        { 
            if (_Data1 != value) 
            {
                _Data1 = value;
                NotifyPropertyChanged("Data1");
            }
        }
    }

    // getter and setter for Data2 go here

    public MyDateRange DateRange
    {
        get
        {
             return _DateRange;
        }
        set
        {
             if (_DateRange != value)
             {
                 _DateRange = value;
                 NotifyPropertyChanged("DateRange");

                 // if date range changed, need to query for stats again
                 RunQueries();
             }
         }
     }

     private void RunQueries()
     {
          GetData1();
          GetData2();
     }

     private void GetData1()
     {
          // call wcf service to get the data
     }

     private void GetData1Completed(object s, EventArgs e)
     {
          // update Data1 property with results of service call
     }

     // etc
}
like image 559
MCS Avatar asked Nov 15 '22 05:11

MCS


1 Answers

I'll try to summarize your situation:

  • If data range is changed - only visible graphs should be updated.
  • If any graph becomes visible - it should update itself immediately.

So there is obvious need for a visibility property.

Solution for the 1st point: check possibility of command's execution in the RunQueries method.

Solution for the 2nd point: execute command in a setter of Visibility property.

Here is example:

private DelegateCommand UpdateData1Command { get; set; }

    public MyViewModel()
    {
        this.UpdateData1Command = new DelegateCommand(_ => this.GetData1(), _ => this.IsGraph1Visible);
    }

    private bool isGraph1Visible;

    public bool IsGraph1Visible
    {
        get { return isGraph1Visible; }
        set
        {
            isGraph1Visible = value;
            OnPropertyChanged("IsGraph1Visible");

            if(UpdateData1Command.CanExecute(null))
                UpdateData1Command.Execute(null);
        }
    }

    private void RunQueries()
    {
        if (UpdateData1Command.CanExecute(null))
            UpdateData1Command.Execute(null);
    }

    private void GetData1()
    {
        // call wcf service to get the data
    }

Each graph now has properties Data, IsVisible, UpdateDataCommand and method GetData. I recommend to move them into a separate viewmodel.

like image 128
vortexwolf Avatar answered Dec 16 '22 06:12

vortexwolf