我有一个SL3应用程序多选列表框使用棱镜,我需要一个集合在我的视图模型包含在列表框中当前选定的项目。
该视图模型不知道该视图的任何所以它没有访问列表框控件。 此外,我需要能够清除从视图模型列表框中选择的项目。
不知道如何解决这个问题
感谢迈克尔
我有一个SL3应用程序多选列表框使用棱镜,我需要一个集合在我的视图模型包含在列表框中当前选定的项目。
该视图模型不知道该视图的任何所以它没有访问列表框控件。 此外,我需要能够清除从视图模型列表框中选择的项目。
不知道如何解决这个问题
感谢迈克尔
因此,假设你有以下属性的视图模型:
public ObservableCollection<string> AllItems { get; private set; }
public ObservableCollection<string> SelectedItems { get; private set; }
你会被你的AllItems收集绑定到ListBox开始:
<ListBox x:Name="MyListBox" ItemsSource="{Binding AllItems}" SelectionMode="Multiple" />
问题是,在列表框selectedItems属性不是一个DependencyProperty。 这是非常糟糕的,因为你不能把它绑定到你的视图模型的东西。
第一种方法是只把这个逻辑的代码隐藏,来调整视图模型:
public MainPage()
{
InitializeComponent();
MyListBox.SelectionChanged += ListBoxSelectionChanged;
}
private static void ListBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var listBox = sender as ListBox;
if(listBox == null) return;
var viewModel = listBox.DataContext as MainVM;
if(viewModel == null) return;
viewModel.SelectedItems.Clear();
foreach (string item in listBox.SelectedItems)
{
viewModel.SelectedItems.Add(item);
}
}
这种方法将工作,但它实在是太丑了。 我首选的方法是提取这种行为为“附加的行为”。 如果你这样做,你可以完全消除您的代码隐藏,并在XAML设置。 奖金是,这个“附加的行为”,现在是可以重复使用的任何列表框:
<ListBox ItemsSource="{Binding AllItems}" Demo:SelectedItems.Items="{Binding SelectedItems}" SelectionMode="Multiple" />
这里是为附加的行为的代码:
public static class SelectedItems
{
private static readonly DependencyProperty SelectedItemsBehaviorProperty =
DependencyProperty.RegisterAttached(
"SelectedItemsBehavior",
typeof(SelectedItemsBehavior),
typeof(ListBox),
null);
public static readonly DependencyProperty ItemsProperty = DependencyProperty.RegisterAttached(
"Items",
typeof(IList),
typeof(SelectedItems),
new PropertyMetadata(null, ItemsPropertyChanged));
public static void SetItems(ListBox listBox, IList list) { listBox.SetValue(ItemsProperty, list); }
public static IList GetItems(ListBox listBox) { return listBox.GetValue(ItemsProperty) as IList; }
private static void ItemsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var target = d as ListBox;
if (target != null)
{
GetOrCreateBehavior(target, e.NewValue as IList);
}
}
private static SelectedItemsBehavior GetOrCreateBehavior(ListBox target, IList list)
{
var behavior = target.GetValue(SelectedItemsBehaviorProperty) as SelectedItemsBehavior;
if (behavior == null)
{
behavior = new SelectedItemsBehavior(target, list);
target.SetValue(SelectedItemsBehaviorProperty, behavior);
}
return behavior;
}
}
public class SelectedItemsBehavior
{
private readonly ListBox _listBox;
private readonly IList _boundList;
public SelectedItemsBehavior(ListBox listBox, IList boundList)
{
_boundList = boundList;
_listBox = listBox;
_listBox.SelectionChanged += OnSelectionChanged;
}
private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
_boundList.Clear();
foreach (var item in _listBox.SelectedItems)
{
_boundList.Add(item);
}
}
}
我想有真正的双向绑定,以便列表框选择反映包含底层视图模型的SelectedItems集合中的项目。 这使我控制由逻辑视图模型层的选择。
这里是我的修改到SelectedItemsBehavior类。 如果视图模型属性实现INotifyCollectionChanged(例如,由的ObservableCollection <T>类型实现)它们同步ListBox.SelectedItems收集与底层视图模型属性。
public static class SelectedItems
{
private static readonly DependencyProperty SelectedItemsBehaviorProperty =
DependencyProperty.RegisterAttached(
"SelectedItemsBehavior",
typeof(SelectedItemsBehavior),
typeof(ListBox),
null);
public static readonly DependencyProperty ItemsProperty = DependencyProperty.RegisterAttached(
"Items",
typeof(IList),
typeof(SelectedItems),
new PropertyMetadata(null, ItemsPropertyChanged));
public static void SetItems(ListBox listBox, IList list) { listBox.SetValue(ItemsProperty, list); }
public static IList GetItems(ListBox listBox) { return listBox.GetValue(ItemsProperty) as IList; }
private static void ItemsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var target = d as ListBox;
if (target != null)
{
AttachBehavior(target, e.NewValue as IList);
}
}
private static void AttachBehavior(ListBox target, IList list)
{
var behavior = target.GetValue(SelectedItemsBehaviorProperty) as SelectedItemsBehavior;
if (behavior == null)
{
behavior = new SelectedItemsBehavior(target, list);
target.SetValue(SelectedItemsBehaviorProperty, behavior);
}
}
}
public class SelectedItemsBehavior
{
private readonly ListBox _listBox;
private readonly IList _boundList;
public SelectedItemsBehavior(ListBox listBox, IList boundList)
{
_boundList = boundList;
_listBox = listBox;
_listBox.Loaded += OnLoaded;
_listBox.DataContextChanged += OnDataContextChanged;
_listBox.SelectionChanged += OnSelectionChanged;
// Try to attach to INotifyCollectionChanged.CollectionChanged event.
var notifyCollectionChanged = boundList as INotifyCollectionChanged;
if (notifyCollectionChanged != null)
{
notifyCollectionChanged.CollectionChanged += OnCollectionChanged;
}
}
void UpdateListBoxSelection()
{
// Temporarily detach from ListBox.SelectionChanged event
_listBox.SelectionChanged -= OnSelectionChanged;
// Synchronize selected ListBox items with bound list
_listBox.SelectedItems.Clear();
foreach (var item in _boundList)
{
// References in _boundList might not be the same as in _listBox.Items
var i = _listBox.Items.IndexOf(item);
if (i >= 0)
{
_listBox.SelectedItems.Add(_listBox.Items[i]);
}
}
// Re-attach to ListBox.SelectionChanged event
_listBox.SelectionChanged += OnSelectionChanged;
}
void OnLoaded(object sender, RoutedEventArgs e)
{
// Init ListBox selection
UpdateListBoxSelection();
}
void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
// Update ListBox selection
UpdateListBoxSelection();
}
void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
// Update ListBox selection
UpdateListBoxSelection();
}
void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
// Temporarily deattach from INotifyCollectionChanged.CollectionChanged event.
var notifyCollectionChanged = _boundList as INotifyCollectionChanged;
if (notifyCollectionChanged != null)
{
notifyCollectionChanged.CollectionChanged -= OnCollectionChanged;
}
// Synchronize bound list with selected ListBox items
_boundList.Clear();
foreach (var item in _listBox.SelectedItems)
{
_boundList.Add(item);
}
// Re-attach to INotifyCollectionChanged.CollectionChanged event.
if (notifyCollectionChanged != null)
{
notifyCollectionChanged.CollectionChanged += OnCollectionChanged;
}
}
}
感谢这个! 我添加了一个小更新,以支持初始加载和DataContext的变化。
干杯,
亚历山德罗飞行员[MVP / US]
public class SelectedItemsBehavior
{
private readonly ListBox _listBox;
private readonly IList _boundList;
public ListBoxSelectedItemsBehavior(ListBox listBox, IList boundList)
{
_boundList = boundList;
_listBox = listBox;
SetSelectedItems();
_listBox.SelectionChanged += OnSelectionChanged;
_listBox.DataContextChanged += ODataContextChanged;
}
private void SetSelectedItems()
{
_listBox.SelectedItems.Clear();
foreach (object item in _boundList)
{
// References in _boundList might not be the same as in _listBox.Items
int i = _listBox.Items.IndexOf(item);
if (i >= 0)
_listBox.SelectedItems.Add(_listBox.Items[i]);
}
}
private void ODataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
SetSelectedItems();
}
private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
_boundList.Clear();
foreach (var item in _listBox.SelectedItems)
{
_boundList.Add(item);
}
}
}
更新现有的行为与选择上的收集和改变的Rebinded项目
http://rnragu.blogspot.com/2011/04/multiselect-listbox-in-silverlight-use.html
如果你还记得先创建观察集合的一个实例的原始解决上述作品! 此外,您还需要确保观察集合内容类型的内容类型匹配您的ListBox的ItemSource(如果你是从上面提到的确切例子偏离)。
下面是与此问题的解决方案,其中包括一个示例应用程序,一个博客,所以你可以看到究竟是如何使其工作: http://alexshed.spaces.live.com/blog/cns!71C72270309CE838!149.entry
我只是在我的应用程序中实现这一点,它很好地解决了这个问题
对我来说,解决办法是亚历山德罗皮洛蒂更新与布赖恩Genisio附加的行为结合起来。 但是,删除代码为DataContext的改变Silverlight 4中不支持这一点。
如果您在列表框绑定到ObservableCollection<string>
上述工作正常,但如果你绑定到像复杂对象ObservableCollection<Person> SelectedItems { get; private set; }
ObservableCollection<Person> SelectedItems { get; private set; }
ObservableCollection<Person> SelectedItems { get; private set; }
通过一个DataTemplate它似乎并没有工作。 这是由于equals方法集合使用的默认实现。 你可以告诉哪些字段确定的对象相等时比较您的Person对象解决这个问题,这是由implementating接口进行IEquatable<T>
的对象。
在此之后,的IndexOf(项目)的代码将工作和能够比较如果对象是相等的,并选择项目列表
// References in _boundList might not be the same as in _listBox.Items
int i = _listBox.Items.IndexOf(item);
if (i >= 0)
_listBox.SelectedItems.Add(_listBox.Items[i]);
见链接: http://msdn.microsoft.com/en-us/library/ms131190(VS.95).aspx
即时通讯使用在XAML选择改变事件EventToCommand对象和通过那里列表框作为参数。 比MMVM命令是选择的项目管理的ObservableCollection。 它的方便,快捷;)
由Brian Genisio和塞缪尔·杰克的解决方案在这里是巨大的。 我已经成功地实现了它。 但是,我也有这个地方没有工作的情况下,自,我与WPF或.Net的专家,我无法调试。 我仍然不能确定是什么问题了,但在适当的时候,我发现了一个解决方法多选结合。 而在这个解决方案,我没有得到到DataContext。
该解决方案是人谁也不能作出上述2个解决方案的工作。 我想这个解决方案将不被视为MVVM。 它是这样的。 假设你在视图模型2分集:
public ObservableCollection<string> AllItems { get; private set; }
public ObservableCollection<string> SelectedItems { get; private set; }
你需要一个列表框:
<ListBox x:Name="MyListBox" ItemsSource="{Binding AllItems}" SelectionMode="Multiple" />
现在添加一个列表框,并将其绑定到SelectedItems和设置可见:
<ListBox x:Name="MySelectedItemsListBox" ItemsSource="{Binding SelectedItems, Mode=OneWayToSource}" SelectionMode="Multiple" Visibility="Collapsed" />
现在,在WPF页面背后的代码,添加到构造后InitializeComponent()方法:
MyListBox.SelectionChanged += MyListBox_SelectionChanged;
并添加了一个方法:
private void MyListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
MySelectedItemsListBox.ItemsSource = MyListBox.SelectedItems;
}
和你做。 这将肯定工作。 我想这可以在Silverlight也可以使用,如果上述方案不能正常工作。
对于那些谁仍然不能让candritzky答案工作,确保你没有修改Windows的主题颜色和我一样。 事实证明我的列表框的背景颜色选择匹配的颜色,当列表框是失焦,从而使它看起来像被什么也没有选择。
更改列表框的背景刷红检查,如果这是发生了什么事给你。 这让我花2个小时,直到我意识到......