Mateusz
Mateusz

Reputation: 171

How to get a value of gui element in WPF?

I'm trying to get a value of an element from dispatcher. However I can't wrap my head around how to pass it later in the code. I want to pass progress to the testbox about what was done.

I get the value whenever I just use ProductId.Text in the main thread.

Task.Run(() =>
            {
                ProductId.Dispatcher.Invoke(() =>
                {
                    string productId = ProductId.Text;});
                Console.WriteLine($"Creating game {productId}");
            });

I just want to pass the variable productId later in the code. Any ideas?

Upvotes: 0

Views: 809

Answers (1)

Panagiotis Kanavos
Panagiotis Kanavos

Reputation: 131219

From the comments, it seems there's a long-running background process that needs to post updates to the UI.

This is easy to do using the Progress class and the IProgress interface. This is described in Enabling Progress and Cancellation in Async APIs. The Progress can raise an event or call an Action<T> callback on the thread it was created on. The IProgress.Report method allows other threads to send messages to the Progress

Copying from the article's example, this method processes images in a background thread. Each time it wants to report progress, it calls progress.Report(message);

async Task<int> UploadPicturesAsync(List<Image> imageList, IProgress<string> progress)
{
        int totalCount = imageList.Count;
        int processCount = await Task.Run<int>(() =>
        {
            foreach (var image in imageList)
            {
                //await the processing and uploading logic here
                int processed = await UploadAndProcessAsync(image);
                if (progress != null)
                {
                    var message=$"{(tempCount * 100 / totalCount)}";
                    progress.Report(message);
                }
                tempCount++;
            }

            return tempCount;
        });
        return processCount;
}

All that's needed is to create a new Progress instance in the UI thread before starting the asynchronous method :

void ReportProgress(string message)
{
    //Update the UI to reflect the progress value that is passed back.
    txtProgress.Text=message;
}

private async void Start_Button_Click(object sender, RoutedEventArgs e)
{
    //construct Progress<T>, passing ReportProgress as the Action<T> 
    var progressIndicator = new Progress<int>(ReportProgress);

    //load the image list *before* starting the background worker
    var folder=txtPath.Text;
    var imageList=LoadImages(folder);
   //call async method
    int uploads=await UploadPicturesAsync(imageList, progressIndicator);
}

Reading from the UI

Another important thing is that UploadPicturesAsync doesn't try to read its input from the UI element, whichever it may be. It accepts the input it needs, the list of images, as a parameter. This makes it easier to run in the background, easier to test and a lot easier to modify.

For example, instead of reading from a text box, the code could be modified to display a Folder Browser dialog.

Upvotes: 1

Related Questions