1

OK, so I'm attempting to teach myself the MVVM pattern and WPF, and I'm running into a block.

I have a ViewModel that has a "SelectedProduct" field. When that SelectedProduct field is set, I want to populate the contents of another property, "BindedLimits", by calling a long running function (It takes about 2-10 seconds depending on what product is selected). Ideally, I would like to kickoff the update in the background, and somehow display a "progress" window while this is occuring, but I can't seem to find any solid resources on how I would accomplish this, or if this is even the "right" way to be doing something like this.

Here's my ViewModel so far...

public class LimitsViewModel : PropertyChangedBase
{

    private ProductFamily selectedProduct;

    public ProductFamily SelectedProduct
    {
        get { return this.selectedProduct; }
        set
        {
            bool runLongOperation = true;

            if (value == this.selectedProduct)
            {
                runLongOperation = false;
            }

            this.SetPropertyChanged(ref this.selectedProduct, value);

            if (runLongOperation)
            {
                this.Limits = LoadLimits();
            }
        }
    }

    private ObservableCollection<BindedLimit> limits;

    public ObservableCollection<BindedLimit> Limits
    {
        get { return this.limits; }
        set
        {
            this.SetPropertyChanged(ref this.limits, value);
        }
    }

    private BindedLimit selectedLimit;

    public BindedLimit SelectedLimit
    {
        get { return this.selectedLimit; }
        set
        {
            this.SetPropertyChanged(ref this.selectedLimit, value);
        }
    }

    private ObservableCollection<BindedLimit> LoadLimits()
    {
        // Long running stuff here
    }
}
Calvin
  • 710
  • 1
  • 10
  • 28
  • 1
    `ObservableCollection` require UI thread. You can prepare `List` in background (using `Thread`, `Task`, etc) and then invoke update of `ObservableCollection`. See [here](http://stackoverflow.com/q/5480372/1997232). – Sinatr Dec 12 '14 at 15:02
  • OK, I didn't know that about ObservableCollection. So if I implement the command in that link you sent me, how do I invoke it on the selection change? Do I just call Command.Execute in the setter? – Calvin Dec 12 '14 at 15:19

2 Answers2

1

Something like

private ProductFamily _selectedProduct;
public ProductFamily SelectedProduct
{
    get { return _selectedProduct; }
    set
    {
        this.SetPropertyChanged(ref _selectedProduct, value)
        Limits.Clear(); // or Limits = new ...
        Task.Run(() => LongTask());
    }
}

private void LongTask()
{
    var list = new List<BindedLimit>();
    ...
    App.Current.Dispatcher.Invoke(() => Limits = new ObservableCollection<BindedItems>(list));
}
Sinatr
  • 20,892
  • 15
  • 90
  • 319
0

Sinatr's link to What's the best way to update an ObservableCollection from another thread? helped me figure the solution. Here is what I devised. Thanks!

public class LimitsViewModel : PropertyChangedBase
{
    private CancellationTokenSource tokenSource;

    public LimitsViewModel()
    {
        this.tokenSource = new CancellationTokenSource();
    }

    public ICommand LoadCommand
    {
        get { return new RelayCommand(async x => await this.LoadLimits(this.tokenSource.Token)); }
    }

    private ProductFamily selectedProduct;
    public ProductFamily SelectedProduct
    {
        get { return this.selectedProduct; }
        set
        {
            this.SetPropertyChanged(ref this.selectedProduct, value);
            this.LoadCommand.Execute(null);
        }
    }

    private ObservableCollection<BindedLimit> limits;
    public ObservableCollection<BindedLimit> Limits
    {
        get { return this.limits; }
        set { this.SetPropertyChanged(ref this.limits, value); }
    }

    private bool limitsLoading;
    public bool LimitsLoading
    {
        get { return this.limitsLoading; }
        set { this.SetPropertyChanged(ref this.limitsLoading, value); }
    }

    private BindedLimit selectedLimit;
    public BindedLimit SelectedLimit
    {
        get { return this.selectedLimit; }
        set { this.SetPropertyChanged(ref this.selectedLimit, value); }
    }

    private async Task LoadLimits(CancellationToken ct)
    {
    }
}
Community
  • 1
  • 1
Calvin
  • 710
  • 1
  • 10
  • 28