Y
Y
Yes2019-08-17 18:08:59
WPF
Yes, 2019-08-17 18:08:59

How to make the ProgressBar work while the application is under load?

Hello. I have a WPF application:

MainWindow.xaml

<Window.Resources>
        <Style x:Key="NormalStatusBar" TargetType="DockPanel">
            <Setter Property="Background" Value="#FF007ACC" />
        </Style>
        <Style x:Key="LoadDataStatusBar" TargetType="DockPanel">
            <Setter Property="Background" Value="#9333FF" />
        </Style>
        <Style x:Key="ErrorStatusBar" TargetType="DockPanel">
            <Setter Property="Background" Value="#eb4034" />
        </Style>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="20" />
        </Grid.RowDefinitions>
        <Menu VerticalAlignment="Top">
            <MenuItem Header="File">
                <MenuItem
                    x:Name="LoadDataToDB"
                    Click="LoadDataToDB_Click"
                    Header="LoadDataToDB" />
            </MenuItem>
        </Menu>
        <DockPanel x:Name="StatusBar" Grid.Row="1" Style="{StaticResource LoadDataStatusBar}"> <!--Style меняться во время выполнения LoadDataInDB()-->
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
                <TextBlock
                    x:Name="DataStatus"
                    Grid.Column="0"
                    Margin="10,0,0,0"
                    Foreground="White"
                    Text="Статус данных" />
                <DockPanel Grid.Column="1" HorizontalAlignment="Right">
                    <TextBlock
                        x:Name="CurrnetLine"
                        Margin="0,0,5,0"
                        Foreground="White"
                        Text="{Binding currentLine}" /> <!--должно меняться во время выполнения LoadDataInDB()-->
                    <TextBlock
                        Margin="0,0,5,0"
                        Foreground="White"
                        Text="/" />
                    <TextBlock
                        x:Name="MaxLines"
                        Margin="0,0,10,0"
                        Foreground="White"
                        Text="{Binding maxLines}" />
                </DockPanel>

            </Grid>
        </DockPanel>
    </Grid>


MainWindow.cs:

public string maxLines { get; set; }
        public string currentLine { get; set; }

        Style normalBarStyle;
        Style LoadBarStyle;
        Style errorBarStyle;
        public MainWindow()
        {
            InitializeComponent();
            normalBarStyle = this.FindResource("NormalStatusBar") as Style;
            LoadBarStyle = this.FindResource("LoadDataStatusBar") as Style;
            errorBarStyle = this.FindResource("ErrorStatusBar") as Style;
            StatusBar.Style = normalBarStyle;
            DataContext = this;
        }
        private void LoadDataToDB_Click(object sender, RoutedEventArgs e)
        {
            StatusBar.Style = LoadBarStyle;
            Mouse.OverrideCursor = Cursors.Wait;
            Analyzer analyzer = new Analyzer();
            analyzer.LoadDataInDB(this);//вызываю метод который делает приложение не активным
            Mouse.OverrideCursor = Cursors.Arrow;
            StatusBar.Style = normalBarStyle;
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public void ChangeLineData()
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(maxLines)));
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(currentLine)));
        }


