private void cmdFind_Click(object sender, RoutedEventArgs e)
        {
            // Disable the button and clear previous results.
            cmdFind.IsEnabled     = false;
            cmdCancel.IsEnabled   = true;
            lstPrimes.ItemsSource = null;
            lblStatus.Text        = "";

            // Get the search range.
            int from, to;

            if (!Int32.TryParse(txtFrom.Text, out from))
            {
                lblStatus.Text = "Invalid From value.";
                return;
            }
            if (!Int32.TryParse(txtTo.Text, out to))
            {
                lblStatus.Text = "Invalid To value.";
                return;
            }

            // Start the search for primes on another thread.
            threadWrapper            = new FindPrimesThreadWrapper(from, to);
            threadWrapper.Completed += threadWrapper_Completed;
            threadWrapper.Cancelled += threadWrapper_Cancelled;
            threadWrapper.Start();

            lblStatus.Text = "The search is in progress...";
        }
        private void threadWrapper_Completed(object sender, FindPrimesCompletedEventArgs e)
        {
            FindPrimesThreadWrapper thread = (FindPrimesThreadWrapper)sender;

            this.Dispatcher.BeginInvoke(delegate() {
                if (thread.Status == StatusState.Completed)
                {
                    int[] primes          = e.PrimeList;
                    lblStatus.Text        = "Found " + primes.Length + " prime numbers.";
                    lstPrimes.ItemsSource = primes;
                }

                cmdFind.IsEnabled   = true;
                cmdCancel.IsEnabled = false;
            }
                                        );
        }