簡體   English   中英

使用 WPF 中的字典進行雙向數據綁定

[英]Two Way Data Binding With a Dictionary in WPF

我想將Dictionary<string, int>綁定到 WPF 中的ListView 我想通過數據綁定機制來更新Dictionary中的Values 我不想只更改Keys Values 我也不關心向Dictionary添加新映射。 我只想更新現有的。

將 Dictionary 設置為ListViewItemsSource並不能實現這一點。 它不起作用,因為ListView使用 Enumerator 來訪問Dictionary的內容,並且該枚舉的元素是不可變的KeyValuePair對象。

我目前的調查嘗試使用Keys屬性。 我將此分配給我的ListViewItemsSource屬性。 這確實允許我顯示Keys但我對 WPF 的數據綁定機制了解得不夠多,無法訪問DictionaryValues

我發現了這個問題: 在 XAML 中訪問代碼隱藏變量,但似乎仍然無法彌合差距。

你們中有人知道如何使這種方法起作用嗎? 有沒有人有更好的方法?

看起來,作為最后的手段,我可​​以構建一個自定義對象並將其粘貼在一個List ,從中我重新創建/更新我的Dictionary但這似乎是一種繞過內置數據綁定功能而不是有效利用它的方法。

我不認為你能用字典做你想做的事。

  1. 因為字典沒有實現 INotifyPropertyChanged 或 INotifyCollectionChanged
  2. 因為它不會像你想要的那樣允許雙向綁定。

我不確定它是否完全符合您的要求,但我會使用ObservableCollection和自定義類。

我正在使用 DataTemplate,以展示如何以兩種方式對值進行綁定。

當然在這個實現中沒有使用 Key,所以你可以只使用ObservableCollection<int>

自定義類

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

設置項目來源

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>

這有點hacky,但我讓它工作了(假設我明白你想要什么)。

我首先創建了一個視圖模型類:

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));
        }
    }
}

然后在 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>

Data屬性的值綁定到ListBoxItemsSource 正如您在問題中所述,這會導致使用KeyValuePair<int, string>實例作為ListBox背后的數據。 我將DisplayMemberPath設置為Key以便鍵的值將用作ListBox每個項目的顯示值。

正如您所發現的,您不能只使用KeyValuePairValue作為TextBox的數據,因為它是只讀的。 相反, TextBox綁定到視圖模型上的一個屬性,該屬性可以獲取和設置當前選定鍵的值(通過將ListBoxSelectedItem屬性綁定到視圖模型上的另一個屬性來更新)。 我必須將此屬性設為可空(因為KeyValuePair是一個結構體),以便代碼可以檢測到何時沒有選擇。

在我的測試應用程序中,這似乎導致對TextBox編輯傳播到視圖模型中的Dictionary

這或多或少是你想要的嗎? 看起來它應該更干凈一些,但我不確定是否有任何方法可以這樣做。

只是發布我從上面得出的內容。 您可以將以下內容放入ObservableCollection<ObservableKvp<MyKeyObject, MyValueObject>>

將密鑰設為只讀,因為它們的密鑰可能不應更改。 這需要 Prism 才能工作 - 或者您可以實現自己的INotifyPropertyChanged版本

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;
    }
}

代替

Dictionary<string, int>

,你能有一個

Dictionary<string, IntWrapperClass>?

讓 IntWrapperClass 實現 INotifyPropertyCHanged。 然后你可以有一個 Listview/Listbox 雙向綁定到字典中的項目。

我設法在我的項目中使用由 dr.wpf 在這里實現的可觀察字典並使用作為值而不是字符串而是對象 []。 在 xaml 中,我得到了類似的信息:

<ListView ItemsSource="{Binding myObservableDictionary}" >
                            <ListView.View>
                                <GridView>
                                    <GridViewColumn DisplayMemberBinding="{Binding Key}"/>
                                    <GridViewColumn>
                                        <GridViewColumn.CellTemplate>
                                            <DataTemplate>
                                                <TextBox Text="{Binding Value[0], Mode=TwoWay}" AllowDrop="True" Drop="TextBox_Drop"></TextBox>
                                            </DataTemplate>
                                        </GridViewColumn.CellTemplate>
                                    </GridViewColumn>
                                </GridView>
                            </ListView.View>
                        </ListView> 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM