Почему не работает Progressbar в Wpf

150
02 февраля 2019, 13:20

Все таки не могу понять, почему не работает прогрессбар. Использую MVVM WPF. Есть модель,это класс с одним методом.

 namespace progresbar.Model
{
    public delegate void StatusChangedHandler(int status);
    public class Model
    {
        private int start;
        private int end=100;
        public event StatusChangedHandler StatusChange;
        public int Startbar
        {
            get { return start; }
            set
            {
                start = value;
                StatusChange?.Invoke(value);
            }
        }
        public int End
        {
            get { return end; }
            set
            {
                end = value;
            }
        }
        public  void Go()
        {
            Task.Factory.StartNew(() =>
            {
               // просто метод
            Startbar = 0;
                while (Startbar != End)
                {
                    Startbar++;
                }
            });
         }
    }

Есть ViewModel

 class MainViewModel : INotifyPropertyChanged
{
  Model.Model _model=new Model.Model();
    private int _startbar;
    private int _endbar;
    // реализую интерфейс
    public event PropertyChangedEventHandler PropertyChanged;
    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    // команда запуска
    public Comand CommandToPars
    {
        get
        {
            return new Comand(  () =>
            {
             // выполняю метод 
                _model.Go();
                // забираю свойства
                // праивльно ли так делать?
                _model.StatusChange += _model_StatusChange;
                _endbar = _model.End;
            });
        }
    }
    private void _model_StatusChange(int status)
    {
        Startbar = status;
    }

    // свойства для прогресбара
    public int Startbar
    {
        get { return _startbar; }
        set
        {
            _startbar = value;
            OnPropertyChanged();
        }
    }
    public int Endtbar
    {
        get { return _endbar; }
        set
        {
            _endbar = value;
            OnPropertyChanged();
        }
    }
    public MainViewModel()
    {
    }
}

Есть View

<Window x:Class="progresbar.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:progresbar"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid HorizontalAlignment="Left" Height="345" Margin="10,25,0,0" VerticalAlignment="Top" Width="747">
            <Button Command="{ Binding CommandToPars}" Content="Button" HorizontalAlignment="Left" Height="42" Margin="173,279,0,0" VerticalAlignment="Top" Width="389"/>
            <ProgressBar Maximum="{Binding Endtbar}" 
                         Value="{Binding StartBar}" HorizontalAlignment="Left" Height="46" Margin="94,85,0,0" VerticalAlignment="Top" Width="512"/>
        </Grid>
    </Grid>
</Window>

Подскажите что не так делаю, почему не работает. Прогресс бар всегда зеленый, даже когда кнопка не нажата, а при отладке все переменные вроде нормально работают.

Answer 1

Возьмем вашу разметку (не забудем указать минимальное значение)

    <Grid HorizontalAlignment="Left" Height="345" Margin="10,25,0,0" VerticalAlignment="Top" Width="747">
        <Button Command="{ Binding CommandToPars}" Content="Button" HorizontalAlignment="Left" Height="42" Margin="173,279,0,0" VerticalAlignment="Top" Width="389"/>
        <ProgressBar  Maximum="{Binding Endtbar}"   Minimum="0" 
                     Value="{Binding Startbar}" HorizontalAlignment="Left" Height="46" Margin="94,85,0,0" VerticalAlignment="Top" Width="512"/>
    </Grid>

Модель

public delegate void StatusChangedHandler(int status);
public class Model
{
    private int start;
    private int end = 100;
    public event StatusChangedHandler StatusChange;
    public int Startbar
    {
        get { return start; }
        set
        {
            start = value;
            StatusChange?.Invoke(value);
        }
    }
    public int End
    {
        get { return end; }
        set
        {
            end = value;
        }
    }
    public void Go()
    {
        Task.Factory.StartNew(() =>
        {
            // просто метод
            Startbar = 0;
            while (Startbar < End)
            {
                Thread.Sleep(100);
                Startbar++;
            }
        });
    }
}

Вьюмодель (у меня нет вашей команды, потому я свою накатал)

public class DelegateCommand : ICommand
{
    private readonly Action _action;
    public DelegateCommand(Action action)
    {
        _action = action;
    }
    public bool CanExecute(object parameter)
    {
        return true;
    }
    public void Execute(object parameter)
    {
        _action();
    }
    public event EventHandler CanExecuteChanged;
}
class MainViewModel : INotifyPropertyChanged
{
    Model _model = new Model();
    private int _startbar;
    private int _endbar = 100;
    // реализую интерфейс
    public event PropertyChangedEventHandler PropertyChanged;
    //[NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    // команда запуска
    public ICommand CommandToPars
    {
        get
        {
            return new DelegateCommand(() =>
            {
                // выполняю метод 
                _model.Go();
                // забираю свойства
                // праивльно ли так делать?
                _model.StatusChange += _model_StatusChange;
                _endbar = _model.End;
            });
        }
    }
    private void _model_StatusChange(int status)
    {
        Startbar = status;
    }

    // свойства для прогресбара
    public int Startbar
    {
        get { return _startbar; }
        set
        {
            _startbar = value;
            OnPropertyChanged();
        }
    }
    public int Endtbar
    {
        get { return _endbar; }
        set
        {
            _endbar = value;
            OnPropertyChanged();
        }
    }
    public MainViewModel()
    {
    }
}

Запускаем

Answer 2

Ну тут много чего править нужно.

Во-первых, в теле сеттера свойства должны вызывать OnPropertyChanged с именем свойства и вообще в любом месте кода вы должны вызывать этот метод с именем. Тогда VM через событие пинает View на обновление значения. Например:

public int StartBar
{
    get { return _startbar; }
    set
    {
        _startbar = value;
        OnPropertyChanged("StartBar");
    }
}

Во-вторых, как вы запускаете? У вас при нажатии кнопки последовательно:

  1. Запускается новый поток, котором без каких либо ожиданий в цикле изменяется значение StartBar.
  2. Привязываетесь к событию изменения значения.
  3. И только в последнюю очередь у endbar выставляете значение.

В результате: с очень большой вероятностью, за то время, пока основной поток, после запуска нового потока дойдет до подписки на событие, созданный поток успеет пробежаться по всем значениям и в результате ты даже не словишь ни одного срабатывания.

В принципе, запомни, что любая инициализация должна происходить ДО начала работы - это в общем-то логично. То есть подписки на события, установки значения - до запуска счётчика.

Ну и в-третьих, зачем отдельно имеет поля для свойств в VM? У вас есть модель с этими значениями, поэтому берите значения оттуда. Например:

public int EndBar
{
    get { return _model.End; }
    set
    {
        _model.End = value;
        OnPropertyChanged("EndBar");
    }
}

Только для данного примера нужно будет менять кардинально модель и логику взаимодействия с VM, поэтому это лишь рекомендация.

READ ALSO
ListView извлечение данных

ListView извлечение данных

Застопорился на элементе ListViewИмеется код:

214
Почему свойство = null?

Почему свойство = null?

Я сделал свой UserConrol типа Button ModernBtnxaml

204
Как сделать переменную членом класса?

Как сделать переменную членом класса?

Как сделать переменную cheked членом класса timer ?

177