INotifyPropertyChanged и его нулевое событие

#c# #wpf #xaml

#c# #wpf #xaml

Вопрос:

Я хочу обновить свою метку в форме WPF с именем «CurrentMoney»; Я написал класс Money, который был реализован из «INotifyPropertyChanged».

UPD: Я изменен на шаблон MVVM с помощью класса create ViewModelBase. Все еще имеют значение «PropertyChaged» null. Как это исправить и почему это произошло?

Money.cs

     public class Money : ViewModelBase {
    private double currentMoney;

    public double CurrentMoney {
        get => currentMoney;
        set {
            currentMoney = value;
            OnPropertyChanged("CurrentMoney");
        }
    }

    public Money() => currentMoney = 10000;

    public int addMoney(double count) {
        CurrentMoney  = count;
        return 1;
    }       
    public int subMoney(double count) {
        CurrentMoney -= count;
        if (currentMoney < 0)
            return 100;
        return 1;
    }
}
  

MainWindow.cs

 public partial class MainWindow : Window {

    public Money currentMoney;

    public MainWindow ( ) {
        InitializeComponent();
        currentMoney = new Money();
        CurrentMoney.DataContext = currentMoney;
    }

    private void Initialize() {
        CurrentMoney.Content = "Current money: "   currentMoney.CurrentMoney;
        CurrentPollution.Content = CurrentPollution.Content.ToString()   Pollution.CurrentPollution;
        Facktories.Content = Facktories.Content.ToString()   FactoriesList.Quantity;
        FactoriesPollution.Content = FactoriesPollution.Content.ToString()   FactoriesList.FullPolution;
    }

    private void MenuItem_Click(object sender, RoutedEventArgs e) {
        var buy = new BuySMTH();
        this.Visibility = Visibility.Collapsed;
        buy.Show();
    }

    private void Window_ContentRendered(object sender, EventArgs e) {           
        Initialize();
    }

    private void Tmp_Click(object sender, RoutedEventArgs e) {
        currentMoney.addMoney(1000);
    }
  

MainWindow.xaml

 <Window x:Class="Coursage.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:Coursage"
    xmlns:model="clr-namespace:Coursage.Logic.Money"
    mc:Ignorable="d"
    ContentRendered="Window_ContentRendered"
    Title="MainWindow" Height="450" Width="545.802">
<Window.Resources>
    <model:Money x:Key="Money" ></model:Money>
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource Money}}">
    <Menu Panel.ZIndex="-1" Height="25px" Width="Auto" Background="Yellow" Margin="0,0,0,398">
        <MenuItem Header="Info" Background="Green"></MenuItem>
        <MenuItem Header="Buy" Click="MenuItem_Click" Background="Red"></MenuItem>
        <MenuItem Header="Info" Background="Blue"></MenuItem>
    </Menu>
    <Label Content="{Binding Path=CurrentMoney}" Name="CurrentMoney" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="10,23,0,0"/>
    <Label Content="Population: " Name="Population"  HorizontalAlignment="Left" VerticalAlignment="Top" Margin="10,49,0,0"/>
    <Label Content="Current pollution: " Name="CurrentPollution" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="10,80,0,0"/>
    <Label Content="Number of factories: " Name="Facktories" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="10,235,0,0"/>
    <Label Content="Pollution of factories: " Name="FactoriesPollution" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="10,261,0,0"/>
    <Label Content="Number of cars: " Name="Cars" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="250,235,0,0"/>
    <Label Content="Pollution of cars: " x:Name="CarsPollution" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="250,261,0,0"/>
    <Label Content="Start Date: " x:Name="StartDate" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="267,25,0,0"/>
    <Label Content="Day Count: " x:Name="DayCount" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="267,56,0,0"/>
    <Label Content="Date: " x:Name="Date" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="267,80,0,0"/>
    <Button Name="Tmp" Content="Button" HorizontalAlignment="Left" Margin="81,149,0,0" VerticalAlignment="Top" Width="75" Click="Tmp_Click"/>
</Grid>
  

Комментарии:

1. Лучше всего здесь найти рабочий пример простой viewmodel и очень точно имитировать этот пример вместо того, чтобы пытаться изобрести свой собственный способ ведения дел. Когда вы подражаете этому примеру, вы можете увидеть много вещей, которые, как вам кажется, вы могли бы сделать по-другому, потому что вы просто знаете, что они «не имеют значения». Но они действительно имеют значение. Не делайте ничего по-другому. Ваш код не соответствует ни одному из рабочих примеров, которые вы видели. Следуйте примерам.

2. Я бы посоветовал изучить MVVM и отказаться от написания кода позади.

Ответ №1:

Вы должны реализовать интерфейс в самом свойстве. Кроме того, при вызове события PropertyChanged вы должны использовать локальный (ограниченный) дескриптор события, чтобы избежать условий гонки.

 public class Money : INotifyPropertyChanged {
    double _currentMoney;

    public event PropertyChangedEventHandler PropertyChanged;
    public double CurrentMoney 
    { 
         get => _currentMoney;
         set
         {
             _currentMoney = value;
             OnPropertyChanged();
         }
    }

    public Money() => CurrentMoney = 1000;

    public int addMoney(double count) {
        CurrentMoney  = count;
        return 1;
    }       

    public int subMoney(double count) {
        CurrentMoney -= count;
        if (CurrentMoney < 0) { return 100; }
        return 1;
    }

    public void OnPropertyChanged([CallerMemberName] string propertyName = null) 
    {
        var handle = PropertyChanged;
        handle?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

}
  

РЕДАКТИРОВАТЬ: Чтобы сэкономить время на вводе текста, вы также можете создать класс ViewModelBase для обработки более мелких деталей.

 public abstract class ViewModelBase : INotifyPropertyChanged, IDisposable
{
    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged([CallerMemberName] string propertyName = null) 
    {
        var handle = PropertyChanged;
        handle?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    public virtual void Dispose() => PropertyChanged = null;
}
  

Затем при реализации класса ViewModel просто наследуется от base (но все равно уведомляет об изменениях свойств).

 public class MyClass : ViewModelBase
{
    string _myField;
    public string MyProperty
    {
        get => _myField;
        set
        {
            _myField = value;
            OnPropertyChanged();
        } 
    }
}
  

ПРИМЕЧАНИЕ: Фактическая ошибка возникает из атрибута [CallerMemberName] и вызова метода из другого метода. Вы можете либо передать имя свойства в качестве параметра, либо использовать метод from с самим свойством без необходимости указывать имя свойства.

Комментарии:

1. Я изменил код, можете посмотреть на XAML. Все еще есть свойство, измененное на NULL

2. Вы устанавливаете 2-сторонние привязки к ярлыкам, которые пользователь не может редактировать, вы устанавливаете содержимое ярлыков в xaml и в коде, и вам нужно создать DataContext (ViewModel) для вашего окна или установить привязку к окну и указать путь к свойству.

Ответ №2:

NotifyPropertyChanged принимает propertyName в качестве параметра. Значение по умолчанию равно null.

 private void NotifyPropertyChanged([CallerMemberName] string propertyName = null) {
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
  

Вы можете использовать NotifyPropertyChanged(«propertyName»), если хотите увидеть изменения в «propertyName» в пользовательском интерфейсе.

Для вашего примера имя свойства — «CurrentMoney»

Ответ №3:

Вам нужно передать имя реквизита, которое изменено

NotifyPropertyChanged("CurrentMoney");