/// <summary>
        /// Calculates the strength of the given <paramref name="password"/> and
        /// fires the <c>ScoreUpdated</c> event if the calculation succeeded.
        /// The function protects against too frequent updates by cancelling
        /// calculations that are still running while a new calculation was already
        /// started. It is therefore safe to call this method in quick succession.
        /// </summary>
        /// <param name="password"></param>
        private void CalculateResult(string password)
        {
            // The provided password will sometimes be null when the dialog was
            // closed before our calc thread got a chance to do its thing, so
            // we need to handle this case here.
            if (password == null)
            {
                return;
            }

            // This will avoid updating the UI too often on quick password input
            for (int i = 0; i < 10; i++)
            {
                Thread.Sleep(5);
                if (_cts.IsCancellationRequested)
                {
                    return;
                }
            }

            // Now we should be good to calculate
            PasswordStrengthResult result = new PasswordStrengthResult();

            result.PasswordLength = password.Length;

            if (result.PasswordLength > PW_MIN_LENGTH)
            {
                result.StrengthPoints = result.PasswordLength;
            }
            else
            {
                result.StrengthPoints = (int)(result.PasswordLength / 2);
            }

            for (int i = 0; i < password.Length; i++)
            {
                char c = password[i];

                // Uppercase
                if (c >= 65 && c <= 90)
                {
                    if (!result.UppercaseUsed)
                    {
                        result.StrengthPoints += 2;
                        result.UppercaseUsed   = true;
                    }
                }
                // Lowercase
                else if (c >= 97 && c <= 122)
                {
                    if (!result.LowercaseUsed)
                    {
                        result.LowercaseUsed = true;
                    }
                }
                // Digit
                else if (c >= 48 && c <= 57)
                {
                    if (!result.DigitsUsed)
                    {
                        result.StrengthPoints += 2;
                        result.DigitsUsed      = true;
                    }
                }
                // Symbol
                else
                {
                    if (!result.SymbolsUsed)
                    {
                        result.StrengthPoints += 2;
                        result.SymbolsUsed     = true;
                    }
                }

                if (result.AllCharClassesUsed)
                {
                    break;                            // No need to look any further
                }
                if (_cts.IsCancellationRequested)
                {
                    return;
                }
            }

            if (result.StrengthPoints < STRENGTH_POINTS_MIN_MEDIUM)
            {
                result.Rating = PasswordRating.POOR;
            }
            else if (result.StrengthPoints < STRENGTH_POINTS_MIN_GOOD)
            {
                result.Rating = PasswordRating.MEDIUM;
            }
            else
            {
                result.Rating = PasswordRating.GOOD;
            }

            // Last chance to detect cancellation
            if (_cts.IsCancellationRequested)
            {
                return;
            }

            // Fire score updated event
            ScoreUpdated?.Invoke(this, new ScoreUpdatedEventArgs(result));
        }
 public ScoreUpdatedEventArgs(PasswordStrengthResult passwordScore)
 {
     Score = passwordScore;
 }