开发者

Notify Property Changed on a Dictionary

开发者 https://www.devze.com 2023-02-09 17:02 出处:网络
I have a WPF / XAML form data-bound to a property in a dictionary, similar to this: <TextBox Text=\"{Binding Path=Seat[2B].Name}\">

I have a WPF / XAML form data-bound to a property in a dictionary, similar to this:

<TextBox Text="{Binding Path=Seat[2B].Name}">

Seat is exposed as a property IDiction开发者_JAVA技巧ary<String, Reservation> on the airplane object.

class airplane
{
    private IDictionary<String, Reservation> seats;
    public IDictionary<String, Reservation> Seat
    {
        get { return seats; }
        // set is not allowed
    }
}

From within the code of my Window, the value of seat 2B is sometimes changed, and after that, I want to notify the UI that the property has changed.

class MyWindow : Window
{
    private void AddReservation_Click(object sender, EventArgs e)
    {
        airplane.Seat["2B"] = new Reservation();
        // I want to override the assignment operator (=)
        // of the Seat-dictionary, so that the airplane will call OnNotifyPropertyChanged.
    }
}

I've looked to see if the Dictionary is IObservable, so that I could observe changes to it, but it doesn't seem to be.

Is there any good way to "catch" changes to the dictionary in the airplane-class so that I can NotifyPropertyChanged.


Dr. WPF has created an ObservableDictionary at this link: http://drwpf.com/blog/2007/09/16/can-i-bind-my-itemscontrol-to-a-dictionary/

Update: The comment made by Dr. WPF in the following link says that he has fixed this problem himself so the following change should no longer be required

Also, an addition was made at this link: http://10rem.net/blog/2010/03/08/binding-to-a-dictionary-in-wpf-and-silverlight

The small change was

// old version
public TValue this[TKey key]
{
    get { return (TValue)_keyedEntryCollection[key].Value; }
    set { DoSetEntry(key, value);}
}

// new version
public TValue this[TKey key]
{
    get { return (TValue)_keyedEntryCollection[key].Value; }
    set
    {
        DoSetEntry(key, value);
        OnPropertyChanged(Binding.IndexerName);
    }
}


You can just create a class that implement INotifyPropertyChanged interface, wrap your value by this class and use it in Dictionary. I had a similar problem and did next:

class Parameter : INotifyPropertyChanged //wrapper
{
    private string _Value;
    public string Value //real value
    {
        get { return _Value; }
        set { _Value = value; RaisePropertyChanged("Value"); } 
    }

    public Parameter(string value)
    {
        Value = value;
    }

    public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
    public void RaisePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
public class ViewModel
{
    public Dictionary<string, Parameter> Parameters
}
<ItemsControl ItemsSource="{Binding Path=Parameters, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Margin="3" >
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
                <Label Grid.Column="0" Content="{Binding Path=Key}" Margin="3" />
                <TextBox Grid.Column="1" Text="{Binding Path=Value.Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsReadOnly="True" Margin="3" />
            </Grid>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

After that, if you change values in the dictionary, you will see the changes in UI.


You need to have a clear idea of what you're trying to accomplish before you start implementing change notification. If you want the UI to be updated when an object stored in the dictionary with a given key changes, that's one thing. If you want the UI to be updated when a property of the object stored in the dictionary changes, that's another thing entirely.

In other words, if you want the UI to update when Reservation.Name changes, you need the Reservation object to perform change notification. If you want the UI to update when Seat[2B] is set to a different Reservation, then the dictionary will need to perform change notification.


One way to get around this would probably be to encapsulate a dictionary, then you can implement notifying interfaces and control the access to the dictionary, i.e. if someone uses the brackets to set a value you can set the value of the internal dictionary and raise the notification.


You could always derive from Dictionary (or IDictionary) to produce an ObservableDictionary:

public class ObservableDictionary<TKey, TVal>:IDictionary<TKey, TVal>, IObservable
{
   private Dictionary<TKey, TVal> _data;

   //Implement IDictionary, using _data for storage and raising NotifyPropertyChanged
}

The biggest problem you'll likely encounter is that you won't be able to directly detect a change to a value; only adding and removing KVPs. To do that, change _data to a List<ObservableKeyValuePair>, implement IObservable there as well, and attach a handler to every new element you create or receive that will respond to the KVP's NotifyPropertyChanged and raise your parent class's NotifyValueChanged.

0

精彩评论

暂无评论...
验证码 换一张
取 消