WPF属性Setter中文本首字母大写实现及绑定不生效问题排查
WPF绑定NewBook属性后Setter修改不生效的问题解决
开发C# WPF项目时,View端将ViewModel的NewBook属性绑定到文本框,但该属性Setter中的修改无法在View端体现。需求是文本框失去键盘焦点时,通过ConvertToTitle方法将字符串转为标题格式(首字母大写),且不想定义单独属性,直接通过NewBook实现。
问题原因
- Book类未实现INotifyPropertyChanged:文本框绑定的是Book对象的具体属性(如Title),当ConvertToTitle通过反射修改这些属性时,Book没有通知View属性已变更,导致界面不更新。
- ConvertToTitle触发时机错误:当前仅在NewBook对象被重新赋值时(Setter触发)才执行格式化,而非文本框失去焦点时,不符合需求。
- ViewModel的NewBook属性通知逻辑不覆盖内部属性变化:NewBook的
SetProperty仅在对象引用变更时发送通知,Book内部属性变化不会触发ViewModel的属性变更通知。
解决步骤
- 让
Book类实现INotifyPropertyChanged接口,确保内部属性变更时能通知View更新。 - 调整
ConvertToTitle的触发时机,改为文本框失去焦点时执行,通过命令绑定实现。 - 移除
NewBookSetter中不必要的ConvertToTitle调用,避免对象赋值时重复格式化。
修改后的代码
1. Book类实现INotifyPropertyChanged
using System.ComponentModel; using System.Runtime.CompilerServices; public class Book : INotifyPropertyChanged { private string _title; public string Title { get => _title; set { if (_title != value) { _title = value; OnPropertyChanged(); } } } private string _barkod; public string Barkod { get => _barkod; set { if (_barkod != value) { _barkod = value; OnPropertyChanged(); } } } private int _demirbasNo; public int DemirbasNo { get => _demirbasNo; set { if (_demirbasNo != value) { _demirbasNo = value; OnPropertyChanged(); } } } // 其他Book属性需按相同模式实现Setter通知 public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
2. 修改ViewModel,添加格式化命令
internal class NewBookViewModel : ObservableObject { LibraryDataContext Db; public RelayCommand SaveCommand { get; private set; } public RelayCommand CloseWindowCommand { get; } public RelayCommand FormatBookCommand { get; private set; } private Book _newBook; public Book NewBook { get { return _newBook; } set { SetProperty(ref _newBook, value); // 移除此处的ConvertToTitle调用,改为焦点失去时触发 } } private int _demirbasNo; public int DemirbasNo { get { return _demirbasNo; } set { SetProperty(ref _demirbasNo, value); NewBook.Barkod = Barcode.GenerateEAN13BarcodeNumber(_demirbasNo); NewBook.DemirbasNo = _demirbasNo; } } private Author Author; private Yayinevi publisher; public ObservableCollection<Book> BooklarCollection { get; set; } public NewBookViewModel() { SaveCommand = new RelayCommand(SaveExecute, CanExecuteSaveCommand); CloseWindowCommand = new RelayCommand(CloseExecute); FormatBookCommand = new RelayCommand(FormatBookExecute); Db = new LibraryDataContext(); NewBook = new Book(); DemirbasNo = BookService.NextDemirbaşNo(); NewBook.Barkod = Barcode.GenerateEAN13BarcodeNumber(_demirbasNo); } private bool CanExecuteSaveCommand() => true; private void SaveExecute() { BookValidator validator = new BookValidator(); ValidationHelper<Book> validationHelper = new ValidationHelper<Book>(validator); MessagePublisher.SendMessage(validationHelper.ErrorMessage(NewBook), MessageType.Error); /* AuthorCheck(newBook.Authori.AdiSoyadi); PublisherCheck(newBook.Yayinevi.Adi); BookService<Book> BookService = new BookService<Book>(); BookService.Add(newBook); BookService.SaveChanges(); CloseExecute(); */ } private void CloseExecute() { var activeWindow = Application.Current.Windows.OfType<BaseWindow>().SingleOrDefault(w => w.IsActive); if (activeWindow != null) { activeWindow.Close(); } } private void FormatBookExecute() { NewBook.ConvertToTitle(); } } public static class BookExtensions { public static void ConvertToTitle<T>(this T obj) { PropertyInfo[] propertyInfo = typeof(T).GetProperties(); foreach (var property in propertyInfo) { if (property.PropertyType == typeof(string)) { string value = (string)property.GetValue(obj); if (!string.IsNullOrEmpty(value)) { property.SetValue(obj, CultureInfo.CurrentCulture.TextInfo.ToTitleCase(value)); } } } } }
3. View端绑定文本框的LostFocus事件到命令
先通过NuGet安装Microsoft.Xaml.Behaviors.Wpf包,然后在XAML中添加命名空间:
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
修改文本框绑定:
<TextBox Text="{Binding NewBook.Title, UpdateSourceTrigger=LostFocus}"> <i:Interaction.Triggers> <i:EventTrigger EventName="LostFocus"> <i:InvokeCommandAction Command="{Binding FormatBookCommand}" /> </i:EventTrigger> </i:Interaction.Triggers> </TextBox>
内容的提问来源于stack exchange,提问作者Fatih Uyanık
相关产品推荐
相关产品推荐

