TOC

This article is currently in the process of being translated into Persian (~51% done).

کنترل های متفرقه:

کنترل ProgressBar

WPF دارای یک کنترل مفید برای نمایش پیشرفت (progress) می باشد که ProgressBar نامگذاری شده است. این کنترل توسط تنظیم یک مقدار حداقل و یک مقدار حداکثر، و سپس یک مقدار فزاینده کار می کند که یک نشانگر بصری را به نمایش می گذارد که نشان می دهد فرایند تا چه حدی پیشرفت کرده است. در زیر یک مثال بسیار ساده از این کنترل را مشاهده می کنید:

<Window x:Class="WpfTutorialSamples.Misc_controls.ProgressBarSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ProgressBarSample" Height="100" Width="300">
    <Grid Margin="20">
        <ProgressBar Minimum="0" Maximum="100" Value="75" />
    </Grid>
</Window>

در این حالت، از یک رویکرد ساده برای نمایش پیشرفت با استفاده از درصد (مقدار بین 0 و 100) استفاده کردیم و مقدار اولیه 75 را به آن داده ایم. رویکرد دیگر استفاده از مقادیر واقعی حداقلی و حداکثری از لیستی از کارهایی که باید اجرا شوند است. برای نمونه، اگر در مجموعه ای شامل لیستی از فایل ها باشید، که هرکدام از آنها بررسی می شود، می توانید حداقل مقدار را برابر 0 قرار دهید و حداکثر به تعداد فایل های درون لیست و سپس مقدار را با هردور از حلقه افزایش دهید.

ProgressBar مانند سایر کنترل های WPF متناسب با سیستم عامل اجرا کننده، رندر می شود. در زیر از ویندوز 7 استفاده شده که از طیف رنگی زیبایی استفاده می کند همانطور که در تصویر زیر مشاهده می کنید.

نمایش پیشرفت، برای تسک های طولانی مدت

مثال زیر نشان دهنده استفاده از یک ProgressBar است، اما طبیعتا می توانید پیشرفت را برای برخی تسک های حقیقی انجام دهید و نه بعنوان یک مقدار ثابت.

در بسیاری از موقعیت ها شما از ProgressBar برای نمایش پیشرفت تسک های سنگین و نیازمند زمان برای تکمیل استفاده می کنید و اینجا دقیقا زمانیست که اکثر برنامه نویسان مرتکب اشتباهی رایج می شوند: اکثرا قسمتی از کار سنگین را بر روی رشته اصلی برنامه انجام دهید، زمانیکه بطور همزمان نیازدارید تا بروز رسانی کنترلی را (مثلا برای ProgressBar) بر روی همان رشته انجام دهید، بزودی متوجه خواهید شد که نمی توانید هردوی اینها را همزمان و بر روی یک رشته انجام دهید. یا برای روشن تر شدن موضوع، می توانید این کار را بکنید اما ProgressBar در واقع بروزرسانی پیشرفت را تا قبل از تکمیل تسک انجام نمی دهد و در واقع رندر آن بی فایده است.

برای نمایش این موضوع، مثال زیر را ملاحظه کنید:

<Window x:Class="WpfTutorialSamples.Misc_controls.ProgressBarTaskOnUiThread"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ProgressBarTaskOnUiThread" Height="100" Width="300"
        ContentRendered="Window_ContentRendered">
    <Grid Margin="20">
        <ProgressBar Minimum="0" Maximum="100" Name="pbStatus" />
    </Grid>
</Window>
using System;
using System.Threading;
using System.Windows;

namespace WpfTutorialSamples.Misc_controls
{
	public partial class ProgressBarTaskOnUiThread : Window
	{
		public ProgressBarTaskOnUiThread()
		{
			InitializeComponent();
		}

		private void Window_ContentRendered(object sender, EventArgs e)
		{
			for(int i = 0; i < 100; i++)
			{
				pbStatus.Value++;
				Thread.Sleep(100);
			}
		}
	}
}

مثالی بسیار پایه ای، که در آن به محض آماده سازی پنجره، یک حلقه را از 0 تا صد انجام می دهیم و در هر دور، مقدار ProgressBar را افزایش می دهیم. کامپیوتر های مدرن می توانند این کار را در کمتر از یک چشم به هم زدن انجام دهند، پس تاخیری به میزان 100 میلی ثانیه برای هردور ایجاد کردیم. متاسفانه، همانطور که قبلا گفته شد، هیچ اتفاقی رخ نمی دهد. این تصویر نشان دهنده ProgressBar در میانه کار است:

