#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»), чтобы обновить это поле в представлении списка. Еще раз спасибо.