2009-04-28 36 views
22

Tôi muốn gắn một Dictionary<string, int> vào một ListView trong WPF. Tôi muốn làm điều này theo cách mà các Values trong Dictionary được cập nhật thông qua cơ chế ràng buộc dữ liệu. Tôi không muốn thay đổi số Keys chỉ bằng Values. Tôi cũng không quan tâm đến việc thêm ánh xạ mới vào Dictionary. Tôi chỉ muốn cập nhật những cái hiện có.Liên kết dữ liệu hai chiều với một từ điển trong WPF

Đặt từ điển là ItemsSource của ListView không thực hiện việc này. Nó không hoạt động vì ListView sử dụng Bộ đếm để truy cập nội dung của Dictionary và các thành phần của điều tra đó là bất biến KeyValuePair đối tượng.

Dòng điều tra hiện tại của tôi cố gắng sử dụng thuộc tính Keys. Tôi chỉ định điều này cho thuộc tính ItemsSource của ListView của tôi. Điều này cho phép tôi hiển thị Keys nhưng tôi không biết đủ về cơ chế dữ liệu của WPF để truy cập vào Values trong Dictionary.

Tôi đã tìm thấy câu hỏi này: Access codebehind variable in XAML nhưng vẫn không thể thu hẹp khoảng cách.

Bạn có biết cách làm cho phương pháp này hoạt động không? Có ai có cách tiếp cận tốt hơn không? Có vẻ như, như một phương sách cuối cùng, tôi có thể xây dựng một đối tượng tùy chỉnh và dán nó vào một số List mà tôi tạo lại/cập nhật Dictionary nhưng điều này có vẻ giống như một cách để phá vỡ chức năng ràng buộc dữ liệu được tích hợp sẵn chứ không phải là sử dụng hiệu quả nó.

+1

Bạn có nên sử dụng cái gì đó như ObservableDictionary với WPF không? – Rauhotz

+0

Làm thế nào bạn sẽ làm cho nó hai chiều? Giá trị của chúng có được hiển thị trong một TextBox không? – Ray

+0

@Ray Tôi có trình chỉnh sửa tùy chỉnh cho Giá trị của mình. Nếu tôi có một ví dụ với một TextBox, tôi chắc chắn rằng tôi có thể làm cho nó đáp ứng nhu cầu của tôi. –

Trả lời

17

Tôi không nghĩ rằng bạn sẽ có thể làm những gì bạn muốn với từ điển.

  1. Bởi vì từ điển không thực hiện INotifyPropertyChanged hoặc INotifyCollectionChanged
  2. Bởi vì nó sẽ không cho phép hai cách ràng buộc như bạn muốn.

Tôi không chắc liệu nó có phù hợp với yêu cầu của bạn chính xác hay không, nhưng tôi sẽ sử dụng ObservableCollection và lớp tùy chỉnh.

Tôi đang sử dụng DataTemplate, để hiển thị cách tạo liên kết trên các giá trị theo hai cách.

Tất nhiên trong việc thực hiện chính này không được sử dụng, vì vậy bạn chỉ có thể sử dụng một ObservableCollection<int>

Custom Class

public class MyCustomClass 
{ 
    public string Key { get; set; } 
    public int Value { get; set; } 
} 

Set ItemsSource

ObservableCollection<MyCustomClass> dict = new ObservableCollection<MyCustomClass>(); 
dict.Add(new MyCustomClass{Key = "test", Value = 1}); 
dict.Add(new MyCustomClass{ Key = "test2", Value = 2 }); 
listView.ItemsSource = dict; 

XAML

<Window.Resources> 
    <DataTemplate x:Key="ValueTemplate"> 
     <TextBox Text="{Binding Value}" /> 
    </DataTemplate> 
</Window.Resources> 
<ListView Name="listView"> 
    <ListView.View> 
     <GridView> 
      <GridViewColumn CellTemplate="{StaticResource ValueTemplate}"/> 
     </GridView> 
    </ListView.View> 
</ListView> 
+1

Tôi thực sự ít lo lắng về việc truyền bá các thay đổi từ từ điển sang ListView hơn là về việc truyền các thay đổi từ ListView sang từ điển. Các giao diện INotify có vẻ như họ đang quan tâm chủ yếu với các cựu. –

+0

Vâng, đúng vậy. Tuy nhiên tôi đã cố gắng để một từ điển ràng buộc chính xác và không thể làm cho nó hoạt động, như bạn không thể. Sẽ xem nếu có ai khác có bất kỳ đề xuất nào. – Ray

+0

MyCustomClass cần triển khai INotifyPropertyChanged – MikeT

6

Đây là một chút hacky, nhưng tôi đã nhận nó để làm việc (giả sử rằng tôi hiểu những gì bạn muốn).

đầu tiên tôi tạo ra một lớp xem mô hình:

class ViewModel : INotifyPropertyChanged 
{ 
    public ViewModel() 
    { 
     this.data.Add(1, "One"); 
     this.data.Add(2, "Two"); 
     this.data.Add(3, "Three"); 
    } 

