Пример #1
0
        /// <summary>Создаёт новый экземпляр TextBoxBindingState, записывает его в
        /// присоединённое к <see cref="TextBox"/> свойство BindingState
        /// и возвращает его.</summary>
        /// <param name="textBox"><see cref="TextBox"/> чьё свойство инициализируется.</param>
        /// <param name="isNumericOnly">Режим ввода только чисел.</param>
        /// <returns>Созданный в методе <see cref="TextBoxBindingState"/>.</returns>
        private static TextBoxBindingState InitBindingState(TextBox textBox, bool isNumericOnly)
        {
            TextBoxBindingState state = new TextBoxBindingState(null, textBox.Text, isNumericOnly);

            textBox.SetValue(BindingStatePropertyKey, state);
            return(state);
        }
Пример #2
0
        /// <summary>Обновляет значение свойств присоединённого свойства BindingState.</summary>
        /// <param name="textBox"><see cref="TextBox"/> свойство которого обновляется.</param>
        /// <param name="changes">Коллекция объектов, содержащий сведения о произошедших изменениях.</param>
        /// <remarks>Свойство <see cref="TextBoxBindingState.NewText"/> обновляется значением <paramref name="textBox"/>.Text,<br/>
        /// в свойство <see cref="TextBoxBindingState.Changes"/> записывается значение <paramref name="changes"/>.</remarks>
        private static void UpdateTextBindingState(TextBox textBox, ICollection <TextChange> changes)
        {
            TextBoxBindingState state = GetBindingState(textBox);

            //if (state.NewText != textBox.Text)
            state.UpdateText(textBox.Text);

            state.Changes = changes;
        }
Пример #3
0
            public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
            {
                object ret = null;

                object source = values[0];

                if (source == DependencyProperty.UnsetValue)
                {
                    return(source);
                }

                TextBox textBox = (TextBox)values[1];
                string  newText = textBox.Text;

                // Получение состояния привязки TextBox
                TextBoxBindingState bindingState = GetBindingState(textBox);
                string oldText = bindingState.OldText;

                // Получение парсера для типа в первой привязке (привязка к Источнику).
                TryParseNumberHandler tryParse = NumericTryParse.GetTryParse(source.GetType());

                // Если парсер получить не удалось, значит первая привязка получила значение недопустимого типа.
                if (tryParse == null)
                {
                    throw InvalidNumberType;
                }

                Debug.Write(GetType().Name + $".Convert.values: {source}, \"{oldText}\", \"{newText}\"");

                // Получение цифрового стиля из параметра. Если он не задан, то используется стиль по умолчанию для этого типа.
                NumberStyles style = NumericTryParse.GetNumberStyle(parameter, values[0].GetType());

                // Проверка причины вызова конвертера.
                switch (bindingState.UpdateSourceState)
                {
                // Метод UpdateSource() не вызывался, значит значение обновляется по привязке от Источника.
                case UpdateSourceStateEnum.NotCalled:

                    // Получение из TextBox числа в том же типе, что получено от Источника (в первой привязке).
                    // И сравнение этого числа с числом Источника.
                    if (tryParse(newText, style, culture, out object target) && target.Equals(source))
                    {
                        // Отменяется присвоение значения привязкой.
                        ret = Binding.DoNothing;
                    }

                    // Иначе возвращается число Источника в текстовом виде в заданной культуре.
                    else
                    {
                        ret = System.Convert.ToString(source, culture);
                    }
                    break;

                // Обновление источника произошло в ходе выполнения метода UpdateSource().
                // Значит в TextBox.Text корректное значение и его надо проверить только на крайние пробелы
                // для режима ввода "Tолько Числа".
                case UpdateSourceStateEnum.Called:
                    if (bindingState.IsNumericOnly)
                    {
                        if (newText.Length < 1 || char.IsWhiteSpace(newText[0]) || char.IsWhiteSpace(newText[newText.Length - 1]))
                        {
                            // Устанавливается флаг обновления по привязке
                            bindingState.TextChangeSource = PropertyChangeSourceEnum.Binding;
                            // Возращается TextBox старое значение.
                            UndoText(textBox, oldText, bindingState.Changes);
                        }
                    }
                    // Отменяется присваивание.
                    ret = Binding.DoNothing;

                    break;

                // После вызова UpdateSource() обновление источника не произошло
                // Значит в TextBox.Text некорректное значение и надо вернуть предыдущее его значение.
                case UpdateSourceStateEnum.CallCanceled:

                    // Устанавливается флаг обновления из конвертера привязки
                    bindingState.TextChangeSource = PropertyChangeSourceEnum.Binding;

                    // Проверяется на пустую строку.
                    // Если она пустая, то надо её заменить на "0".
                    // Это автоматически сделает метод UndoText().
                    if (string.IsNullOrWhiteSpace(newText))
                    {
                        ZeroText(textBox);
                    }

                    else if (!BeginScientific(newText))
                    {
                        // Возращается TextBox старое значение.
                        UndoText(textBox, oldText, bindingState.Changes);
                    }

                    // Сброс флага обновления из конвертера привязки
                    bindingState.TextChangeSource = PropertyChangeSourceEnum.NotBinding;

                    // Проверка строки в TextBox.
                    // Если она корректна, то для сброса валидации надо её же и вернуть
                    if (tryParse(textBox.Text, style, culture, out _))
                    {
                        ret = textBox.Text;
                    }
                    else
                    {
                        // Иначе - отмена присвоения по привязке
                        ret = Binding.DoNothing;
                    }

                    break;

                default:
                    break;
                }

                Debug.WriteLine($"; return: {ret ?? "null"}");

                if (ret is string str && str != textBox.Text)
                {
                    // Устанавливается флаг обновления по привязке
                    bindingState.TextChangeSource = PropertyChangeSourceEnum.Binding;
                }

                return(ret);;

                // Проверка строки на начало научной записи числа
                bool BeginScientific(string text)
                {
                    if (string.IsNullOrWhiteSpace(text))
                    {
                        return(false);
                    }
                    if (style.HasFlag(NumberStyles.AllowExponent))
                    {
                        if (text[text.Length - 1] == 'e' || text[text.Length - 1] == 'E')
                        {
                            text = text.Remove(text.Length - 1, 1);
                        }
                        else if (text.Length > 1 && (text[text.Length - 1] == '-' || text[text.Length - 1] == '+') &&
                                 (text[text.Length - 2] == 'e' || text[text.Length - 2] == 'E'))
                        {
                            text = text.Remove(text.Length - 2, 2);
                        }
                    }
                    return(tryParse(text, style, culture, out _));
                }
            }
