2009-04-29 39 views
55

Trong hành trình tìm hiểu MVVM, tôi đã thiết lập một số hiểu biết cơ bản về WPF và mẫu ViewModel. Tôi đang sử dụng trừu tượng sau đây khi cung cấp một danh sách và quan tâm đến một mục được chọn duy nhất.Quản lý nhiều lựa chọn với MVVM

public ObservableCollection<OrderViewModel> Orders { get; private set; } 
public ICollectionView OrdersView 
{ 
    get 
    { 
     if(_ordersView == null) 
      _ordersView = CollectionViewSource.GetDefaultView(Orders); 
     return _ordersView; 
    } 
} 
private ICollectionView _ordersView; 

public OrderViewModel CurrentOrder 
{ 
    get { return OrdersView.CurrentItem as OrderViewModel; } 
    set { OrdersView.MoveCurrentTo(value); } 
} 

Sau đó tôi có thể ràng buộc OrdersView cùng với hỗ trợ sắp xếp và lọc vào một danh sách trong WPF:

<ListView ItemsSource="{Binding Path=OrdersView}" 
      IsSynchronizedWithCurrentItem="True"> 

này hoạt động thực sự tốt cho quan điểm lựa chọn duy nhất. Nhưng tôi cũng muốn hỗ trợ nhiều lựa chọn trong chế độ xem và có mô hình liên kết với danh sách các mục đã chọn.

Làm cách nào để liên kết ListView.SelectedItems với thuộc tính người quay lại trên ViewModel?

Trả lời

92

Thêm một tài sản IsSelected cho con quý vị ViewModel (OrderViewModel trong trường hợp của bạn):

public bool IsSelected { get; set; } 

Bind tài sản được lựa chọn trên thùng sơn này (cho ListBox trong trường hợp này):

<ListBox.ItemContainerStyle> 
    <Style TargetType="{x:Type ListBoxItem}"> 
     <Setter Property="IsSelected" Value="{Binding Mode=TwoWay, Path=IsSelected}"/> 
    </Style> 
</ListBox.ItemContainerStyle> 

IsSelected được cập nhật để khớp với trường tương ứng trên vùng chứa.

Bạn có thể có được những đứa trẻ được lựa chọn trong mô hình xem bằng cách làm như sau:

public IEnumerable<OrderViewModel> SelectedOrders 
{ 
    get { return Orders.Where(o => o.IsSelected); } 
} 
+29

Xin lưu ý rằng giải pháp này không hoạt động khi sử dụng VirtualizingStackPanel trong ListBox (đây là mặc định). Thông tin thêm tại bài đăng này: http://stackoverflow.com/questions/1273659/virtualizingstackpanel-mvvm-multiple-selection – decasteljau

+3

Bắt tốt. Cảm ơn các cập nhật. Đối với lựa chọn duy nhất, giải pháp tốt nhất là ICollectionView. Microsoft cần tạo một giao diện ICollectionView hỗ trợ đa lựa chọn. –

+0

Bạn vừa mới cứu mạng tôi! – jpsstavares

1

Nếu bạn đang sử dụng MVVM-LIGHT bạn có thể sử dụng mô hình này:

http://blog.galasoft.ch/archive/2010/05/19/handling-datagrid.selecteditems-in-an-mvvm-friendly-manner.aspx

Không đặc biệt thanh lịch nhưng có vẻ như đáng tin cậy ít nhất là

+0

hmm nó thực sự chỉ đi một chiều với phương pháp này (xem> xem mô hình). không có khả năng xem mô hình> xem như mô tả ở đây –

+2

Trong khi điều này về lý thuyết có thể trả lời câu hỏi, [nó sẽ là thích hợp hơn] (http://meta.stackexchange.com/q/8259) để bao gồm các phần thiết yếu của câu trả lời ở đây, và cung cấp liên kết để tham khảo. – Kev

4

Người ta có thể thử tạo thuộc tính đính kèm.

Làm như vậy sẽ tiết kiệm một từ việc thêm thuộc tính IsSelected cho mỗi danh sách bạn ràng buộc. Tôi đã thực hiện nó cho ListBox, nhưng nó có thể được sửa đổi để sử dụng trong chế độ xem danh sách. Thông tin

<ListBox SelectionMode="Multiple" 
     local:ListBoxMultipleSelection.SelectedItems="{Binding SelectedItems}" > 

thêm: WPF – Binding ListBox SelectedItems – Attached Property VS Style.

+1

Tôi thích giải pháp của bạn rất nhiều, tuy nhiên tôi đã phải sửa đổi mã của bạn để nó không cache hộp danh sách trong thuộc tính đính kèm. Caching hộp danh sách ngăn bạn sử dụng thuộc tính đính kèm trên bất kỳ hộp danh sách nào khác. – Darkhydro

11

tôi có thể đảm bảo với bạn: SelectedItems là thực sự bindable như một XAML CommandParameter

Có một giải pháp đơn giản cho vấn đề chung này; để làm cho nó làm việc bạn phải làm theo ALL các quy tắc sau:

  1. Sau Ed Ball's suggestion, trên lệnh databinding XAML của bạn, xác định các thuộc tính CommandParameterTRƯỚC các Command thuộc tính. Đây là lỗi rất tốn thời gian.

    enter image description here

  2. Hãy chắc chắn rằng bạn ICommand 's CanExecuteExecute phương pháp này có một tham số kiểu object. Bằng cách này bạn có thể ngăn im lặng ngoại lệ cast xảy ra bất cứ khi nào của databinding loại CommandParameter không phù hợp với loại tham số Command phương pháp của bạn:

    private bool OnDeleteSelectedItemsCanExecute(object SelectedItems) 
    { 
        // Your code goes here 
    } 
    
    private bool OnDeleteSelectedItemsExecute(object SelectedItems) 
    { 
        // Your code goes here 
    } 
    

Ví dụ, bạn có thể gửi một ListView/ListBox 's SelectedItems đối với các phương pháp ICommand hoặc chính mình là ListView/ListBox. Tuyệt, phải không?

Tôi hy vọng điều này ngăn người khác dành nhiều thời gian tôi đã tìm ra cách nhận được SelectedItems làm thông số CanExecute.

+2

Tôi vừa thử với DataGrid, sử dụng thuộc tính SelectedItems theo cách này tôi thấy rằng CanExecute đã được gọi với giá trị _previous_ nếu SelectedItems (tức là nó không phản ánh trạng thái hiện tại của vùng chọn). Có thể khác với các điều khiển khác, nhưng tỏ ra vô dụng trong trường hợp này. –

+1

Làm việc tốt cho tôi (đối với cả ListBox và DataGrid, sử dụng .Net 4.0). Tôi đã có Command và CommandParameter theo thứ tự ngược lại với những gì đã được chỉ định và nó vẫn hoạt động. – BCA

+2

đẹp, hoàn hảo cho listview trên 4.5.2. Thêm gì là tôi cũng đã sử dụng DelegateCommand từ Prism cho Execute và CanExecute.so: 'ICommand btnCommand = new DelegateCommand (Thực thi, CanExecute); ' – CularBytes

Các vấn đề liên quan