Click here to Skip to main content
15,868,440 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I am trying to get my head around MVVM, I created little project to test some things that I will need in the real one.
I have MainView, MainViewWindow, ProgressView which contains ProgressBar and ProgressViewModel that uses BackgroundWorker to update ProgressBar.
In my MainView I have UserControl that has it's content bound to ProgressViewModel.
Whatever starting value for Progress I set in ProgressViewModel() is reflected on ProgressBar when I start my App. It does not update however. From what I understand BackgroundWorker reports progress back to UI thread, but I tried Invoking anyway with to luck... I guess the problem is in MainViewModel or MainView but I can't get to it. Does MainViewModel need to implement INotifyPropertyChanged in case of UserControl? What am I missing here?

Here's code:

MainView.xaml
XML
<Window x:Class="Testing.MainView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:dsvm="clr-namespace:Testing.ViewModels"
        xmlns:dsv="clr-namespace:Testing.Views"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <dsvm:MainViewModel/>
    </Window.DataContext>
    <Window.Resources>
        <DataTemplate DataType="{x:Type dsvm:ProgressViewModel}">
            <dsv:ProgressView/>
        </DataTemplate>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Button Grid.Row="0" Content="Click to Test Binding" Command="{Binding ButtonCommand}"/>
        <UserControl Grid.Row="1" Content="{Binding viewModel}"/>
    </Grid>
</Window>


MainViewModel.cs
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using Testing.Models;

namespace Testing.ViewModels
{
    public class MainViewModel 
    {
        private ICommand _buttonCommand;

        public ICommand ButtonCommand
        {
            get
            {
                return _buttonCommand;
            }
            set
            {
                _buttonCommand = value;
            }
        }

        public ProgressViewModel viewModel {get;set;}

        public MainViewModel()
        {
            ButtonCommand = new RelayCommand(RunCommand);
            viewModel = new ProgressViewModel();
        }

        public void RunCommand(object obj)
        {
            viewModel.Run();
        }
    }
}


ProgressView.xaml
XML
<UserControl x:Class="Testing.Views.ProgressView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:dsvm="clr-namespace:Testing.ViewModels"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.DataContext>
        <dsvm:ProgressViewModel/>
    </UserControl.DataContext>
    <DockPanel>
        <ProgressBar IsIndeterminate="False" Minimum="0" Maximum="100" Value="{Binding Progress, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}" />
    </DockPanel>
</UserControl>


ProgressViewModel.cs
C#
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Testing.ViewModels
{
    public class ProgressViewModel : INotifyPropertyChanged
    {
        public BackgroundWorker bgWorker { get; set; }

        private double _progress;
        public double Progress
        {
            get { return _progress; }
            set
            {
                if (_progress != value)
                {
                    _progress = value;
                    RaisePropertyChanged("Progress");
                }   
            }
        }

        public ProgressViewModel()
        {
            _progress = 0.0;
            bgWorker = new BackgroundWorker();
            bgWorker.DoWork += bgWorker_DoWork;
            bgWorker.ProgressChanged += bgWorker_ProgressChanged;
            bgWorker.WorkerReportsProgress = true;
        }

        public void Run()
        {
            bgWorker.RunWorkerAsync();
        }

        private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            for (int i = 0; i < 100; ++i)
                bgWorker.ReportProgress(i);
        }

        private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            Progress = e.ProgressPercentage;
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}


and RelayCommand.cs
from http://www.codeproject.com/Tips/813345/Basic-MVVM-and-ICommand-Usage-Example
C#
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace Testing.Models
{
    public class RelayCommand : ICommand
    {
        private Action<object> execute;

        private Predicate<object> canExecute;

        private event EventHandler CanExecuteChangedInternal;

        public RelayCommand(Action<object> execute)
            : this(execute, DefaultCanExecute)
        {
        }

        public RelayCommand(Action<object> execute, Predicate<object> canExecute)
        {
            if (execute == null)
                throw new ArgumentNullException("execute");
            if (canExecute == null)
                throw new ArgumentNullException("canExecute");

            this.execute = execute;
            this.canExecute = canExecute;
        }

        public event EventHandler CanExecuteChanged
        {
            add
            {
                CommandManager.RequerySuggested += value;
                this.CanExecuteChangedInternal += value;
            }

            remove
            {
                CommandManager.RequerySuggested -= value;
                this.CanExecuteChangedInternal -= value;
            }
        }

        public bool CanExecute(object parameter)
        {
            return this.canExecute != null && this.canExecute(parameter);
        }

        public void Execute(object parameter)
        {
            this.execute(parameter);
        }

        public void OnCanExecuteChanged()
        {
            EventHandler handler = this.CanExecuteChangedInternal;
            if (handler != null)
                handler.Invoke(this, EventArgs.Empty);
        }

        public void Destroy()
        {
            this.canExecute = _ => false;
            this.execute = _ => { return; };
        }

        private static bool DefaultCanExecute(object parameter)
        {
            return true;
        }
    }
}


Thank you in advance, I am gratefull for all tips I can get :)
Posted

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900