توجه داشته باشید که نشانگر موس نشان می دهد که اتفاقی در حال رخ دادن است و هنوز ظاهر ProgressBar شبیه وضعیتی است که در زمان شروع فرایند بود. (خالی است). به محض اتمام حلقه در حالی که تسک زمانبر ما به اتمام می رسد، ProgressBar شبیه تصویر زیر می شود:

That really didn't help your users see the progress! Instead, we have to perform the task on a worker thread and then push updates to the UI thread, which will then be able to immediately process and visually show these updates. An excellent tool for handling this job is the BackgroundWorker class, which we talk much more about elsewhere in this tutorial. Here's the same example as above, but this time using a BackgroundWorker:

<Window x:Class="WpfTutorialSamples.Misc_controls.ProgressBarTaskOnWorkerThread"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ProgressBarTaskOnWorkerThread" Height="100" Width="300"
        ContentRendered="Window_ContentRendered">
    <Grid Margin="20">
        <ProgressBar Minimum="0" Maximum="100" Name="pbStatus" />
    </Grid>
</Window>
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows;

namespace WpfTutorialSamples.Misc_controls
{
	public partial class ProgressBarTaskOnWorkerThread : Window
	{
		public ProgressBarTaskOnWorkerThread()
		{
			InitializeComponent();
		}

		private void Window_ContentRendered(object sender, EventArgs e)
		{
			BackgroundWorker worker = new BackgroundWorker();
			worker.WorkerReportsProgress = true;
			worker.DoWork += worker_DoWork;
			worker.ProgressChanged += worker_ProgressChanged;

			worker.RunWorkerAsync();
		}

		void worker_DoWork(object sender, DoWorkEventArgs e)
		{
			for(int i = 0; i < 100; i++)
			{
				(sender as BackgroundWorker).ReportProgress(i);
				Thread.Sleep(100);
			}
		}

		void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
		{
			pbStatus.Value = e.ProgressPercentage;
		}
	}
}

As you can see on the screenshot, the progress is now updated all the way through the task, and as the cursor indicates, no hard work is being performed on the UI thread, which means that you can still interact with the rest of the interface.

Please be aware that while the BackgroundWorker does help a lot with multithreading related problems, there are still some things you should be aware of, so please have a look at the BackgroundWorker articles in this tutorial before doing anything more advanced than a scenario like the one above.

Indeterminate

For some tasks, expressing the progress as a percentage is not possible or you simply don't know how long it will take. For those situations, the indeterminate progress bar has been invented, where an animation lets the user know that something is happening, while indicating that the running time can't be determined.

The WPF ProgressBar supports this mode through the use of the IsIndeterminate property, which we'll show you in the next example:

<Window x:Class="WpfTutorialSamples.Misc_controls.ProgressBarIndeterminateSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ProgressBarIndeterminateSample" Height="100" Width="300">
    <Grid Margin="20">
        <ProgressBar Minimum="0" Maximum="100" Name="pbStatus" IsIndeterminate="True" />
    </Grid>
</Window>

Notice that the green progress indicator is not anchored to either of the sides - instead it floats freely from start to finish and then it starts all over again.

ProgressBar with text

One thing that I really missed from the standard WPF ProgressBar is the ability to show a text representation of the progress as well as the progress bar. Fortunately for us, the flexibility of WPF makes this really easy for us to accomplish. Here's an example:

<Window x:Class="WpfTutorialSamples.Misc_controls.ProgressBarTextSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ProgressBarTextSample" Height="100" Width="300">
    <Grid Margin="20">
        <ProgressBar Minimum="0" Maximum="100" Value="75" Name="pbStatus" />
        <TextBlock Text="{Binding ElementName=pbStatus, Path=Value, StringFormat={}{0:0}%}" HorizontalAlignment="Center" VerticalAlignment="Center" />
    </Grid>
</Window>

We accomplish the above by putting the ProgressBar and the TextBlock showing the percentage inside of the same Grid, without specifying any rows or columns. This will render the TextBlock on top of the ProgressBar, which is exactly what we want here, because the TextBlock has a transparent background by default.

We use a binding to make sure that the TextBlock show the same value as the ProgressBar. Notice the special StringFormat syntax, which allows us to show the value with a percentage sign postfix - it might look a bit strange, but please see the StringFormat article of this tutorial for more information on it.


This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!