public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { Debug.Write(GetType().Name + $".ConvertBack.value: \"{value}\" to "); object ret = null; string text = (string)value; // Получение парсера для типа в первой привязке (привязка к Источнику). TryParseNumberHandler tryParse = NumericTryParse.GetTryParse(targetTypes[0]); // Если парсер получить не удалось, значит первая привязка получила значение недопустимого типа. if (tryParse == null) { throw InvalidTargetNumberType; } // Получение цифрового стиля из параметра. Если он не задан, то используется стиль по умолчанию для этого типа. NumberStyles style = NumericTryParse.GetNumberStyle(parameter, targetTypes[0]); // Получение из строки числа заданного типа в заданной культуре // Если удалось, то оно возвращается. if (tryParse(text, style, culture, out object target)) { ret = target; } Debug.WriteLine($"return: {(ret == null ? "null" : target)}"); // Возвращается массив с одним элементом: полученным числом или null. return(new object[] { ret }); }
public virtual object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { Debug.Write(GetType().Name + $".ConvertBack.value: \"{value}\" to "); object ret = null; // Полученной значение может быть только строкой или null. if (value != null && value.GetType() != typeof(string)) { throw InvalidValueType; } // Если получен null или строка из пробелов, то используется "0". string text = (string)value; if (string.IsNullOrWhiteSpace(text)) { text = "0"; } // Должно быть две и только две привязки. if (targetTypes?.Length != 2) { throw InvalidNumberTargets; } // Получение парсера для типа в первой привязке (привязка к Источнику). TryParseNumberHandler tryParse = NumericTryParse.GetTryParse(targetTypes[0]); // Если парсер получить не удалось, значит первая привязка получила значение недопустимого типа. if (tryParse == null) { throw InvalidTargetNumberType; } // Тип значения по второй привязке (сравниваемый текст) должен быть только string. if (targetTypes[1] != typeof(string)) { throw InvalidTargetStringType; } // Получение цифрового стиля из параметра. Если он не задан, то используется стиль по умолчанию для этого типа. NumberStyles style = NumericTryParse.GetNumberStyle(parameter, targetTypes[0]); // Получение из полученного значения числа заданного типа в заданной культуре // Если удалось, то оно возвращается. if (tryParse(text, style, culture, out object target)) { ret = target; } Debug.WriteLine($"return: {ret ?? "null"}"); // Если ret значение не присваивалось, то значит строка некорректна // Тогда возвращается null, что вызывает ошибку валидации. if (ret == null) { return(null); } // Иначе возвращается массив с одним элементом: полученным числом. return(new object[] { ret }); }
public virtual object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { Debug.Write(GetType().Name + $".Convert.values: {values[0]}, \"{values[1]}\""); object ret = null; // Целевой тип должен быть или строкой или object. if (targetType != typeof(string) && targetType != typeof(object)) { throw InvalidTargetType; } // Должно передаваться две и только две привязки. if (values?.Length != 2) { throw InvalidNumberValues; } // Получение парсера для типа в первой привязке (привязка к Источнику). TryParseNumberHandler tryParse = NumericTryParse.GetTryParse(values[0]?.GetType()); // Если парсер получить не удалось, значит первая привязка получила значение недопустимого типа. if (tryParse == null) { throw InvalidNumberType; } object source = values[0]; // Тип значения по второй привязке (сравниваемый текст) должен быть только string. if (values[1]?.GetType() != typeof(string)) { throw InvalidStringType; } string text = (string)values[1]; // Получение цифрового стиля из параметра. Если он не задан, то используется стиль по умолчанию для этого типа. NumberStyles style = NumericTryParse.GetNumberStyle(parameter, values[0].GetType()); // Получение из строки (во второй привязке) числа в том же типе, что получено от Источника (в первой привязке). // И сравнение этого числа с числом Источника. if (tryParse(text, style, culture, out object target) && target.Equals(source)) { // Если числа равны, то проверяется наличие крайних пробелов в строке. // Если пробелов нет, то отменяется присвоение значения привязкой. string trim = text.Trim(); if (trim == text) { ret = Binding.DoNothing; } // Иначе присваивается строка с удалёнными крайними пробелами. else { ret = trim; } } // Если из строки не удалось получить число, или это число не равно числу Источника, // то возвращается число Источника в текстовом виде в заданной культуре. else { ret = System.Convert.ToString(source, culture); } Debug.WriteLine($"; return: {ret ?? "null"}"); return(ret);; }
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 _)); } }