/// <summary>
        /// Delete the selected material, or whatever else is appropriate when the Delete key is pressed.
        /// (Insertion Point deletes the following character.)
        /// </summary>
        public override void Delete()
        {
            Invalidate();             // while we still know the old position.
            if (Hookup == null)
            {
                return;
            }
            if (StringPosition == Hookup.Text.Length)
            {
                if (Hookup.ClientRunIndex == Para.Source.ClientRuns.Count - 1)
                {
                    DeleteLineBreak();
                    return;
                }
                if (Para.Source.ClientRuns[Hookup.ClientRunIndex + 1] is TextClientRun)
                {
                    // Delete at end of previous run.
                    var nextClientRun = Para.Source.NonEmptyStringClientRunBeginningAfter(Hookup.ClientRunIndex);
                    if (nextClientRun == null)
                    {
                        return;
                    }
                    // Enhance JohnT: maybe some kind of hookup can merge with previous para or delete an embedded object?
                    CopyFrom(nextClientRun.SelectAtStart(Para));
                    //Debug.Assert(StringPosition != Hookup.Text.Length - 1, "should have selected at the START of a non-empty run");
                }
            }
            var insertionPointEnd = new InsertionPoint(Hookup, StringPosition + 1, AssociatePrevious);

            if (!Hookup.CanDelete(this, insertionPointEnd))
            {
                return;
            }
            Hookup.Delete(this, insertionPointEnd);
        }
        /// <summary>
        /// Implement the backspace key function (delete one character, or merge two paragraphs).
        /// </summary>
        public void Backspace()
        {
            Invalidate();             // while we still know the old position.
            if (Hookup == null)
            {
                return;
            }
            if (StringPosition == 0)
            {
                if (Hookup.ClientRunIndex == 0)
                {
                    BackspaceDeleteLineBreak();
                    return;
                }
                if (Para.Source.ClientRuns[Hookup.ClientRunIndex - 1] is TextClientRun)
                {
                    // Delete at end of previous run.
                    var prevClientRun = Para.Source.NonEmptyStringClientRunEndingBefore(Hookup.ClientRunIndex);
                    if (prevClientRun == null)
                    {
                        return;
                    }
                    // Enhance JohnT: maybe some kind of hookup can merge with previous para or delete an embedded object?
                    CopyFrom(prevClientRun.SelectAtEnd(Para));
                    Debug.Assert(StringPosition != 0, "should have selected at the END of a non-empty run");
                }
            }
            string oldValue = Hookup.Text;
            int    newPos   = Surrogates.PrevChar(oldValue, StringPosition);        // Enhance JohnT: should we delete back to a base?
            var    start    = new InsertionPoint(Hookup, newPos, false);

            if (!Hookup.CanDelete(start, this))
            {
                return;
            }
            Hookup.Delete(start, this);
            StringPosition = newPos;
        }