Страницы

Поиск по вопросам

понедельник, 10 февраля 2020 г.

Как запретить обновления данных в WPF в TextBox?

#c_sharp #wpf


У меня есть метод во ViewModel который обновляет данные каждую секунду с базы, получается
когда я хочу изменить данные в TexBox данные которые я ввожу перебиваются каждую секунду,
как запретить обновлять данные когда TexBox находится в фокусе??
    


Ответы

Ответ 1



UPDATE: Более гибкое решение с использованием Behaviors: public class TextBoxBehavior : Behavior { private readonly DependencyProperty _targetProperty = TextBox.TextProperty; private BindingExpression _bindingExpression; private DateTime _keyDownRaisedAt; private bool _bindingCleared; protected override void OnAttached() { base.OnAttached(); _bindingExpression = AssociatedObject.GetBindingExpression(_targetProperty); AssociatedObject.LostFocus += AssociatedObjectOnLostFocus; AssociatedObject.PreviewTextInput += AssociatedObjectOnPreviewTextInput; AssociatedObject.PreviewKeyDown += AssociatedObjectOnPreviewKeyDown; } private void AssociatedObjectOnPreviewKeyDown(object sender, KeyEventArgs keyEventArgs) { _keyDownRaisedAt = DateTime.Now; } private void AssociatedObjectOnLostFocus(object sender, RoutedEventArgs routedEventArgs) { if (_bindingCleared && _bindingExpression != null) { string tmp = AssociatedObject.Text; AssociatedObject.SetBinding(_targetProperty, _bindingExpression.ParentBinding); _bindingCleared = false; AssociatedObject.SetCurrentValue(_targetProperty, tmp); } } private void AssociatedObjectOnPreviewTextInput(object sender, TextCompositionEventArgs textCompositionEventArgs) { if (!_bindingCleared && AssociatedObject.IsFocused && IsKeyDown()) { BindingOperations.ClearBinding(AssociatedObject, _targetProperty); _bindingCleared = true; } } private bool IsKeyDown() { DateTime currentTime = DateTime.Now; return (currentTime - _keyDownRaisedAt) < TimeSpan.FromMilliseconds(10); } protected override void OnDetaching() { base.OnDetaching(); AssociatedObject.PreviewTextInput -= AssociatedObjectOnPreviewTextInput; AssociatedObject.PreviewKeyDown -= AssociatedObjectOnPreviewKeyDown; AssociatedObject.LostFocus -= AssociatedObjectOnLostFocus; } } Для его подключения в XAML-разметке необходимо написать: И не забыть так же соответствующее пространство имён: xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" Решение с кастомным TextBox: public class CustomTextBox : TextBox { private DateTime _keyDownRaisedAt; private BindingExpression _binding; private bool _bindingCleared; protected override void OnPreviewKeyDown(KeyEventArgs e) { _keyDownRaisedAt = DateTime.Now; base.OnPreviewKeyDown(e); } public override void EndInit() { _binding = this.GetBindingExpression(TextProperty); base.EndInit(); } protected override void OnLostFocus(RoutedEventArgs e) { if (_bindingCleared) { var tmp = this.Text; SetBinding(TextProperty, _binding.ParentBinding); _bindingCleared = false; SetCurrentValue(TextProperty, tmp); } base.OnLostFocus(e); } protected override void OnPreviewTextInput(TextCompositionEventArgs e) { DateTime current = DateTime.Now; if (!_bindingCleared && IsFocused && (current - _keyDownRaisedAt) < TimeSpan.FromMilliseconds(10)) { BindingOperations.ClearBinding(this, TextProperty); _bindingCleared = true; } base.OnPreviewTextInput(e); } }

Ответ 2



Я пойду по пути постановки таймера на паузу, другой вариант мне кажется алогичным. У меня работает такой простой пример: Доменная модель данных из БД (у вас она есть своя): class UserModel { public int Id { get; set; } public string Name { get; set; } } Набросок репозитория для получения экземпляров UserModel: class UserRepository { List users; public UserRepository() { users = new List { new UserModel { Id = 1, Name = "Иванов Иван" }, new UserModel { Id = 2, Name = "Петров Петр" }, new UserModel { Id = 3, Name = "Сидоров Сидор" }, new UserModel { Id = 4, Name = "Антонов Антон" }, new UserModel { Id = 5, Name = "Сергеев Сергей" } }; } public UserModel GetUserByIndex(int index) => users[index]; public UserModel GetUserById(int id) => users.First(u => u.Id == id); public int UsersCount => users.Count; } "Стандартные" (немного упрощенные) вспомогательные классы для MVVM, честно собранные на просторах SO/ruSO: База для VM: class VM : INotifyPropertyChanged { protected void Set(ref T field, T value, [CallerMemberName] string propertyName = "") { field = value; NotifyPropertyChanged(propertyName); } protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); public event PropertyChangedEventHandler PropertyChanged; } Реализация ICommand: class DelegateCommand : ICommand { Action execute; Predicate canExecute = _ => true; public DelegateCommand(Action execute) { if (execute == null) throw new NullReferenceException(nameof(execute)); this.execute = execute; } public DelegateCommand(Action execute, Predicate canExecute) : this(execute) { if (canExecute == null) throw new NullReferenceException(nameof(canExecute)); this.canExecute = canExecute; } public event EventHandler CanExecuteChanged; public bool CanExecute(object parameter) => canExecute(parameter); public void Execute(object parameter) => execute(parameter); } Основная VM: class MainVM : VM { UserRepository repo = new UserRepository(); UserModel selectedUser; public UserModel SelectedUser { get { return selectedUser; } set { Set(ref selectedUser, value); } } DelegateCommand pauseCommand; public ICommand PauseCommand { get { if (pauseCommand == null) pauseCommand = new DelegateCommand(_ => PauseTimer()); return pauseCommand; } } DelegateCommand playCommand; public ICommand PlayCommand { get { if (playCommand == null) playCommand = new DelegateCommand(_ => StartTimer()); return playCommand; } } Timer timer = new Timer(1000); int currentIndex = -1; public MainVM() { timer.Elapsed += (o, e) => NextUser(); timer.Start(); } void NextUser() { currentIndex = (currentIndex + 1) % repo.UsersCount; SelectedUser = repo.GetUserByIndex(currentIndex); } void PauseTimer() { timer.Stop(); } void StartTimer() { // Здесь надо обновить запись в БД: // repo.UpdateUser(SelectedUser); timer.Start(); } } Разметка содержимого окна: