Примечание: см. Также соответствующий пост .
В этой статье описывается, как можно прикреплять поведения из кода позади.
Однако , я твердо не советовал бы вам идти по этому пути, если у вас нет настоятельной необходимости .Если вы используете этот подход в своем ViewModel
, вы потеряете всю тестируемость, поскольку он генерирует ViewModel
, который тесно связан с View
и фактически не может жить без него.( Примечание: , по этой причине также не рекомендуется возвращать аргументы событий для ViewModel
, используйте CommandParameter
вместо того, чтобы возвращать DataContext
, если необходимо).
Обычно вы можете заархивировать свою цель с помощью MVVM другим способом, и остальная часть поста описывает это.
Во-первых, вам не нужно использовать Command
, чтобы получить выбранное свойство или получить уведомлениечто это свойство изменилось.Обычный пример для этого - привязка SelectedItem
вашего списка к свойству в ViewModel
.Теперь вы можете использовать событие PropertyChanged
, чтобы отслеживать изменение этого свойства.
Во-вторых, используйте шаблоны для создания и оформления ваших списков.Если вам нужно показать их только при выборе элемента, используйте конвертер BooleanToVisibility
(, см. Здесь и свяжите свойство Visibility
вложенных списков со свойством на ViewModel
, используя конвертер.(не мой образец).
Образец создает ListBox, у которого есть ItemsSource
, связанный со свойством Items ViewModel
. Кроме того, он создает ContentControl
, который имеет DataContext
, связанный сSelectedItem
из ViewModel
. ContentControl
затем снова содержит ListBox
, где DataContext
связан со свойством SubItem
ItemViewModel
. MainViewModel
генерирует тестовые данные, которые будут показаныво время разработки и выполнения.
В остальной части поста показан пример:
1. XAML главной страницы (кроме):
<ContentControl x:Name="target1" Grid.Row="1" DataContext="{Binding SelectedItem}" Margin="20,0">
<ContentControl.Template>
<ControlTemplate>
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Name:" Margin="0,0,10,0"/>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
<ListBox ItemsSource="{Binding SubItems}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Padding" Value="1"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Border Background="navy" BorderBrush="White" BorderThickness="1">
<TextBlock Text="{Binding Name}"/>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</ControlTemplate>
</ContentControl.Template>
</ContentControl>
</Grid>
</Grid>
2. Модель основного вида
public MainViewModel()
{
// working with fields to ensure no events are fired during initial phase
this._items = new ObservableCollection<ItemViewModel>();
for (int i = 0; i < 5; ++i) {
var item = new ItemViewModel() { Name = string.Format("Item {0}", i) };
for (int j = 0; j < 3; ++j)
item.SubItems.Add(new ItemViewModel() { Name = string.Format("{0} - Sub Item {1}", item.Name, j) });
this._items.Add(item);
}
this.SelectedItem = this._items[0];
if (IsInDesignMode) {
// Code runs in Blend --> create design time data.
} else {
// Code runs "for real"
}
}
#region [Items]
/// <summary>
/// The <see cref="Items" /> property's name.
/// </summary>
public const string ItemsPropertyName = "Items";
private ObservableCollection<ItemViewModel> _items = default(ObservableCollection<ItemViewModel>);
/// <summary>
/// Gets the Items property.
/// TODO Update documentation:
/// Changes to that property's value raise the PropertyChanged event.
/// This property's value is broadcasted by the Messenger's default instance when it changes.
/// </summary>
public ObservableCollection<ItemViewModel> Items {
get {
return _items;
}
set {
if (_items == value) {
return;
}
var oldValue = _items;
_items = value;
// Update bindings, no broadcast
RaisePropertyChanged(ItemsPropertyName);
// Update bindings and broadcast change using GalaSoft.MvvmLight.Messenging
//RaisePropertyChanged(ItemsPropertyName, oldValue, value, true);
}
}
#endregion
#region [SelectedItem]
/// <summary>
/// The <see cref="SelectedItem" /> property's name.
/// </summary>
public const string SelectedItemPropertyName = "SelectedItem";
private ItemViewModel _selectedItem = default(ItemViewModel);
/// <summary>
/// Gets the SelectedItem property.
/// TODO Update documentation:
/// Changes to that property's value raise the PropertyChanged event.
/// This property's value is broadcasted by the Messenger's default instance when it changes.
/// </summary>
public ItemViewModel SelectedItem {
get {
return _selectedItem;
}
set {
if (_selectedItem == value) {
return;
}
var oldValue = _selectedItem;
_selectedItem = value;
// Update bindings, no broadcast
RaisePropertyChanged(SelectedItemPropertyName);
// Update bindings and broadcast change using GalaSoft.MvvmLight.Messenging
//RaisePropertyChanged(SelectedItemPropertyName, oldValue, value, true);
}
}
#endregion
}
3. Модель ItemView
открытый класс ItemViewModel: ViewModelBase {#region [Имя]
/// <summary>
/// The <see cref="Name" /> property's name.
/// </summary>
public const string NamePropertyName = "Name";
private string _name = default(string);
/// <summary>
/// Gets the Name property.
/// TODO Update documentation:
/// Changes to that property's value raise the PropertyChanged event.
/// This property's value is broadcasted by the Messenger's default instance when it changes.
/// </summary>
public string Name {
get {
return _name;
}
set {
if (_name == value) {
return;
}
var oldValue = _name;
_name = value;
// Update bindings, no broadcast
RaisePropertyChanged(NamePropertyName);
// Update bindings and broadcast change using GalaSoft.MvvmLight.Messenging
//RaisePropertyChanged(NamePropertyName, oldValue, value, true);
}
}
#endregion
#region [SubItems]
/// <summary>
/// The <see cref="SubItems" /> property's name.
/// </summary>
public const string SubItemsPropertyName = "SubItems";
private ObservableCollection<ItemViewModel> _myProperty = new ObservableCollection<ItemViewModel>();
/// <summary>
/// Gets the SubItems property.
/// TODO Update documentation:
/// Changes to that property's value raise the PropertyChanged event.
/// This property's value is broadcasted by the Messenger's default instance when it changes.
/// </summary>
public ObservableCollection<ItemViewModel> SubItems {
get {
return _myProperty;
}
set {
if (_myProperty == value) {
return;
}
var oldValue = _myProperty;
_myProperty = value;
// Update bindings, no broadcast
RaisePropertyChanged(SubItemsPropertyName);
// Update bindings and broadcast change using GalaSoft.MvvmLight.Messenging
//RaisePropertyChanged(SubItemsPropertyName, oldValue, value, true);
}
}
#endregion
}
Редактировать 2
Шаблоны элемента управления Panorama
можно найти здесь .