WPF 使用附加属性来绑定ListBox的SelectedItems
ListBox的SelectedItems属性是只读属性,没法直接绑定。
一般的处理方式是在命令中通过命令参数传递到ViewModel里面。
现在通过附加属性来解决这个问题
效果
后台代码
1 using Microsoft.Toolkit.Mvvm.ComponentModel; 2 using Microsoft.Toolkit.Mvvm.Input; 3 using System; 4 using System.Collections; 5 using System.Collections.Generic; 6 using System.Collections.ObjectModel; 7 using System.Linq; 8 using System.Text; 9 using System.Threading.Tasks; 10 using System.Windows; 11 using System.Windows.Controls; 12 using System.Windows.Controls.Primitives; 13 using System.Windows.Input; 14 15 namespace MvvmToolkit学习 16 { 17 public class DataItem 18 { 19 public ObservableCollection<string> Data { get; set; } 20 21 public string Name { get; set; } 22 23 public DataItem() 24 { 25 Data = new ObservableCollection<string>(); 26 Data.Add("Sun"); 27 Data.Add("Moon"); 28 Data.Add("Star"); 29 } 30 } 31 32 public class ListBoxHelper 33 { 34 // Using a DependencyProperty as the backing store for SelectedItems. This enables animation, styling, binding, etc... 35 public static readonly DependencyProperty SelectedItemsProperty = 36 DependencyProperty.RegisterAttached("SelectedItems", 37 typeof(IList), 38 typeof(ListBoxHelper), 39 new FrameworkPropertyMetadata(default(IList), 40 FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, 41 OnSelectedItemsChanged)); 42 43 public static IList GetSelectedItems(DependencyObject obj) 44 { 45 return (IList)obj.GetValue(SelectedItemsProperty); 46 } 47 48 public static void SetSelectedItems(DependencyObject obj, IList value) 49 { 50 obj.SetValue(SelectedItemsProperty, value); 51 } 52 53 private static void OnlistBoxSelectionChanged(object sender, SelectionChangedEventArgs e) 54 { 55 IList dataSource = GetSelectedItems(sender as DependencyObject); 56 //添加用户选中的当前项. 57 foreach (var item in e.AddedItems) 58 { 59 dataSource.Add(item); 60 } 61 //删除用户取消选中的当前项 62 foreach (var item in e.RemovedItems) 63 { 64 dataSource.Remove(item); 65 } 66 } 67 68 private static void OnSelectedItemsChanged(DependencyObject target, DependencyPropertyChangedEventArgs e) 69 { 70 var listBox = target as ListBox; 71 if ((listBox != null) && (listBox.SelectionMode == SelectionMode.Multiple)) 72 { 73 if (e.OldValue != null) 74 { 75 listBox.SelectionChanged -= OnlistBoxSelectionChanged; 76 } 77 IList collection = e.NewValue as IList; 78 listBox.SelectedItems.Clear(); 79 if (collection != null) 80 { 81 foreach (var item in collection) 82 { 83 listBox.SelectedItems.Add(item); 84 } 85 listBox.SelectionChanged += OnlistBoxSelectionChanged; 86 } 87 } 88 } 89 } 90 91 internal class Test1ViewModel : ObservableObject 92 { 93 private RelayCommand addCmd; 94 95 public ICommand AddCmd 96 { 97 get 98 { 99 if (addCmd == null) 100 { 101 addCmd = new RelayCommand(PerformAddCmd); 102 } 103 104 return addCmd; 105 } 106 } 107 108 public ObservableCollection<DataItem> DataItems { get; set; } 109 public List<DataItem> SelectedItems { get; set; } 110 public double Size { get; set; } 111 112 public Test1ViewModel() 113 {
//因为默认值是null所以不会进回调
//给附加属性一个非null值就可以了
//只有附加属性值改变的时候才会触发PropertyChanged,默认属性值是null,VM的值也是null,所以不触发
114 SelectedItems = new List<DataItem>();//此处属性必须赋值,不可为null。。绕了很久的弯路 115 DataItems = new ObservableCollection<DataItem>(); 116 DataItems.Add(new DataItem() { Name = "A" }); 117 DataItems.Add(new DataItem() { Name = "B" }); 118 } 119 120 private void PerformAddCmd() 121 { 122 MessageBox.Show(string.Join("", SelectedItems.Select(s => s.Name))); 123 } 124 } 125 }
xaml
1 <Window 2 x:Class="MvvmToolkit学习.Test1" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 6 xmlns:local="clr-namespace:MvvmToolkit学习" 7 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 8 xmlns:pu="clr-namespace:Panuon.UI.Silver;assembly=Panuon.UI.Silver" 9 Title="Test1" 10 Width="800" 11 Height="450" 12 d:DataContext="{d:DesignInstance Type={x:Type local:Test1ViewModel}}" 13 mc:Ignorable="d"> 14 <Grid> 15 <StackPanel Orientation="Vertical"> 16 <ListBox 17 Width="200" 18 Height="100" 19 local:ListBoxHelper.SelectedItems="{Binding SelectedItems}" 20 ItemsSource="{Binding DataItems}" 21 SelectionMode="Multiple"> 22 <ListBox.ItemTemplate> 23 <DataTemplate DataType="{x:Type local:DataItem}"> 24 <StackPanel Orientation="Horizontal"> 25 <CheckBox IsChecked="{Binding Path=IsSelected, RelativeSource={RelativeSource AncestorType=ListBoxItem, Mode=FindAncestor}}"/> 26 <ContentControl Content="{Binding}"/> 27 </StackPanel> 28 </DataTemplate> 29 </ListBox.ItemTemplate> 30 </ListBox> 31 32 <Button 33 Width="200" 34 Height="35" 35 Command="{Binding AddCmd}"/> 36 <DataGrid pu:DataGridHelper.SelectedItems="{Binding DataItems}"/> 37 </StackPanel> 38 </Grid> 39 </Window>