我刚开始使用MVVM并遇到了障碍,我希望有人可以帮助我。我正在尝试使用2个列表框创建一个简单的视图。第一个列表框中的选择将填充第二个列表框。我创建了一个类,用于存储我想要绑定的信息。
MyObject类(Observable Object只是实现INotifyPopertyChanged的基类)
public class MyObject : ObservableObject
{
String _name = String.Empty;
ObservableCollection<MyObject> _subcategories;
public ObservableCollection<MyObject> SubCategories
{
get { return _subcategories; }
set
{
_subcategories = value;
RaisePropertyChanged("SubCategories");
}
}
public String Name
{
get { return _name; }
set
{
_name = value;
RaisePropertyChanged("Name");
}
}
public MyObject()
{
_subcategories = new ObservableCollection<EMSMenuItem>();
}
}
在我的viewmodel中,我创建了两个ObservableCollections
public ObservableCollection<EMSMenuItem> Level1MenuItems { get; set; }
public ObservableCollection<EMSMenuItem> Level2MenuItems { get; set; }
在ViewModel的构造函数中,我有:
this.Level1MenuItems = new ObservableCollection<EMSMenuItem>();
this.Level2MenuItems = new ObservableCollection<EMSMenuItem>();
this.Level1MenuItems = LoadEMSMenuItems("Sample.Xml");
适用于Level1项目并且它们在视图中正确显示。但是,当用户单击列表框中的项目时,我会调用一个命令,该命令具有以下内容:
Level2MenuItems = ClickedItem.SubCategories;
由于某种原因,这不会更新第二个列表框的UI。如果我在这个位置放置一个断点,我可以看到Level2MenuItems中存储了正确的信息。如果我编写一个foreach循环并将它们单独添加到Level2MenuItems集合中,那么它会正确显示。
同样作为测试,我将以下内容添加到构造函数中:
Level2MenuItems = Level1MenuItems[0].SubCategories;
并且更新正确。
那么为什么代码在构造函数中按预期工作,或者在循环时,而不是在用户单击列表框中的项目时?
答案 0 :(得分:7)
您需要在Level2MenuItems
属性上提出更改通知。
而不是
public ObservableCollection<EMSMenuItem> Level2MenuItems { get; set; }
你需要
private ObservableCollection<EMSMenuItem> _level2MenuItems;
public ObservableCollection<EMSMenuItem> Level2MenuItems
{
get { return _level2MenuItems; }
set
{
_level2MenuItems = value;
RaisePropertyChanged("Level2MenuItems");
}
}
前者在构造函数中工作的原因是Binding尚未发生。但是,由于您通过命令执行更改引用,这在绑定后发生,您需要告诉视图它已更改
答案 1 :(得分:1)
你需要在ObservableCollection中实现你的poco类INotifyPropertyChanged。
示例:
<viewModels:LocationsViewModel x:Key="viewModel" />
.
.
.
<ListView
DataContext="{StaticResource viewModel}"
ItemsSource="{Binding Locations}"
IsItemClickEnabled="True"
ItemClick="GroupSection_ItemClick"
ContinuumNavigationTransitionInfo.ExitElementContainer="True">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" Margin="0,0,10,0" Style="{ThemeResource ListViewItemTextBlockStyle}" />
<TextBlock Text="{Binding Latitude, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Style="{ThemeResource ListViewItemTextBlockStyle}" Margin="0,0,5,0"/>
<TextBlock Text="{Binding Longitude, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Style="{ThemeResource ListViewItemTextBlockStyle}" Margin="5,0,0,0" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
public class LocationViewModel : BaseViewModel
{
ObservableCollection<Location> _locations = new ObservableCollection<Location>();
public ObservableCollection<Location> Locations
{
get
{
return _locations;
}
set
{
if (_locations != value)
{
_locations = value;
OnNotifyPropertyChanged();
}
}
}
}
public class Location : BaseViewModel
{
int _locationId = 0;
public int LocationId
{
get
{
return _locationId;
}
set
{
if (_locationId != value)
{
_locationId = value;
OnNotifyPropertyChanged();
}
}
}
string _name = null;
public string Name
{
get
{
return _name;
}
set
{
if (_name != value)
{
_name = value;
OnNotifyPropertyChanged();
}
}
}
float _latitude = 0;
public float Latitude
{
get
{
return _latitude;
}
set
{
if (_latitude != value)
{
_latitude = value;
OnNotifyPropertyChanged();
}
}
}
float _longitude = 0;
public float Longitude
{
get
{
return _longitude;
}
set
{
if (_longitude != value)
{
_longitude = value;
OnNotifyPropertyChanged();
}
}
}
}
public class BaseViewModel : INotifyPropertyChanged
{
#region Events
public event PropertyChangedEventHandler PropertyChanged;
#endregion
protected void OnNotifyPropertyChanged([CallerMemberName] string memberName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(memberName));
}
}
}
答案 2 :(得分:0)
您的Subcategories
财产should be read-only。