You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的属性变更通知。

解决步骤

  1. 让Book类实现INotifyPropertyChanged接口,确保内部属性变更时能通知View更新。
  2. 调整ConvertToTitle的触发时机,改为文本框失去焦点时执行,通过命令绑定实现。
  3. 移除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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 19:20:57