The absolutely simplest solution would be to translate to and from a DataTable and bind a GridView to that.
The DataTables support all the databinding support you want and allows you to add and remove rows.
Once the user is done, you could copy the contents of the datatable into your dictionary again.
Naive and dirty, but it would get the job done.
Dictionary<string, string> dict = new Dictionary<string, string>();
dict.Add("foo", "bar");
//translate and bind
DataTable dt = new DataTable();
dt.Columns.Add("Key", typeof(string));
dt.Columns.Add("Value", typeof(string));
dict
.ToList()
.ForEach(kvp => dt.Rows.Add(new object [] {kvp.Key,kvp.Value}));
//allows you to add uniqueness constraint to the key column :-)
dt.Constraints.Add("keyconstraint", dt.Columns[0], true);
myDataGridView.DataSource = dt;
And to translate back to dict:
Dictionary<string, string> dict = new Dictionary<string, string>();
DataTable dt = (DataTable)myDataGridView.DataSource;
dt.AsEnumerable()
.ToList()
.ForEach(row => dict.Add(row[0] as string, row[1] as string));