Обновить свойство объекта после изменения свойства в BindingList - PullRequest
0 голосов
/ 15 мая 2018

Я очень новичок в WPF и MVVM, и это очень расстраивает меня в данный момент.Моя текущая проблема:

У меня есть следующий XAML:

<Window.DataContext>
    <local:MainViewModel />
</Window.DataContext>

<WrapPanel Orientation="Horizontal">
    <ComboBox ItemsSource="{Binding StreetList}" SelectedItem="{Binding SelectedStreet}" DisplayMemberPath="StreetName" IsSynchronizedWithCurrentItem="True"/>
    <DataGrid ItemsSource="{Binding SelectedStreet.HouseList}" AutoGenerateColumns="False" >
        <DataGrid.Columns>
            <DataGridTextColumn Header="YearBuilt" Binding="{Binding YearBuilt}" />
            <DataGridTextColumn Header="Price" Binding="{Binding Price}" />
            <DataGridTextColumn Header="#Rooms" Binding="{Binding Rooms}" />
        </DataGrid.Columns>
    </DataGrid>
    <TextBox Text="{Binding AvgPricePerRoom, Mode=OneWay}" />
</WrapPanel>

где:

public class MainViewModel : ViewModelBase
{
    public BindingList<StreetViewModel> StreetList { get { return _streetList; } }
    private BindingList<StreetViewModel> _streetList;

    public StreetViewModel SelectedStreet
    {
        get { return _selectedStreet; }
        set { _selectedStreet = (StreetViewModel)value.Clone(); RaisePropertyChanged(); RaisePropertyChanged(nameof(AvgPricePerRoom)); }
    }
    private StreetViewModel _selectedStreet;

    public double AvgPricePerRoom
    {
        get
        {
            return
              _selectedStreet.HouseList[0].Price / _selectedStreet.HouseList[0].Rooms / 3 +
              _selectedStreet.HouseList[1].Price / _selectedStreet.HouseList[1].Rooms / 3 +
              _selectedStreet.HouseList[2].Price / _selectedStreet.HouseList[2].Rooms / 3;
        }
    }

    public MainViewModel()
    {
        _streetList = new BindingList<StreetViewModel>();
        fill_HouseGroupList();
    }

    void fill_HouseGroupList() { ... }
}

и ( EDITED ):

public class StreetViewModel : ViewModelBase, ICloneable
{
    public string StreetName
    {
        get { return _streetName; }
        set { _streetName = value; RaisePropertyChanged(); }
    }
    private string _streetName;


    private BindingList<HouseViewModel> _houseList;

    private void HouseList_ListChanged(object sender, ListChangedEventArgs e)
    {
        throw new NotImplementedException();
    }

    public StreetViewModel() { }

    public StreetViewModel(string streetName) : this()
    {
        _streetName = streetName;

        _houseList = new BindingList<HouseViewModel>();

        HouseList.ListChanged += HouseList_ListChanged;
    }

    public BindingList<HouseViewModel> HouseList
    {
        get
        {
            if(_houseList.Count == 0)
            {
                switch (_streetName)
                {
                    case "Main street":
                        _houseList.Add(new HouseViewModel(new House(2015, 600, 9)));
                        _houseList.Add(new HouseViewModel(new House(1929, 20, 4)));
                        _houseList.Add(new HouseViewModel(new House(1969, 30, 6)));
                        break;
                    case "School street":
                        _houseList.Add(new HouseViewModel(new House(2017, 50, 10)));
                        _houseList.Add(new HouseViewModel(new House(1930, 10, 5)));
                        _houseList.Add(new HouseViewModel(new House(1970, 20, 7)));
                        break;
                    case "Garden street":
                        _houseList.Add(new HouseViewModel(new House(2014, 1, 15)));
                        _houseList.Add(new HouseViewModel(new House(1935, 20, 11)));
                        _houseList.Add(new HouseViewModel(new House(1978, 20, 9)));
                        break;
                }
            }
            return _houseList;
        }
    }

    public object Clone()
    {
        var houseGroupCopy = new StreetViewModel(this._streetName);

        houseGroupCopy._houseList = new BindingList<HouseViewModel>();
        foreach (var house in _houseList)
        {
            houseGroupCopy._houseList.Add(new HouseViewModel(new House(house.YearBuilt, house.Price, house.Rooms)));
        }

        return houseGroupCopy;
    }
}

plus:

public class HouseViewModel : ViewModelBase
{
    public House House
    {
        get { return _house; }
        set { _house = value;  RaisePropertyChanged(); }
    }
    private House _house;

    public int YearBuilt
    {
        get { return _house.YearBuilt; }
        set { _house.YearBuilt = value; RaisePropertyChanged(); }
    }

    public double Price
    {
        get { return _house.Price; }
        set { _house.Price = value; RaisePropertyChanged(); }
    }

    public int Rooms
    {
        get { return _house.Rooms; }
        set { _house.Rooms = value; RaisePropertyChanged(); }
    }

    public HouseViewModel(House house)
    {
        _house = house;
    }
}

... поэтому в DataGrid моего представления я могу редактировать свойства элементов House в моем HouseList, который является BindingList.Как сделать так, чтобы свойство AvgPricePerRoom, которое отображается в TextBox, учитывало изменения в DataGrid, чтобы обновленный AvgPricePerRoom отображался при изменениях в Grid?Я использовал BindingList, потому что я прочитал, что в отличие от ObservableCollection он вызывает событие также при изменении свойств его элементов (кроме проблем с производительностью), но как мне обработать это событие и уведомить моего AvgPricePerRoom в этом примере?

(Не весь приведенный выше код важен для моей проблемы, и я прошу прощения за это ...)

Ответы [ 2 ]

0 голосов
/ 16 мая 2018

Я нашел решение, хотя я не уверен, является ли это «правильным способом» сделать это:

В соответствии с рекомендацией я превратил AvgPricePerRoom в полное свойство с полем поддержки и добавил функцию, которая устанавливаетAvgPricePerRoom:

void CalculateAverage()
{
    AvgPricePerRoom =
          _selectedStreet.HouseList[0].Price / _selectedStreet.HouseList[0].Rooms / 3 +
          _selectedStreet.HouseList[1].Price / _selectedStreet.HouseList[1].Rooms / 3 +
          _selectedStreet.HouseList[2].Price / _selectedStreet.HouseList[2].Rooms / 3;
}

Затем я изменил свойство SelectedStreet на:

public StreetViewModel SelectedStreet
{
    get { return _selectedStreet; }
    set
    {
        _selectedStreet = value.Clone() as StreetViewModel;
        CalculateAverage();
        _selectedStreet.HouseList.ListChanged += (o, e) => CalculateAverage();
        RaisePropertyChanged();
    }
}

Это правильное решение MVVM?

0 голосов
/ 15 мая 2018

При связывании коллекций существует 3 вида уведомлений об изменениях, с которыми необходимо позаботиться:

  1. Уведомления о добавлении / удалении из списка.Это единственное уведомление, которое ObservableCollection фактически удаляет.
  2. Уведомления для свойства, предоставляющего ObservableCollection (StreetList в вашем примере).OC очень плохо справляются с крупномасштабными изменениями (без функции AddRange), поэтому часто лучше создать новый список в коде, выставляя его только в качестве последнего шага.
  3. Уведомление об изменении каждого свойства StreetViewModel.
...