Por omissão não está incluído nenhum dicionário observável no WPF, mas seria bastante útil ter um para usar numa DataGrid editável. Neste artigo vamos fazer um pouco de “batota” e dar uso à ObservableCollection para criar um novo tipo parecido ao de um dicionário observável.
Não podem usar directamente um dicionário numa DataGrid editável, porque os KeyValuePairs são apenas de leitura e não implementam a interface INotifyPropertyChanged.
Mas ao criar uma nova classe para pares e herdando a ObservableCollection podemos imitar o comportamento de um dicionário observável.
Uma classe par observável
Começamos pela classe que vai guardar o par chave/valor:
public class Pair<TKey, TValue> : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected TKey _key;
protected TValue _value;
public TKey Key
{
get { return _key; }
set
{
if (
(_key == null && value != null)
|| (_key != null && value == null)
|| !_key.Equals(value))
{
_key = value;
NotifyPropertyChanged("Key");
}
}
}
public TValue Value
{
get { return _value; }
set
{
if (
(_value == null && value != null)
|| (_value != null && value == null)
|| (_value != null && !_value.Equals(value)))
{
_value = value;
NotifyPropertyChanged("Value");
}
}
}
}
Incluímos construtores e métodos:
public class Pair<TKey, TValue> : INotifyPropertyChanged
{
public Pair()
{
}
public Pair(TKey key, TValue value)
{
Key = key;
Value = value;
}
public Pair(KeyValuePair<TKey, TValue> kv)
{
Key = kv.Key;
Value = kv.Value;
}
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Colecção de pares observável
Podemos agora criar um novo tipo de colecção observável semelhante a um dicionário:
public class ObservablePairCollection<TKey, TValue> : ObservableCollection<Pair<TKey, TValue>>
{
public ObservablePairCollection()
: base()
{
}
public ObservablePairCollection(IEnumerable<Pair<TKey, TValue>> enumerable)
: base(enumerable)
{
}
public ObservablePairCollection(List<Pair<TKey, TValue>> list)
: base(list)
{
}
public ObservablePairCollection(IDictionary<TKey, TValue> dictionary)
{
foreach (var kv in dictionary)
{
Add(new Pair<TKey, TValue>(kv));
}
}
public void Add(TKey key, TValue value)
{
Add(new Pair<TKey, TValue>(key, value));
}
}
Não precisam de parar por aqui. Não é muito difícil implementar a interface IDictionary e ter um dicionário de “verdade”, mas em muitos casos esta solução é suficiente.
Usar uma DataGrid editável
Vamos fazer uso disto numa DataGrid editável.
Declarem a propriedade no vosso view model:
readonly ObservablePairCollection<string, string> _countries;
public ObservablePairCollection<string, string> Countries { get { return _countries; } }
E criem alguns dados para testes:
public MainWindowViewModel()
{
_countries = new ObservablePairCollection<string, string>();
_countries.Add("Ascension Island", "AC");
_countries.Add("Andorra", "AD");
_countries.Add("United Arab Emirates", "AE");
_countries.Add("Afghanistan", "AF");
_countries.Add("Antigua and Barbuda", "AG");
// etc.
}
Incluam o controlo no XAML:
<DataGrid ItemsSource="{Binding Countries}" />
O resultado é o seguinte: