WPF ListView не обновляется при изменении свойств

#c# #wpf #listview #binding #inotifypropertychanged

#c# #wpf #listview #привязка #inotifypropertychanged

Вопрос:

У меня есть представление списка WPF, в котором отображаются материал, его толщина и единица измерения толщины в поле со списком … xaml выглядит так (я удалил все настройки визуализации для наглядности):

     <ListView ItemsSource="{Binding Path=MaterialLayers}" IsSynchronizedWithCurrentItem="True">
        <ListView.Resources>
            <x:Array x:Key="DistanceUnitItems" Type="sys:String" xmlns:sys="clr-namespace:System;assembly=mscorlib">
                <sys:String>cm</sys:String>
                <sys:String>inches</sys:String>
            </x:Array>
            <DataTemplate x:Key="ThicknessUnit">
                <ComboBox ItemsSource="{StaticResource DistanceUnitItems}" SelectedIndex="{Binding ThicknessUnit}" />
            </DataTemplate>
        </ListView.Resources>
        <ListView.View>
            <GridView>
                <GridViewColumn Header="Material Name" DisplayMemberBinding="{Binding MaterialName}"/>
                <GridViewColumn Header="Material Thickness" DisplayMemberBinding="{Binding MaterialThickness}"/>  />
                <GridViewColumn Header="Thickness Unit" CellTemplate="{StaticResource ThicknessUnit}"  />
            </GridView>
        </ListView.View>
    </ListView>
 

MaterialLayers является ли ObservableCollection<MaterialLayer>

MaterialLayer имеет свойства для MaterialName, MaterialThickness и ThicknessUnit (что равно 0 для cm и 1 для дюймов). MaterialThickness преобразует внутренне сохраненное значение (которое выражается в сантиметрах) в единицу измерения, указанную ThicknessUnit .

При изменении ThicknessUnit моя DataViewModel вызывает обработчик PropertyChanged события с «MaterialLayers» в качестве имени свойства.

Итак, я ожидал, что MaterialThickness будет обновляться автоматически при изменении ThicknessUnit .

Я отладил его, и вызывается PropertyChanged(«MaterialLayers»).(Когда вызывается метод набора ThicknessUnit, он вызывает событие в родительском классе данных (MyData), которое вызывает событие в DataViewModel, которое вызывает обработчик PropertyChanged .)

Соответствующий код из DataViewModel

     public delegate void DataChangedHandler(String identifier);

    public DataViewModel()
    {
        Data = new MyData();
        Data.DataChanged  = new DataChangedHandler(RaisePropertyChanged);
    }

    public ObservableCollection<XTRRAMaterialLayer> MaterialLayers
    {
        get { return _data.MaterialLayers; }
        set { }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public void RaisePropertyChanged(string propertyName)
    {
        // take a copy to prevent thread issues
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
 

Соответствующий код из MyData

 public class XTRRAData
{
    public ObservableCollection<XTRRAMaterialLayer> MaterialLayers { get; set; }

    public event DataChangedHandler DataChanged;



    public MyData()
    {
        MaterialLayers = new ObservableCollection<MaterialLayer>();
    }

    public void myDataChanged(String identifier)
    {
        DataChanged(identifier);
    }

    public void AddLayer()
    {
        MaterialLayer layer = new MaterialLayer() { MaterialName="Test", MaterialThickness=5, ThicknessUnit=0 };

        layer.DataChanged  = new DataChangedHandler(myDataChanged); 
    }
}
 

Соответствующий код из MaterialLayer

 public class XTRRAMaterialLayer
{
    public XTRRAMaterialLayer()
    {
        _thicknessUnit = 0; // cm
    }
    public event DataChangedHandler DataChanged;

    public String MaterialName { get; set; }

    public double MaterialThickness
    {
        get
        {
            switch (_thicknessUnit)
            {
                default:
                case 0:
                    return DIST;
                case 1:
                    return DIST * 0.393700787;
            }
        }
        set
        {
            switch (_thicknessUnit)
            {
                default:
                case 0:
                    DIST = value;
                    break;
                case 1:
                    DIST = value / 0.393700787;
                    break;
            }

        }
    }

    public int _thicknessUnit;

    public int ThicknessUnit
    {
        get { return(int) _thicknessUnit;  }
        set
        {
            _thicknessUnit = (eThicknessUnits)value;
            FireDataChanged("MaterialLayers");
        }
    }

    public void FireDataChanged(String identifier)
    {
        if(DataChanged!=null)
        {
            DataChanged(identifier);
        }
    }

}
 

Кто-нибудь может помочь?

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

1. WPF не будет повторно привязывать пользовательский интерфейс, если экземпляр резервных данных такой же, как и раньше. Похоже, вы создаете PropertyChanged для всей коллекции ItemsSource, что ничего не приведет. Кстати, повышение propertychanged в классе, отличном от того, в котором определено фактическое свойство, также ничего не приведет. Опубликуйте соответствующий код для ViewModel и элементов данных.

2. @HighCore Я добавил запрошенный код. PropertyChanged вызывается из ViewModel. Как мне правильно вызвать propertychanged в подпункте? Я пробовал «MaterialThickness», и это не сработало.

Ответ №1:

Вам необходимо реализовать INotifyPropertyChanged в классе, в котором находится изменяемое свойство — в вашем случае XTRRAMaterialLayer, и вызвать событие PropertyChanged при изменении свойства.

Ваши свойства должны выглядеть примерно так:

 public int ThicknessUnit
{
    get { return(int) _thicknessUnit;  }
    set
    {
        _thicknessUnit = (eThicknessUnits)value;
        NotifyPropertyChanged();
    }
}
 

Обработчик события NotifyPropertyChanged:

 public event PropertyChangedEventHandler PropertyChanged;

// This method is called by the Set accessor of each property. 
// The CallerMemberName attribute that is applied to the optional propertyName 
// parameter causes the property name of the caller to be substituted as an argument. 
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
    if (PropertyChanged != null)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}
 

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

1. Спасибо за ответ, который устранил проблему. Я просто не правильно понял, откуда нужно было получить PropertyChanged, но ответ имеет смысл. Единственное изменение вышеизложенного заключается в том, что в моем установщике ThicknessUnit мне также нужно вызвать NotifyPropertyChanged(«MaterialThickness»), чтобы обновить это поле в представлении списка. Еще раз спасибо.