    Dictionary<int, string> data = new Dictionary<int, string>(); 
    public IDictionary<int, string> Data 
    { 
     get { return this.data; } 
    } 

    private KeyValuePair<int, string>? selectedKey = null; 
    public KeyValuePair<int, string>? SelectedKey 
    { 
     get { return this.selectedKey; } 
     set 
     { 
      this.selectedKey = value; 
      this.OnPropertyChanged("SelectedKey"); 
      this.OnPropertyChanged("SelectedValue"); 
     } 
    } 

    public string SelectedValue 
    { 
     get 
     { 
      if(null == this.SelectedKey) 
      { 
       return string.Empty; 
      } 

      return this.data[this.SelectedKey.Value.Key]; 
     } 
     set 
     { 
      this.data[this.SelectedKey.Value.Key] = value; 
      this.OnPropertyChanged("SelectedValue"); 
     } 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 
    private void OnPropertyChanged(string propName) 
    { 
     var eh = this.PropertyChanged; 
     if(null != eh) 
     { 
      eh(this, new PropertyChangedEventArgs(propName)); 
     } 
    } 
} 

Và sau đó trong XAML:

<Window x:Class="WpfApplication1.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="Window1" Height="300" Width="300"> 
    <Grid> 
     <Grid.RowDefinitions> 
      <RowDefinition /> 
      <RowDefinition Height="Auto" /> 
     </Grid.RowDefinitions> 
     <ListBox x:Name="ItemsListBox" Grid.Row="0" 
      ItemsSource="{Binding Path=Data}" 
      DisplayMemberPath="Key" 
      SelectedItem="{Binding Path=SelectedKey}"> 
     </ListBox> 
     <TextBox Grid.Row="1" 
      Text="{Binding Path=SelectedValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/> 
    </Grid> 
</Window> 

Giá trị của tài sản Data được ràng buộc với ItemsSource của ListBox. Khi bạn nêu trong câu hỏi, điều này dẫn đến việc sử dụng các trường hợp KeyValuePair<int, string> làm dữ liệu đằng sau số ListBox. Tôi đặt DisplayMemberPath thành Key để giá trị của khóa sẽ được sử dụng làm giá trị được hiển thị cho mỗi mục trong ListBox.

Như bạn đã tìm thấy, bạn không thể chỉ sử dụng Value của số KeyValuePair làm dữ liệu cho số TextBox, vì đó là chỉ đọc. Thay vào đó, TextBox được gắn với thuộc tính trên mô hình chế độ xem có thể nhận và đặt giá trị cho khóa hiện được chọn (được cập nhật bằng cách gắn thuộc tính SelectedItem của ListBox cho thuộc tính khác trên mô hình chế độ xem). Tôi phải làm cho thuộc tính này có thể vô hiệu hóa (vì KeyValuePair là một cấu trúc), để mã có thể phát hiện khi không có lựa chọn nào.

Trong ứng dụng thử nghiệm của tôi, điều này dường như dẫn đến việc chỉnh sửa để TextBox truyền đến Dictionary trong mô hình chế độ xem.

Đó có phải là nhiều hay ít những gì bạn đang làm? Nó có vẻ như nó phải là một chút sạch hơn, nhưng tôi không chắc chắn nếu có bất kỳ cách nào để làm như vậy.

+0

tình huống của tôi hơi khác.Tôi muốn hiển thị các từ điển như nhiều cặp Label và Textbox. Bạn còn ý kiến ​​nào không? – YukiSakura

+0

@YukiSakura Tôi sẽ bắt đầu với một ItemsControl được tìm thấy trong từ điển và đặt mẫu mục để có một Nhãn và một TextBox bị ràng buộc vào các thuộc tính thích hợp. – Andy

+0

Tôi đã thử, nhưng có lỗi tại thời gian chạy nói rằng 'không thể sử dụng TwoWay cho một KeyValuePair chỉ đọc ...' – YukiSakura

0

Thay vì

Dictionary<string, int> 

, bạn có thể có một

Dictionary<string, IntWrapperClass>? 

Có IntWrapperClass thực hiện INotifyPropertyChanged. Sau đó, bạn có thể có một Listview/Listbox hai chiều ràng buộc với các mục trong từ điển.

0

Chỉ cần đăng những gì tôi bắt nguồn từ phần trên. Bạn có thể đặt bên dưới vào một số điện thoại ObservableCollection<ObservableKvp<MyKeyObject, MyValueObject>>

Làm cho khóa chỉ đọc vì khóa có thể không được thay đổi. Điều này cần Prism hoạt động - hoặc bạn có thể triển khai phiên bản của riêng mình là INotifyPropertyChanged thay vì

public class ObservableKvp<K, V> : BindableBase 
{ 
    public K Key { get; } 

    private V _value; 
    public V Value 
    { 
     get { return _value; } 
     set { SetProperty(ref _value, value); } 
    } 

    public ObservableKvp(K key, V value) 
    { 
     Key = key; 
     Value = value; 
    } 
} 
Các vấn đề liên quan