动态将eventtocommand操作添加到列表框 [英] Dynamically add eventtocommand actions to a listbox
问题描述
我的页面上有
命名空间:
xmlns:Custom="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:GalaSoft_MvvmLight_Command="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WP7"
xaml在设计时:
<ListBox x:Name="ItemGroupsList" ItemsSource="{Binding ItemGroups}" Height="496"
SelectedItem="{Binding SelectedItemGroup, Mode=TwoWay}" >
<Custom:Interaction.Triggers>
<Custom:EventTrigger EventName="SelectionChanged">
<GalaSoft_MvvmLight_Command:EventToCommand
x:Name="SelectionChangedEvent"
Command="{Binding GoToEditItemGroupCommand, Mode=OneWay}"
PassEventArgsToCommand="True"/>
</Custom:EventTrigger>
</Custom:Interaction.Triggers>
在代码中,我在运行时生成了多个列表框,并希望能够像上面显示的上述xaml代码一样,绑定到viewmode上的relaycommand.
In the code, I am generating multiple listboxes at run time and would like to be able to bind to a relaycommand on the viewmode like the above xaml code shown above...
如何在运行时在视图背后的代码中执行上述操作.
How do I do the above at run time in the code behind of the view.
此外,我想将动态生成的列表框的数据上下文分配给与当前绑定到我的视图的视图模型不同的视图模型.
Also, I would like to assign the datacontext for the dynamically generated listboxes to different view models than the one currently bound to my view.
基本上,我有一个全景图像,每个全景图像项目都是动态创建的,每个全景图像项目都有一个列表框,该列表框将通过中继命令绑定到视图模型
Basically, I have a panaroma and with each panaroma items being created dynamically with each panaroma item having a listbox that will be bound to a viewmodel with relaycommands
推荐答案
Note: See also related post.
本文介绍了如何从后面的代码中附加行为.
This article describes how you can attach behaviours from code behind.
但是,除非您有令人信服的需求,否则我强烈建议您不要走这条路.如果在ViewModel
中使用此方法,则会失去所有可测试性,因为它会生成与View
高度耦合的ViewModel
,实际上没有它就无法生存. (<旁注:出于这个原因,如果需要,将事件args返回给ViewModel
并使用CommandParameter
代替返回DataContext
也不是一个好习惯.)
However, I firmly would not advise you to go down this route unless you have a compelling need to do so. If you use this approach in your ViewModel
, you will loose all testability as it generates a ViewModel
that is highly coupled to the View
, and in fact cannot live without it. (Side note: for this reason it is also not good practise to return the event args to the ViewModel
use CommandParameter
instead to return the DataContext
if needed).
通常,您可以以其他方式使用MVVM归档目标,其余内容将对此进行介绍.
Normally you can archive your goal using MVVM in another manner, and the rest of the post describes this.
首先,您无需使用Command
即可获取所选属性,也无需获取该属性已更改的通知.通常的模式是将列表框的SelectedItem
绑定到ViewModel
中的属性.现在,您可以使用PropertyChanged
事件来跟踪此属性的更改时间.
First, you do not need to use a Command
to get the selected property, nor to get the notification that this property has changed. The usual pattern for this is that you bind the SelectedItem
of your list box to a property in your ViewModel
. Now you can use the PropertyChanged
event to track when this property changed.
第二,使用模板来生成和设置列表框的样式.当您只需要在选中该项目时才显示它们时,请使用BooleanToVisibility
(示例请参阅转换器,然后使用转换器(而不是我的示例)将子列表框的Visibility
属性绑定到ViewModel
上的属性.
Second, use templates to generate and style your listboxes. When you need to only show them when the item is selected use a BooleanToVisibility
(Sample see here converter and bind the sub-listboxes' Visibility
property to a property on your ViewModel
using the converter (not my the sample).
该示例创建一个列表框,该列表框的ItemsSource
绑定到ViewModel
的Items属性.它还创建一个ContentControl
,该ContentControl
的DataContext
绑定到ViewModel
的SelectedItem
.然后,ContentControl
再次包含ListBox
,其中DataContext
绑定到ItemViewModel
的SubItem
属性. MainViewModel
生成要在设计时和运行时显示的测试数据.
The sample creates a ListBox that has its ItemsSource
bound to the Items property of the ViewModel
. It further creates a ContentControl
that has its DataContext
bound to the SelectedItem
of the ViewModel
. The ContentControl
then contains again a ListBox
where the DataContext
is bound to the SubItem
property of the ItemViewModel
. The MainViewModel
generates test data to be shown at design time and run time.
该帖子的其余部分显示了一个示例:
The rest of the post shows an sample:
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 [名称]
public class ItemViewModel : ViewModelBase { #region [Name]
/// <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
控件的模板
查看全文