更新标签的文本,而无需从每个属性调用标签的NotifyPropertyChanged

时间:2019-08-04 19:13:02

标签: c# wpf xaml mvvm

我正在尝试实现WPF表单(显示员工数据),以便在任何字段中进行更改时,头部标签(FullnameTitle)都带有星号。例如。约翰·史密斯*

我使用的是MVVM,它的ViewModel库还可以跟踪更改。我还选择了将模型属性分开(即NotifyPropertyChanged在ViewModel中)

以下是我目前所拥有的。我知道要更新标题标签,我需要在所有表单字段上调用from sqlalchemy.sql import text session = ... insert_qry = """ INSERT INTO test (id, name) VALUES(:id1, :name1), (:id2, :name2) ON DUPLICATE KEY UPDATE id=VALUES(id), name=VALUES(name) """ params = {'id1':1, 'name1': 'John', 'id2':2, 'name2': 'Bill'} session.execute(text(insert_qry), params)

我的问题是,有没有一种更好的方法,不需要在每个字段都调用它? (我的完整员工表格有50多个字段)

Window XAML

NotifyPropertyChanged("FullnameTitle")

ViewModel

<Label x:Name="HeadLabel" Content="{Binding FullnameTitle, FallbackValue='Employee Details'}" FontSize="28"/>
<StackPanel>
    <Label Content="Employee ID"/>
    <TextBox IsEnabled="False" Text="{Binding Employee.EmployeeId}"/>
    <Label Content="First name"/>
    <TextBox Text="{Binding Firstname, TargetNullValue=''}"/>
    <Label Content="Surname"/>
    <TextBox Text="{Binding Surname, TargetNullValue=''}"/>
    <Label Content="DOB"/>
    <DatePicker SelectedDate="{Binding Dob, TargetNullValue=''}"/>
    <Label Content="Age"/>
    <TextBox Text="{Binding Age, Mode=OneWay}" IsReadOnly="True"/>
</StackPanel>

ViewModelBase

class EmployeeViewModel : ViewModelBase
{
    public EmployeeModel Employee { get; set; }

    public string FullnameTitle
    {
        get
        {
            return string.Format("{0} {1}{2}", Employee.Firstname, Employee.Surname, IsDirty ? "*" : "");
        }
    }

    public string Firstname
    {
        get
        {
            return Employee.Firstname;
        }
        set
        {
            Employee.Firstname = ApplyPropertyChange(Employee.Firstname, value, "Firstname");
        }
    }

    public string Surname
    {
        get
        {
            return Employee.Surname;
        }
        set
        {
            Employee.Surname = ApplyPropertyChange(Employee.Surname, value, "Surname");
        }
    }

    public DateTime? Dob
    {
        get
        {
            return Employee.Dob;
        }
        set
        {
            Employee.Dob = ApplyPropertyChange(Employee.Dob, value, "Dob");
            NotifyPropertyChanged("Age");
        }
    }

    public int Age
    {
        get
        {
            return Employee.Age;  // Age calculation done in EmployeeModel
        }
    }
}

1 个答案:

答案 0 :(得分:-1)

使用事件。

在您的视图模型中,订阅PropertyChanged

EmployeeViewModel.cs

class EmployeeViewModel : ViewModelBase
{
  public EmployeeViewModel()
  {
    this.PropertyChanged += (s, e) => NotifyPropertyChanged(nameof(this.FullnameTitle));
  }

  ...
}

但是要小心。调用PropertyChanged事件发起者NotifyPropertyChanged的属性是 本身引发PropertyChanged事件将导致无限递归。

更好的替代方法

使用由IMultiValueConverter触发的多重绑定ViewModelBase.IsDirty

IsDirtyToStringConverter.cs

class IsDirtyToStringConverter : IMultiValueConverter
{
  #region Implementation of IMultiValueConverter

  public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
  {
    if (values[0] is EmployeeViewModel viewModel && values[1] is bool modelIsDirty)
    {
      return string.Format("{0} {1}{2}", 
        viewModel.Firstname, 
        viewModel.Surname, 
        modelIsDirty 
          ? "*" 
          : string.Empty);
    }

    return Binding.DoNothing;
  }

  public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
  {
    throw new NotSupportedException();
  }

  #endregion
}

Window.xaml

<Window.Resources>
  <IsDirtyToStringConverter x:Keyy="IsDirtyToStringConverter" />
</Window.Resources>

<TextBlock x:Name="HeadLabel" FallbackValue='Employee Details'}" FontSize="28" >
  <TextBlock.Text>
    <MultiBinding Converter="{StaticResource IsDirtyToStringConverter}"
                  FallbackValue='Employee Details'>
       <Binding Path="." />           
       <Binding Path="IsDirty" />
     </MultiBinding>
   </TextBlock.Text>
</TextBlock>
<StackPanel>
  <Label Content="Employee ID"/>
  <TextBox IsEnabled="False" Text="{Binding Employee.EmployeeId}"/>
  <Label Content="First name"/>
  <TextBox Text="{Binding Firstname}"/>
  <Label Content="Surname"/>
  <TextBox Text="{Binding Surname}"/>
  <Label Content="DOB"/>
  <DatePicker SelectedDate="{Binding Dob}"/>
  <Label Content="Age"/>
  <TextBlock Text="{Binding Age}" />
</StackPanel>