В WPF, как я могу иметь привязку к LostFocus, но проверку к PropertyChanged?

Я хочу иметь возможность связать TextBox с UpdateSourceTrigger, установленным на LostFocus (по умолчанию), но выполнять проверку по мере того, как пользователь вводит текст. Лучшее, что я могу придумать, это обработать событие TextChanged и вызвать метод проверки для модели представления. Мне интересно, есть ли лучшее решение.

Моя модель просмотра прослушивает изменения свойств в модели, чтобы обновлять себя (включая форматирование). Я не хочу связываться с UpdateSourceTrigger, установленным в PropertyChanged, потому что это приводит к форматированию текста, как только пользователь вводит (например, пользователь может захотеть ввести «1.2», но как только он/она введет «1 " текст изменится на "1.0" из-за автоматического форматирования моделью представления).

оставьте UpdateSourceTrigger = PropertyChanged и добавьте Delay = 300 (это миллисекунды)

ASh 13.12.2020 19:53

@ASh Разве это не было бы неудобно с точки зрения удобства использования? Пользователь может ввести «1», и если он не наберет «.2» достаточно быстро, он увидит, что текст изменится на «1.0», что, я думаю, будет раздражать. Кроме того, проверка будет не немедленной, а отложенной (поскольку это происходит во время привязки).

redcurry 13.12.2020 19:59

IMO, это приятный побочный эффект, заключающийся в том, что проверка задерживается (лично мне не нравятся красные рамки/восклицательные знаки, когда я все еще печатаю). может быть, использовать StringFormat для форматирования?

ASh 13.12.2020 20:08

Могу я спросить, какую валидацию вы подаете? Вообще мне кажется немного странным иметь такое требование. Нехорошо, чтобы модель представления управляла форматированием текста. Эта логика должна быть реализована в элементе управления. С точки зрения данных не имеет значения, рассматривается ли число 1 как 1 или 1,0. Всегда 1. В вашем случае речь идет о презентации. Вы хотите отобразить 1 как 1.0. Это полностью не зависит от модели представления, нажимающей 1 на модель. И, очевидно, это даже не зависит от проверки, что доказывает, что десятичное требование не является обязательным для достоверности данных.

BionicCode 13.12.2020 22:37

Поэтому, чтобы принудительно использовать десятичные числа, вы должны переопределить UIElement.OnLostFocus или обработать перенаправленное событие элемента LostFocus, чтобы применить правила форматирования после завершения ввода. Это нормально, когда модель представления нормализует данные для представления: при чтении 1 из модели модель представления нормализует его до 1,0, прежде чем предоставлять данные представлению. Но модель представления не должна напрямую корректировать пользовательский ввод. Вы должны делегировать этот вид косметических средств элементу управления, который собирает входные данные и знает о связанных входных событиях. Это также решит вашу проблему.

BionicCode 13.12.2020 22:43

Невозможно заранее разделить условия проверки и обновления свойств. Но вы можете реализовать собственный Control, полученный из TextBox, и исправить это внутри.

aepot 13.12.2020 23:00

Привяжите свой TextBox к промежуточному строковому свойству, которое существует только в вашей ViewModel, вместо того, чтобы привязывать его непосредственно к вашей модели. Затем установите свой UpdateSourceTrigger=PropertyChanged. Это позволит выполнять проверку только на промежуточном звене, а не на вашей модели. В коде программной части xaml зарегистрируйтесь на событие LostFocus вашего TextBox. Внутри этого метода обработчика событий обновите модель только в том случае, если в промежуточной строке нет ошибок, чтобы текст модели отформатировался. Наконец, в том же методе верните текст вашей модели в промежуточное значение, чтобы обновить пользовательский интерфейс.

Tam Bui 13.12.2020 23:57

@BionicCode Проверка проверяет, является ли введенный текст числом и находится ли он в диапазоне. Для меня модели представления — это взаимно однозначное представление представлений, так что презентацию легче тестировать. Таким образом, мои модели представления содержат в основном строковые свойства, даже если их соответствующие свойства модели являются чем-то другим (например, int, double, DateTime). Это также дает мне больший контроль над тем, что отображается; например, мы можем отобразить «-», если базовое значение модели равно double.NaN. Я рассматриваю такую ​​логику представления как обязанность модели представления, потому что мы хотим, чтобы ее можно было протестировать.

redcurry 14.12.2020 01:37
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
8
470
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Развивая комментарий, который я оставил, вот пример того, как это можно сделать.

К вашему сведению, я использовал пакет nuget MvvmLight для сантехники.

MainWindow.xaml

<StackPanel>
    <TextBox x:Name = "myTextBox" Text = "{Binding SomeNumberViewModel.IntermediateText, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" Width = "100" Margin = "5"/>
    <Button Content = "Hi" HorizontalAlignment = "Center" Padding = "5,15" Margin = "5"/>
</StackPanel>

MainWindow.xaml.cs

public MainViewModel ViewModel
{
    get
    {
        return this.DataContext as MainViewModel;
    }
}
public MainWindow()
{
    InitializeComponent();
    this.myTextBox.LostFocus += MyTextBox_LostFocus;
}

private void MyTextBox_LostFocus(object sender, RoutedEventArgs e)
{
    // If the IntermediateText has no validation errors, then update your model.
    if (string.IsNullOrEmpty(this.ViewModel.SomeNumberViewModel[nameof(this.ViewModel.SomeNumberViewModel.IntermediateText)]))
    {
        // Update your model and it gets formatted result
        this.ViewModel.SomeNumberViewModel.ModelValue = this.ViewModel.SomeNumberViewModel.IntermediateText;

        // Then, update your IntermediateText to update the UI.
        this.ViewModel.SomeNumberViewModel.IntermediateText = this.ViewModel.SomeNumberViewModel.ModelValue;
    }
}

MainViewModel.cs

public class MainViewModel : ViewModelBase
{
    private SomeNumberViewModel someNumberViewModel;

    public string MyTitle { get => "Stack Overflow Question 65279367"; }

    public SomeNumberViewModel SomeNumberViewModel
    {
        get
        {
            if (this.someNumberViewModel == null)
                this.someNumberViewModel = new SomeNumberViewModel(new MyModel());
            return this.someNumberViewModel;
        }
    }
}

SomeNumberViewModel.cs

public class SomeNumberViewModel : ViewModelBase, IDataErrorInfo
{
    public SomeNumberViewModel(MyModel model)
    {
        this.Model = model;
    }

    private string intermediateText;
    public string IntermediateText { get => this.intermediateText; set { this.intermediateText = value; RaisePropertyChanged(); } }
    public string ModelValue 
    { 
        get => this.Model.SomeNumber.ToString("0.00"); 
        
        set 
        {
            try
            {
                this.Model.SomeNumber = Convert.ToDouble(value);
                RaisePropertyChanged();
            }
            catch
            {
            }
        } 
    }

    public MyModel Model { get; private set; }
    
    public string Error { get => null; }

    public string this[string columnName]
    {
        get
        {
            switch (columnName)
            {
                case "IntermediateText":
                    if (!string.IsNullOrEmpty(this.IntermediateText) && FormatErrors(this.IntermediateText))
                        return "Format errors";
                    break;
            }

            return string.Empty;
        }
    }

    /// <summary>
    /// Only allow numbers to be \d+, or \d+\.\d+
    /// For Example: 1, 1.0, 11.23, etc.
    /// Anything else is a format violation.
    /// </summary>
    /// <param name = "numberText"></param>
    /// <returns></returns>
    private bool FormatErrors(string numberText)
    {
        var valid = (Regex.IsMatch(numberText, @"^(\d+|\d+\.\d+)$"));
        return !valid;
    }
}

МояМодель.cs

public class MyModel
{
    public double SomeNumber { get; set; }
}

Я думаю, что это хорошее решение, за исключением того, что я бы попытался сделать логику более многоразовой, чтобы ее можно было применять к другим текстовым полям без дублирования кода.

redcurry 14.12.2020 02:11

@redcurry Понятно. Это было решение для первого шага, а не надежное решение.

Tam Bui 14.12.2020 17:11

Другие вопросы по теме