Пример #4
0
        /// <summary>Метод-прослушка изменения текста в <see cref="TextBox"/> в котором была создана привязка <see cref="BindToNumericExtension"/>.</summary>
        /// <param name="sender"><see cref="TextBox"/> в котором изменился текст.</param>
        /// <param name="e">Параметры события. Не используются.</param>
        private void TextBoxTextChanged(object sender, TextChangedEventArgs e)
        {
            // Извлечение TetxBox и привязки его свойства Text
            TextBox textBox = (TextBox)sender;
            MultiBindingExpression multiBindingExpression = BindingOperations.GetMultiBindingExpression(textBox, TextBox.TextProperty);

            // Если привязка - это не PrivateMulti, то отключается прослушка и выход из метода.
            if (!(multiBindingExpression?.ParentMultiBinding is PrivateMulti bindPriv))
            {
                textBox.TextChanged -= TextBoxTextChanged;
                // Очистка приисоединённого свойства.
                ClearTextBindingState(textBox);
                return;
            }

            // Получение состояния привязки
            TextBoxBindingState bindingState = GetBindingState(textBox);

            // Сохранить новый текст и параметры изменения
            UpdateTextBindingState(textBox, e.Changes);

            // Если изменение произошло по привязке - выйти из метода
            if (bindingState.TextChangeSource == PropertyChangeSourceEnum.Binding)
            {
                // Сброс состояния обновления по привязке.
                bindingState.TextChangeSource = PropertyChangeSourceEnum.NotBinding;

                return;
            }

            // Создание триггера обновления источника и обновления источника в нём,
            // запоминание состояния триггера
            bool triggerHasUpdated;

            using (var trigger = new SourceUpdateTrigger(textBox, TextBox.TextProperty))
            {
                // Обновление источника с флагом обновления из метода UpdateSource().
                bindingState.UpdateSourceState = UpdateSourceStateEnum.Called;
                multiBindingExpression.UpdateSource();

                // Запоминание состояния триггера.
                triggerHasUpdated = trigger.HasUpdated;
            }

            // Получение привязки к источнику.
            BindingExpressionBase bindingSource = multiBindingExpression.BindingExpressions[0];

            // Проверка триггера обновления источника
            if (triggerHasUpdated)
            {
                // Вызов обновления привязки к источнику для передачи нового значения.
                //bindingSource.UpdateSource();
            }
            // Если обновления не было - значит была ошибка конвертера
            // Для режима ввода "Только числа" надо вызвать ковертер.
            else if (!triggerHasUpdated && bindPriv.IsNumericOnly)
            {
                // Установка флага отменённого обновления.
                bindingState.UpdateSourceState = UpdateSourceStateEnum.CallCanceled;

                // Вызов обновления привязки от источника для обработки причины прерывания обновления.
                bindingSource.UpdateTarget();
            }

            // Сброс флага обновления из метода UpdateSource().
            bindingState.UpdateSourceState = UpdateSourceStateEnum.NotCalled;
        }