ListBox с ProgressBar [WPF, C#]

0

В этой статье опишу как сделать так, чтобы каждый элемент ListBox‘а имел свой ProgressBar.

Создайте новой WPF-приложение (с названием ListBoxWithProgressBar), переименуйте главное окно в WndMain, добавьте на форму ListBox (измените свойство HorizontalContentAlignment на Stretch) и добавьте следующий шаблон элемента:

<ListBox.ItemTemplate>
<DataTemplate>
	<!-- Создаем грид с двемя колонками -->
	<Grid Margin="0,2" Background="{Binding BackColor}">
		<!-- Длина второй колонки равна 100 пикселям, а первой - оставшаяся длина -->
		<Grid.ColumnDefinitions>
			<ColumnDefinition Width="*" />
			<ColumnDefinition Width="100" />
		</Grid.ColumnDefinitions>
		<TextBlock Text="{Binding Title}" />
		<ProgressBar Name="PbStatus"  Grid.Column="1" Minimum="0" Maximum="100" Value="{Binding Progress}"/>
		<!-- Добавляем блок с текстом на ProgressBar для отображения процентов -->
		<TextBlock Grid.Column="1" Text="{Binding ElementName=PbStatus, Path=Value, StringFormat={}{0:0}%}" HorizontalAlignment="Center" VerticalAlignment="Center" />
	</Grid>
</DataTemplate>
</ListBox.ItemTemplate>

Создаем грид с двумя колонками, в первую добавляем блок с текстом, во вторую прогресс бар с текстовым блоком (который будет отображать проценты). С помощью Binding мы осуществляем привязку к полям класса ListBoxDataItem (который будет описан ниже).

Вот весь код главного окна (также была добавлена кнопка старта):

<Window x:Class="ListBoxWithProgressBar.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ListBoxWithProgressBar" Height="203" Width="297" WindowStartupLocation="CenterScreen" ResizeMode="NoResize" Closing="Window_Closing">
    <Grid>
        <ListBox x:Name="LbActions" HorizontalAlignment="Left" Height="125" Margin="10,10,0,0" VerticalAlignment="Top" Width="265" HorizontalContentAlignment="Stretch">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <!-- Создаем грид с двемя колонками -->
                    <Grid Margin="0,2" Background="{Binding BackColor}">
                        <!-- Длина второй колонки равна 100 пикселям, а первой - оставшаяся длина -->
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="100" />
                        </Grid.ColumnDefinitions>
                        <TextBlock Text="{Binding Title}" />
                        <ProgressBar Name="PbStatus"  Grid.Column="1" Minimum="0" Maximum="100" Value="{Binding Progress}"/>
                        <!-- Добавляем блок с текстом на ProgressBar для отображения процентов -->
                        <TextBlock Grid.Column="1" Text="{Binding ElementName=PbStatus, Path=Value, StringFormat={}{0:0}%}" HorizontalAlignment="Center" VerticalAlignment="Center" />
                    </Grid>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <Button x:Name="BtnStart" Content="Start" HorizontalAlignment="Left" Margin="10,140,0,0" VerticalAlignment="Top" Width="265" Cursor="Hand" Click="BtnStart_Click"/>
    </Grid>
</Window>

Теперь перейдем к написанию логики.

Добавим классу формы 2 переменные:

private string[] _actions;
private Thread _thread;

Первая — массив элементов ListBox, вторая — поток, в котором будем запускать заполнение прогресс баров.

Далее добавьте класс ListBoxDataItem:

private class ListBoxDataItem
{
	public SolidColorBrush BackColor { get; set; } // фон элемента
	public string Title { get; set; } // текст элемента
	public int Progress { get; set; } // прогресс
 
	public ListBoxDataItem( string title, Color backColor, int progress = 0 )
	{
		this.Title = title;
		this.BackColor = new SolidColorBrush( backColor );
		this.Progress = 0;
	}
 
	public ListBoxDataItem( ListBoxDataItem item, int progress, Color backColor )
		: this( item.Title, backColor, progress )
	{
	}
 
	public ListBoxDataItem( ListBoxDataItem item, Color backColor )
		: this( item.Title, backColor, item.Progress )
	{
	}
}

Добавим метод для заполнения ListBox‘а элементами:

private void FillListBox()
{
	this.LbActions.Items.Clear();
	this._actions = new[] { "Action 1", "Action 2", "Action 3", "Action 4", "Action 5" };
	foreach ( string action in this._actions )
	{
		this.LbActions.Items.Add( new ListBoxDataItem( action, Colors.Transparent ) );
	}
}

Создадим обработчик события Window_Closing, который срабатывает во время закрытия окна. В этом обработчике будет прерывать поток, в котором происходит заполнение прогресс баров:

// если был создан поток - закрываем
if ( this._thread != null )
{
	this._thread.Abort();
}

Теперь перейдем к самому заполнению прогресс баров:

private void Work()
{
	var random = new Random();
	for ( int i = 0; i < this.LbActions.Items.Count; i++ )
	{
		var lbItem = this.LbActions.Items[ i ] as ListBoxDataItem;
		if ( lbItem == null )
		{
			return;
		}
		// увеличиваем прогресс
		do
		{
			lbItem = new ListBoxDataItem( lbItem, lbItem.Progress + 1, Colors.Yellow );
			lbItem.BackColor.Freeze();
			Application.Current.Dispatcher.BeginInvoke(
				DispatcherPriority.DataBind,
				new Action( () => this.LbActions.Items[ i ] = lbItem ) );
			Thread.Sleep( random.Next( 0, 50 ) );
		}
		while ( lbItem.Progress < 100 );
		// делаем цвет текущего элемента зеленым
		lbItem = new ListBoxDataItem( lbItem, Colors.GreenYellow );
		lbItem.BackColor.Freeze();
		Application.Current.Dispatcher.BeginInvoke(
			DispatcherPriority.DataBind,
			new Action( () => this.LbActions.Items[ i ] = lbItem ) );
		Thread.Sleep( 50 );
	}
}

Из кода выше видно, что в цикле идем по всем элементам ListBox‘а, приводим их к типу ListBoxDataItem, создаем на основе старых элементов новые, при этом увеличивая значение прогресса на 1 и меняя цвет фона на желтый. Это делаем с задержкой до тех пор, пока прогресс не достигнет 100. Далее меняем цвет текущего элемента на зеленый. И эти действия повторяем для каждого элемента.

И наконец, создадим обработчик клика для кнопки и запустим поток с заполнением прогресс баров:

private void BtnStart_Click( object sender, RoutedEventArgs e )
{
	this.FillListBox();
	this._thread = new Thread( this.Work );
	this._thread.Start();
}

Источники:

0
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest
0 комментариев
Межтекстовые Отзывы
Посмотреть все комментарии
Авторизация
*
*
Регистрация
*
*
*
Пароль не введен
*
Генерация пароля
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x
()
x