diff --git a/MultiTerm.Core/Model/FormattedCharacter.cs b/MultiTerm.Core/Model/FormattedCharacter.cs new file mode 100644 index 0000000..50be437 --- /dev/null +++ b/MultiTerm.Core/Model/FormattedCharacter.cs @@ -0,0 +1,14 @@ +using MultiTerm.Core.Types; + +namespace MultiTerm.Core.Model; + +public class FormattedCharacter : Tuple, IFormattedCharacter +{ + public FormattedCharacter(FormatType item1, string item2) : base(item1, item2) + { + } + + public FormattedCharacter(Tuple tuple) : base(tuple.Item1, tuple.Item2) + { + } +} diff --git a/MultiTerm.Core/Model/IFormattedCharacter.cs b/MultiTerm.Core/Model/IFormattedCharacter.cs new file mode 100644 index 0000000..5d18503 --- /dev/null +++ b/MultiTerm.Core/Model/IFormattedCharacter.cs @@ -0,0 +1,6 @@ +namespace MultiTerm.Core.Model; + +public interface IFormattedCharacter +{ + +} \ No newline at end of file diff --git a/MultiTerm.Core/Model/MultiFormatString.cs b/MultiTerm.Core/Model/MultiFormatString.cs index b2dffb8..28cc076 100644 --- a/MultiTerm.Core/Model/MultiFormatString.cs +++ b/MultiTerm.Core/Model/MultiFormatString.cs @@ -1,4 +1,5 @@ using MultiTerm.Core.Types; +using System.Collections.ObjectModel; using System.Text; namespace MultiTerm.Core.Model; @@ -6,46 +7,46 @@ namespace MultiTerm.Core.Model; /// /// A string that contains symbols of multiple formats such as a standard character as well as hexadecimal or binary parts. /// -public class MultiFormatString +public class MultiFormatString: ObservableCollection { /// - /// List of Symbols that are stored in this + /// Inserts item at given index after checking if its valid using . /// - public List> FormatValuePairs { get; private set; } = new(); - - /// - /// Adds a single character to . - /// - /// format of the - /// value of the symbol, must be convertible to an Unicode character - public void Add(FormatType format, string value) + /// index where to add item, usually items.Count when using Add method. + /// item to insert + protected override void InsertItem(int index, IFormattedCharacter item) { - // check if value is valid, if yes add to FormatValuePairs - if (ValidateValue(format, value)) + if(item is FormattedCharacter formattedChar) { - this.FormatValuePairs.Add(Tuple.Create(format, value)); + //check if valid, only insert then + if(ValidateValue(formattedChar.Item1, formattedChar.Item2) == false) + { + return; + } + + // check if spacing is required and insert + if(IsSpacingCharacterRequiredBeforeNewCharacter(this, index, formattedChar)) + { + base.InsertItem(index, new SpacingCharacter()); + base.InsertItem(index + 1, item); + + return; + } } + + base.InsertItem(index, item); } - /// - /// Removes a given amount of characters from the end of . - /// - /// amount of characters to remove from the list, starting at the last item - /// if amount <= 0 - public void Remove(int amount) + protected override void RemoveItem(int index) { - // guard amount <= 0 - if (amount <= 0) throw new ArgumentException($"{nameof(amount)} cannot be <= 0"); + // if formatted character => just remove + base.RemoveItem(index); - // limit amount to maximum - int listCount = this.FormatValuePairs.Count; - if (amount > listCount) + // if previous item is a spacing character => remove it too + if(this.ElementAt(index-1) is SpacingCharacter) { - amount = listCount; + base.RemoveAt(index - 1); } - - // remove range - this.FormatValuePairs.RemoveRange( (listCount - amount) , amount); } /// @@ -59,15 +60,15 @@ public class MultiFormatString // Internal function to check and finalize a Hexadecimal Conversion // parameter == null will force ending - void finalizeHexConversion(Tuple? formatValuePair) + void finalizeHexConversion(FormattedCharacter? formattedChar) { // hex conversion ongoing? if (String.IsNullOrEmpty(hexConversionCharacters) == false) { // ending conversion or reached limit of characters? - if (formatValuePair == null || - formatValuePair.Item1 != FormatType.Hexadecimal || - hexConversionCharacters.Count() == 4) + if (formattedChar == null || + formattedChar.Item1 != FormatType.Hexadecimal || + hexConversionCharacters.Length == 4) { // finalize conversion stringBuilder.Append(FromHexString(hexConversionCharacters)); @@ -79,15 +80,15 @@ public class MultiFormatString // Internal function to check and finalize a Binary Conversion // parameter == null will force ending - void finalizeBinaryConversion(Tuple? formatValuePair) + void finalizeBinaryConversion(FormattedCharacter? formattedChar) { // binary conversion ongoing? if (String.IsNullOrEmpty(binaryConversionCharacters) == false) { // ending conversion or reached limit of characters? - if (formatValuePair == null || - formatValuePair.Item1 != FormatType.Binary || - binaryConversionCharacters.Count() == 16) + if (formattedChar == null || + formattedChar.Item1 != FormatType.Binary || + binaryConversionCharacters.Length == 16) { // finalize conversion stringBuilder.Append(FromBinaryString(binaryConversionCharacters)); @@ -98,28 +99,31 @@ public class MultiFormatString } // go through every character - foreach (var formatValuePair in this.FormatValuePairs) + foreach (var character in this.Items) { + // can only handle FormattedCharacters + if(character is not FormattedCharacter formattedCharacter) { continue; } + // finalize ongoing conversions if there are any - finalizeHexConversion(formatValuePair); - finalizeBinaryConversion(formatValuePair); + finalizeHexConversion(formattedCharacter); + finalizeBinaryConversion(formattedCharacter); - switch (formatValuePair.Item1) // switch by format + switch (formattedCharacter.Item1) // switch by format { case FormatType.Character: - stringBuilder.Append(formatValuePair.Item2); + stringBuilder.Append(formattedCharacter.Item2); break; case FormatType.Hexadecimal: - hexConversionCharacters += formatValuePair.Item2; + hexConversionCharacters += formattedCharacter.Item2; break; case FormatType.Binary: - binaryConversionCharacters += formatValuePair.Item2; + binaryConversionCharacters += formattedCharacter.Item2; break; default: - throw new NotImplementedException($"'{nameof(ToString)}()' does not implement conversion for format {formatValuePair.Item1}"); + throw new NotImplementedException($"'{nameof(ToString)}()' does not implement conversion for format {formattedCharacter.Item1}"); } } @@ -137,7 +141,7 @@ public class MultiFormatString public static bool ValidateValue(FormatType format, string value) { // invalid if more than one character - if(value.Count() > 1) + if(value.Length > 1) { return false; } // extract character @@ -167,6 +171,80 @@ public class MultiFormatString } } + public static bool IsSpacingCharacterRequiredBeforeNewCharacter(IEnumerable collection, int indexOfInsertion, IFormattedCharacter newCharacter) + { + bool spacingCharRequired = false; + const int charsPerHexBundle = 2; + const int charsPerBinBundle = 8; + + // no spacing required if new character is a spacing character + if(newCharacter is SpacingCharacter) { return false; } + // no handling implemented for items that are not a FormattedCharacter + if(newCharacter is not FormattedCharacter formattedCharacter) + { throw new NotImplementedException($"'{nameof(IsSpacingCharacterRequiredBeforeNewCharacter)}()' does not implement handling for type {newCharacter.GetType()}"); } + + // switch spacing required by type of format + spacingCharRequired = formattedCharacter.Item1 switch + { + FormatType.Character => CheckSpacingRequiredBecauseChangedFormat(collection, indexOfInsertion, formattedCharacter.Item1), + + FormatType.Hexadecimal => CheckSpacingRequiredBecauseChangedFormat(collection, indexOfInsertion, formattedCharacter.Item1) || + CheckSpacingRequiredBecauseBundleMaxCountReached(collection, indexOfInsertion, charsPerHexBundle, formattedCharacter.Item1), + + FormatType.Binary => CheckSpacingRequiredBecauseChangedFormat(collection, indexOfInsertion, formattedCharacter.Item1) || + CheckSpacingRequiredBecauseBundleMaxCountReached(collection, indexOfInsertion, charsPerBinBundle, formattedCharacter.Item1), + + _ => throw new NotImplementedException($"'{nameof(IsSpacingCharacterRequiredBeforeNewCharacter)}()' does not implement handling for format {formattedCharacter.Item1}"), + }; + return spacingCharRequired; + + static bool CheckSpacingRequiredBecauseChangedFormat(IEnumerable collection, int indexOfInsertion, FormatType newCharFormat) + { + // check if previous item is of other type + if (collection.Any()) + { + int count = 1; + FormattedCharacter? prevChar = null; + while (prevChar is null) + { + try + { + prevChar = collection.ElementAt(indexOfInsertion - count++) as FormattedCharacter; + // format of previous is not equal to new => spacer required + if (prevChar != null && prevChar.Item1 != newCharFormat) + { + return true; + } + } + catch (Exception) + { + // end of collection => break while + break; + } + } + } + return false; + } + + static bool CheckSpacingRequiredBecauseBundleMaxCountReached(IEnumerable collection, int indexOfInsertion, int charsPerBundle, FormatType newCharFormat) + { + // spacing required if previous x characters are of same format + if (collection.Count() > charsPerBundle) + { + // get last x elements of collection + var lastXElements = collection.Take(new Range(new Index(indexOfInsertion - charsPerBundle), new Index(indexOfInsertion))); + // get elements of last x elements where the same format is used + var sameFormatElements = lastXElements.Where(x => x is FormattedCharacter formChar && formChar.Item1 == newCharFormat); + // elements of same Format are more (impossible) or equal to amount required for bundle + if (sameFormatElements.Count() >= charsPerBundle) + { + return true; + } + } + return false; + } + } + // from: https://stackoverflow.com/questions/724862/converting-from-hex-to-string // test with: returns: "Hello world" for "48656C6C6F20776F726C64" // see https://www.fileformat.info/info/charset/UTF-16/list.htm diff --git a/MultiTerm.Core/Model/SpacingCharacter.cs b/MultiTerm.Core/Model/SpacingCharacter.cs new file mode 100644 index 0000000..3b7f5d4 --- /dev/null +++ b/MultiTerm.Core/Model/SpacingCharacter.cs @@ -0,0 +1,6 @@ +namespace MultiTerm.Core.Model; + +public class SpacingCharacter : IFormattedCharacter +{ + +} diff --git a/MultiTerm.Core/MultiTerm.Core.csproj b/MultiTerm.Core/MultiTerm.Core.csproj index 1da0386..e073bc8 100644 --- a/MultiTerm.Core/MultiTerm.Core.csproj +++ b/MultiTerm.Core/MultiTerm.Core.csproj @@ -6,10 +6,6 @@ enable - - - - diff --git a/MultiTerm.Core/ViewModel/SendReceiveViewModel.cs b/MultiTerm.Core/ViewModel/SendReceiveViewModel.cs index f29dc16..f92b123 100644 --- a/MultiTerm.Core/ViewModel/SendReceiveViewModel.cs +++ b/MultiTerm.Core/ViewModel/SendReceiveViewModel.cs @@ -43,8 +43,8 @@ public partial class SendReceiveViewModel : TerminalViewModel this.TempSentDataString = this.SendableData.ToString(); // send data this.SendToCommunicationProtocol(this.SendableData.ToString()); - + // clear textbox - this.SendableData = new MultiFormatString(); + this.SendableData.Clear(); } } diff --git a/MultiTerm.Wpf.CustomControl/MultiFormatTextBox/MultiFormatTextBox.cs b/MultiTerm.Wpf.CustomControl/MultiFormatTextBox/MultiFormatTextBox.cs index f201842..0c08525 100644 --- a/MultiTerm.Wpf.CustomControl/MultiFormatTextBox/MultiFormatTextBox.cs +++ b/MultiTerm.Wpf.CustomControl/MultiFormatTextBox/MultiFormatTextBox.cs @@ -2,8 +2,8 @@ using MultiTerm.Core.Types; using System; using System.Collections.Generic; +using System.Collections.Specialized; using System.ComponentModel; -using System.Diagnostics; using System.Linq; using System.Windows; using System.Windows.Controls; @@ -33,8 +33,8 @@ public class MultiFormatTextBox : Control #endregion #region private content - private const string comboBoxTemplateKey = "comboBox"; - private const string richTextBoxTemplateKey = "richTextBox"; + private const string comboBoxTemplateKey = "PART_ComboBox"; + private const string richTextBoxTemplateKey = "PART_RichTextBox"; private ComboBox? comboBox; private RichTextBox? richTextBox; @@ -93,8 +93,10 @@ public class MultiFormatTextBox : Control { this.richTextBox = richTextBox; this.richTextBox.AcceptsReturn = false; - this.richTextBox.KeyDown += RichTextBox_KeyDown; - this.richTextBox.TextChanged += RichTextBox_TextChanged; + this.richTextBox.AcceptsTab = false; + this.richTextBox.PreviewKeyDown += RichTextBox_KeyDown; + this.richTextBox.PreviewTextInput += RichTextBox_PreviewTextInput; + this.richTextBox.SelectionChanged += RichTextBox_SelectionChanged; } else { @@ -108,13 +110,134 @@ public class MultiFormatTextBox : Control if (d is not MultiFormatTextBox mftb) { return; } if (e.NewValue is not MultiFormatString newString) { return; } - // new value is an empty string => clear - if(newString.FormatValuePairs.Count == 0) + // register to collection changed event + if (mftb.CurrentMultiFormatString is INotifyCollectionChanged incc) { - mftb.richTextBox!.Document.Blocks.Clear(); + incc.CollectionChanged += mftb.MultiFormatString_CollectionChanged; } } + /// + /// Reacts on Collection Changed Events. + /// + private void MultiFormatString_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) + { + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + // guard null + if(e.NewItems == null) { return; } + // add list to textbox + this.AddItemsToTextBox(e.NewItems, e.NewStartingIndex); + break; + + case NotifyCollectionChangedAction.Remove: + // guard null + if (e.OldItems == null) { return; } + // remove list to textbox + this.RemoveItemsFromTextBox(e.OldItems, e.OldStartingIndex); + break; + + case NotifyCollectionChangedAction.Reset: + // clear richtextbox add new paragraph + this.richTextBox!.Document.Blocks.Clear(); + this.richTextBox!.Document.Blocks.Add(new Paragraph()); + break; + + case NotifyCollectionChangedAction.Replace: + case NotifyCollectionChangedAction.Move: + default: + throw new NotImplementedException($"'{nameof(MultiFormatString_CollectionChanged)}()' does not support Action '{e.Action}'"); + } + } + + private void RemoveItemsFromTextBox(System.Collections.IList items, int startingIndex) + { + // extract paragraph from richtextbox (last block is paragraph per default) + if (this.richTextBox!.Document.Blocks.LastBlock is not Paragraph paragraph) + { + throw new Exception($"'{nameof(RemoveItemsFromTextBox)}()' found that {nameof(richTextBox)} has no paragraph as last block."); + } + + if (items.Count > 1) + { + throw new Exception("Not supported"); + } + + string toRemoveString = string.Empty; + if (items[0] is FormattedCharacter formattedCharacter) + { + toRemoveString = formattedCharacter.Item2; + } + else if(items[0] is SpacingCharacter spacingCharacter) + { + toRemoveString = " "; + } + else + { + throw new Exception("Not supported"); + } + + var inlineToRemove = paragraph.Inlines.ElementAt(new Index(startingIndex)); // paragraph.Inlines = zero based counting + var textInline = new TextRange(inlineToRemove.ContentStart, inlineToRemove.ContentEnd); + if(textInline.Text != toRemoveString) + { + throw new Exception("Tried to remove other element..."); + } + paragraph.Inlines.Remove(inlineToRemove); + } + + private void AddItemsToTextBox(System.Collections.IList items, int startingIndex) + { + int indexCounter = startingIndex; + + // extract paragraph from richtextbox (last block is paragraph per default) + var paragraph = this.richTextBox!.Document.Blocks.LastBlock as Paragraph ?? throw new Exception($"'{nameof(AddItemsToTextBox)}()' found that {nameof(richTextBox)} has no paragraph as last block."); + + // iterate through formatted characters to add + foreach (IFormattedCharacter item in items) + { + Run? run = null; + + if (item is FormattedCharacter formattedCharacter) + { + // text as run with correct background brush + run = new Run(formattedCharacter.Item2) + { + Background = this.currentlySelectedFormat!.BackgroundBrush + }; + } + else if(item is SpacingCharacter) + { + // text as run with default background brush + run = new Run(" ") + { + Background = defaultBackgroundBrush + }; + } + else + { + throw new Exception($"'{nameof(AddItemsToTextBox)}()' cannot handle items of type {item.GetType()}"); + } + + // if this is the first element to enter in the paragraph => simply add it + if(paragraph!.Inlines.Count == 0) + { + paragraph.Inlines.Add(run); + } + else // add to paragraph of richtextbox after previous inline + { + var previousInline = paragraph.Inlines.ElementAt(new Index(indexCounter-1)); // paragraph.Inlines = zero based counting + paragraph.Inlines.InsertAfter(previousInline, run); + } + + // increment counter + indexCounter++; + } + + // update caret position to index position + this.SetRtbCaretPosition(indexCounter); + } private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { @@ -131,155 +254,101 @@ public class MultiFormatTextBox : Control // set currently selected format, reset offset counter this.currentlySelectedFormat = matchingFormats.First(); - // insert space to separate formats - this.InsertSeparation(); - // focus textbox this.richTextBox!.Focus(); } } - private void InsertSeparation() - { - // disable event handler - this.richTextBox!.TextChanged -= RichTextBox_TextChanged; - - // insert separator, manually updating caret position because wpf somehow does not do it.... - int caretIndexBefore = this.richTextBox.Document.ContentStart.GetOffsetToPosition(this.richTextBox.CaretPosition); - Debug.WriteLine($"caret position before inserting separator: offset={this.richTextBox.Document.ContentStart.GetOffsetToPosition(this.richTextBox.CaretPosition)}"); - this.richTextBox.AppendText(" "); - this.richTextBox.CaretPosition = this.richTextBox.Document.ContentStart.GetPositionAtOffset(caretIndexBefore + 1); - Debug.WriteLine($"caret position after inserting separator: offset={this.richTextBox.Document.ContentStart.GetOffsetToPosition(this.richTextBox.CaretPosition)}"); - - // change background of last one character - RtbChangeTextBackground(this.richTextBox, - start: this.richTextBox!.Document.ContentStart.GetPositionAtOffset(caretIndexBefore), - end: this.richTextBox.CaretPosition, - color: defaultBackgroundBrush); - - // reenable - this.richTextBox.TextChanged += RichTextBox_TextChanged; - } - private void RichTextBox_KeyDown(object sender, KeyEventArgs e) { - // guard combobox null - if (this.comboBox == null) throw new Exception($"{nameof(this.comboBox)} cannot be null"); - - // if key is invalid for this format => ignore it (handled = true) - if (this.currentlySelectedFormat!.IsKeyValid(e.Key) == false) + if (e.Key == Key.Enter) { + // TODO Raise EnterPressedEvent Here e.Handled = true; } + else if(e.Key == Key.Space) + { + e.Handled = true; - // ignore enter - if(e.Key == Key.Enter) - { - e.Handled = true; + // add space to MultiFormatString (will only work if currently character type is selected) + InsertCharacterAtCaretPositionIntoCurrentMultiFormatString(" "); } - } - - private void RichTextBox_TextChanged(object sender, TextChangedEventArgs e) - { - // no changes => exit - if (e.Changes.Count == 0) return; - - lock (lockObj) // lock so no concurrent combobox change can happen + // back one + else if (e.Key == Key.Back) { - foreach (var change in e.Changes) - { - // print new text - Debug.WriteLine($"{nameof(RichTextBox_TextChanged)} changed to '{new TextRange(this.richTextBox!.Document.ContentStart, this.richTextBox.Document.ContentEnd).Text}'"); - Debug.WriteLine($"{nameof(RichTextBox_TextChanged)} caret position '{this.richTextBox!.Document.ContentStart.GetOffsetToPosition(this.richTextBox.CaretPosition)}'"); + var cursorPos = GetRtbCaretPosition(); - // ignore changes that replace something - if (change.RemovedLength == change.AddedLength) continue; - - // if something was added => change background color - if (change.AddedLength > 0) + // remove previous element if it exists + try + { + if (this.CurrentMultiFormatString.ElementAt(cursorPos - 1) != null) { - Debug.WriteLine($"{nameof(RichTextBox_TextChanged)} offset of change = {change.Offset}"); - - // disable event handler so the update does not trigger any TextChanged events (which it does, interestingly) - this.richTextBox!.TextChanged -= RichTextBox_TextChanged; + this.CurrentMultiFormatString.RemoveAt(cursorPos - 1); + } + } + catch { } - // update color - RtbChangeTextBackground(this.richTextBox!, - start: this.richTextBox!.Document.ContentStart.GetPositionAtOffset(change.Offset), - end: this.richTextBox!.Document.ContentStart.GetPositionAtOffset(change.Offset + change.AddedLength), - color: this.currentlySelectedFormat!.BackgroundBrush); + e.Handled = true; + } + // delete next (selection is not allowed) + else if (e.Key == Key.Delete) + { + var cursorPos = GetRtbCaretPosition(); - // reenable event handler - this.richTextBox.TextChanged += RichTextBox_TextChanged; + // remove next element if it exists + try + { + if (this.CurrentMultiFormatString.ElementAt(cursorPos) != null) + { + this.CurrentMultiFormatString.RemoveAt(cursorPos); } } + catch { } - // update CurrentMultiFormatString - this.CurrentMultiFormatString = ConvertRtbContentToMultiFormatString(this.richTextBox!); + e.Handled = true; } } - private static void RtbChangeTextBackground(RichTextBox rtb, TextPointer start, TextPointer end, Brush color) + private int GetRtbCaretPosition() { - // print offsets to start and end position - Debug.WriteLine($"Changing background of textbox offset {rtb.Document.ContentStart.GetOffsetToPosition(start)} until {rtb.Document.ContentStart.GetOffsetToPosition(end)} to {color}"); - - // get editable selection - var textRange = rtb.Selection; - textRange.Select(start, end); - - // Apply property to the selection: - textRange.ApplyPropertyValue(TextElement.BackgroundProperty, color); - - // deselect everything (set to end) - rtb.Selection.Select(end, end); + // magic dividor of 3.... nobody knows why... + return this.richTextBox!.Document.ContentStart.GetOffsetToPosition(this.richTextBox.CaretPosition) / 3; } - private static MultiFormatString ConvertRtbContentToMultiFormatString(RichTextBox rtb) + private void SetRtbCaretPosition(int index) { - MultiFormatString multiFormatString = new(); - // store current caret position - int offsetToCaretPosition = rtb.Document.ContentStart.GetOffsetToPosition(rtb.CaretPosition); - - // get number of symbols - var textLength = rtb.Document.ContentStart.GetOffsetToPosition(rtb.Document.ContentEnd); - // get full content as TextSelection object - var textRange = rtb.Selection; + // magic multiplicator of 3.... nobody knows why... + this.richTextBox!.CaretPosition = this.richTextBox.Document.ContentStart.GetPositionAtOffset(index*3); + } - // loop through every character - for (int offset = 0; offset < textLength; offset++) - { - // select one character - textRange.Select(rtb.Document.ContentStart.GetPositionAtOffset(offset), rtb.Document.ContentStart.GetPositionAtOffset(offset + 1)); + private void RichTextBox_PreviewTextInput(object sender, TextCompositionEventArgs e) + { + // text is never directly inserted => therefore always handled + e.Handled = true; - //skip empty textRange - if (textRange.IsEmpty) - { continue; } + // if more than one char inserted => illegal => cancel + if(e.Text.Length > 1) { return; } - // extract background brush - var brush = (Brush)textRange.GetPropertyValue(TextElement.BackgroundProperty); + this.InsertCharacterAtCaretPositionIntoCurrentMultiFormatString(e.Text); + } - // ignore this symbol, since its a separator - if (brush == defaultBackgroundBrush) - { continue; } + private void InsertCharacterAtCaretPositionIntoCurrentMultiFormatString(string text) + { + var caretPosition = this.GetRtbCaretPosition(); - // search if it is a brush of a format - foreach (var format in formats) - { - // format has this background brush => add to string with format and text - if (brush == format.BackgroundBrush) - { - multiFormatString.Add(format.AssociatedFormatType, textRange.Text); - break; // end loop - } - } - } + // add char to MultiFormatString + FormattedCharacter formattedCharacter = new(this.currentlySelectedFormat!.AssociatedFormatType, text); - // deselect everything (set to end) - textRange.Select( - rtb.Document.ContentStart.GetPositionAtOffset(offsetToCaretPosition), - rtb.Document.ContentStart.GetPositionAtOffset(offsetToCaretPosition)); + this.CurrentMultiFormatString.Insert(caretPosition, formattedCharacter); + //this.CurrentMultiFormatString.Add(formattedCharacter); + } - return multiFormatString; + private void RichTextBox_SelectionChanged(object sender, RoutedEventArgs e) + { + // do not allow selection + if (this.richTextBox!.Selection.Text.Length > 0) + { + this.richTextBox!.Selection.Select(this.richTextBox.Selection.End, this.richTextBox.Selection.End); + } } } diff --git a/MultiTerm.Wpf.CustomControl/MultiFormatTextBox/MultiFormatTextBox.xaml b/MultiTerm.Wpf.CustomControl/MultiFormatTextBox/MultiFormatTextBox.xaml index 315f222..3c343bf 100644 --- a/MultiTerm.Wpf.CustomControl/MultiFormatTextBox/MultiFormatTextBox.xaml +++ b/MultiTerm.Wpf.CustomControl/MultiFormatTextBox/MultiFormatTextBox.xaml @@ -22,8 +22,8 @@ - - + +