This is a little hacky, but I got it to work (assuming that I understand what you want).
I first created a view model class:
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));
        }
    }
}
And then in the 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>
The value of the Data property is bound to the ItemsSource of the ListBox. As you state in the question, this results in using instances of KeyValuePair<int, string> as the data behind the ListBox. I set the DisplayMemberPath to Key so that the value of the key will be used as the displayed value for each item in the ListBox.
As you found, you can't just use the Value of the KeyValuePair as the data for the TextBox, since that is read only. Instead, the TextBox is bound to a property on the view model which can get and set the value for the currently selected key (which is updated by binding the SelectedItem property of the ListBox to another property on the view model). I had to make this property nullable (since KeyValuePair is a struct), so that the code could detect when there is no selection.
In my test app, this seems to result in edits to the TextBox propagating to the Dictionary in the view model.
Is that more or less what you are going for? It seems like it should be a bit cleaner, but I'm not sure if there is any way to do so.