Analyzer.LoadDataInDB(MainWindow):
if (запрос к бд == true) {
 DataBase.ClearAll();
 DirectoryInfo info = new DirectoryInfo(@"path\Data\");
   DirectoryInfo[] dirs = info.GetDirectories();
   for (int r = 0; r < dirs.Length; r++) {
    var additionalLines = File.ReadAllLines(@"path\Data\" + dirs[r].Name + @"\
      Additional.txt "); ~ 10000строк
      var mainLines = File.ReadAllLines(@"pathr\Data\" + dirs[r].Name + @"\
       Main.txt "); ~ 18000строк
       for (int i = 0; i < mainLines.Length; i++) {
        DataMain data = new DataMain(); //обычный класс с переменными
        int markCounter = 0; //слова отделяются символом '!'
        string word = string.Empty;
        for (int j = 0; j < mainLines[i].Length; j++) {
         char symbol = mainLines[i][j];
         mainWindow.maxLines = mainLines.Length.ToString(); //общие количество линий
         mainWindow.currentLine = i.ToString(); //текущая линя
         mainWindow.ChangeLineData(); //вызываю метод для смены данных bining полей
         if (symbol == '!') { //если символ равен разделителю слов
          switch (markCounter) {
           case 2: //если второе слово, то заполнятся это: 
            data.Alpha = Convert.ToInt32(word);
            break;
           case 1: //и дальше тем же методом
            data.Bravo = long.Parse(word);
            break;
           case 0:
            data.Charlie = ConvertToInt(word);
            break;
           case 5:
            data.Delta = ConvertToInt(word);
            break;
           case 6:
            data.Echo = ConvertToInt(word);
            break;
           case 7:
            data.Foxtrot = ConvertToInt(word);
            break;
           case 10:
            data.Golf = ConvertToInt(word);
            break;
           case 11:
            data.Hotel = ConvertToInt(word);
            break;
          }
          word = string.Empty;//очистка переменной
          markCounter++;// +1 к счетчику разделителей 
         } else { //иначе добавляется символ к слову
          word += symbol.ToString();
         }
        }
        DataBase.AddMainData(data); // вызов хранимой процедуры с параметрами
       }
       for (int i = 0; i < additionalLines.Length; i++) {
        //тут все тоже самое только в DataAdditional полей в раза 3-4 больше.
       }
      }
     }

The data parsing algorithm works well. But I can't do ProgresBar. When I call LoadDataInDB(), the application freezes for 20-25 seconds, you can't even move or close it. Only after the completion of the method, maxLine/currentLine are replaced, but with the maximum ones. I understand that this is due to processing and loading data into the DB. But is it possible to make it so that the user can see the progress?

Answer the question

In order to leave comments, you need to log in

2 answer(s)
B
Boris the Animal, 2019-08-18
@volkihar

Made based on your code. In general, you need to write it all a little differently. Passing a reference to classes with algorithms to controls is a very bad idea and immediately forget about this approach.
In this example, if you double-click on the menu item, then two loops will be launched into the thread(s) from the thread pool. You will process it yourself.
In this example, the Analyzer is a separate entity that can notify the outside world about some of its internal events through signals. The event is the same signal, and any number of subscribers can process it. The event can be perceived as pins on the Raspberry Pi board, if you connect something to it, you can somehow react to the signal from the pin, the same thing here.

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ProgressBarExample
{
    internal class Analyzer
    {
        private readonly SynchronizationContext _synchronizationContext;

        public Analyzer()
        {
            // Если экземпляр класса будет создан в UI потоке,
            // то здесь будет контекст синхронизации UI потока, иначе пула потоков
            _synchronizationContext = SynchronizationContext.Current ?? new SynchronizationContext();
        }

        public event EventHandler<AnalyzerEventArgs> ProgressChanged;

        public async Task<Data> LoadDataInDB()
        {
            var result = await Task.Run(async () =>
            {
                for (int i = 0; i < 100; i++)
                {
                    await Task.Delay(250);
                    OnProgressChanged(new AnalyzerEventArgs("line " + (i + 1), 100));
                }
                return new Data() { Text = "Данные " };
            });
            return result;
        }

        private void OnProgressChanged(AnalyzerEventArgs args)
        {
            // Перенаправляем выполнение в UI поток не ожидая пока отработает метод обработчик события.
            _synchronizationContext.Post(state =>
            {
                ProgressChanged?.Invoke(this, (AnalyzerEventArgs)state);
            }, args); // args передаётся в переменную state (грубо говоря)
        }
    }
}

namespace ProgressBarExample
{
    public class AnalyzerEventArgs
    {
        public int MaxLines { get; }

        public string CurrentLine { get; }

        public AnalyzerEventArgs(string currentLine, int maxLines)
        {
            CurrentLine = currentLine;
            MaxLines = maxLines;
        }
    }
}

namespace ProgressBarExample
{
    public class Data
    {
        public string Text { get; set; }
    }
}

using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Input;

namespace ProgressBarExample
{
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        private readonly Analyzer _analyzer;
        private readonly Style _normalBarStyle;
        private readonly Style _loadBarStyle;
        private readonly Style _errorBarStyle;
        private string _maxLines;
        private string _currentLine;

        public string MaxLines
        {
            get { return _maxLines; }
            set
            {
                _maxLines = value;
                OnPropertyChanged();
            }
        }

        public string CurrentLine
        {
            get { return _currentLine; }
            set
            {
                _currentLine = value;
                OnPropertyChanged();
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public MainWindow()
        {
            InitializeComponent();

            _normalBarStyle = FindResource("NormalStatusBar") as Style;
            _loadBarStyle = FindResource("LoadDataStatusBar") as Style;
            _errorBarStyle = FindResource("ErrorStatusBar") as Style;

            _statusBar.Style = _normalBarStyle;
            
            _analyzer = new Analyzer();
            _analyzer.ProgressChanged += OnAnalyzerProgressChanged;
        }

        private void OnAnalyzerProgressChanged(object sender, AnalyzerEventArgs args)
        {
            // Передавать каждый раз одно и тоже бессмысленно, сделаете сами как нужно
            MaxLines = args.MaxLines.ToString();
            CurrentLine = args.CurrentLine;
        }

        private async void LoadDataToDB_Click(object sender, RoutedEventArgs e)
        {
            _statusBar.Style = _loadBarStyle;
            Mouse.OverrideCursor = Cursors.Wait;

            try
            {
                var data = await _analyzer.LoadDataInDB(); 
                MessageBox.Show(data.Text);
            }
            finally
            {
                Mouse.OverrideCursor = Cursors.Arrow;
                _statusBar.Style = _normalBarStyle;
            }
        }

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

<Window
    x:Class="ProgressBarExample.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:local="clr-namespace:ProgressBarExample"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="MainWindow"
    Width="800"
    Height="450"
    DataContext="{Binding RelativeSource={RelativeSource Self}}"
    mc:Ignorable="d">
    <Window.Resources>
        <Style x:Key="NormalStatusBar" TargetType="DockPanel">
            <Setter Property="Background" Value="#FF007ACC" />
        </Style>
        <Style x:Key="LoadDataStatusBar" TargetType="DockPanel">
            <Setter Property="Background" Value="#9333FF" />
        </Style>
        <Style x:Key="ErrorStatusBar" TargetType="DockPanel">
            <Setter Property="Background" Value="#eb4034" />
        </Style>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="20" />
        </Grid.RowDefinitions>
        <Menu VerticalAlignment="Top">
            <MenuItem Header="File">
                <MenuItem
                    x:Name="LoadDataToDB"
                    Click="LoadDataToDB_Click"
                    Header="LoadDataToDB" />
            </MenuItem>
        </Menu>
        <DockPanel
            x:Name="_statusBar"
            Grid.Row="1"
            Style="{StaticResource LoadDataStatusBar}">
            <!--  Style меняться во время выполнения LoadDataInDB()  -->
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
                <TextBlock
                    Grid.Column="0"
                    Margin="10,0,0,0"
                    Foreground="White"
                    Text="Статус данных" />
                <DockPanel Grid.Column="1" HorizontalAlignment="Right">
                    <TextBlock
                        Margin="0,0,5,0"
                        Foreground="White"
                        Text="{Binding CurrentLine}" />
                    <!--  должно меняться во время выполнения LoadDataInDB()  -->
                    <TextBlock
                        Margin="0,0,5,0"
                        Foreground="White"
                        Text="/" />
                    <TextBlock
                        Margin="0,0,10,0"
                        Foreground="White"
                        Text="{Binding MaxLines}" />
                </DockPanel>

            </Grid>
        </DockPanel>
    </Grid>
</Window>

#
#, 2019-08-17
@mindtester

can. you just need to move the processing to a thread separate from the UI, otherwise the blocking turns out to be
google and you will find the wpf progressbar

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question