using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; namespace MultiTerm.Wpf.CustomControl; /// /// Follow steps 1a or 1b and then 2 to use this custom control in a XAML file. /// /// Step 1a) Using this custom control in a XAML file that exists in the current project. /// Add this XmlNamespace attribute to the root element of the markup file where it is /// to be used: /// /// xmlns:MyNamespace="clr-namespace:MultiTerm.Wpf.CustomControl" /// /// /// Step 1b) Using this custom control in a XAML file that exists in a different project. /// Add this XmlNamespace attribute to the root element of the markup file where it is /// to be used: /// /// xmlns:MyNamespace="clr-namespace:MultiTerm.Wpf.CustomControl.MultiFormatTextBox;assembly=MultiTerm.Wpf.CustomControl.MultiFormatTextBox" /// /// You will also need to add a project reference from the project where the XAML file lives /// to this project and Rebuild to avoid compilation errors: /// /// Right click on the target project in the Solution Explorer and /// "Add Reference"->"Projects"->[Browse to and select this project] /// /// /// Step 2) /// Go ahead and use your control in the XAML file. /// /// /// /// internal class Format { public string Name { get; set; } public Brush BackgroundBrush { get; set; } public Predicate IsKeyValid { get; set; } public Format(string name, Brush backgroundBrush, Predicate keyValidator) { this.Name = name; this.BackgroundBrush = backgroundBrush; this.IsKeyValid = keyValidator; } public static List GetListOfNames(IEnumerable formats) { return formats.Select(item => item.Name).ToList(); } } public class MultiFormatTextBox : Control { private static readonly List formats = new() { // character input, accepts all keys new Format("CHAR", Brushes.White, delegate(Key k) { return true; }), // hex input, ignores all keys that are not inbetween 0 and F new Format("HEX", Brushes.Orange, delegate(Key k) { return (k >= Key.D0 && k <= Key.F); }), // binary input, ignores all keys except 0 and 1 new Format("BIN", Brushes.AliceBlue, delegate(Key k) { return (k == Key.D0 || k == Key.D1); }) }; private ComboBox comboBox; private RichTextBox richTextBox; private Format currentlySelectedFormat = formats.First(); static MultiFormatTextBox() { DefaultStyleKeyProperty.OverrideMetadata(typeof(MultiFormatTextBox), new FrameworkPropertyMetadata(typeof(MultiFormatTextBox))); } public override void OnApplyTemplate() { base.OnApplyTemplate(); // get comboBox from template var comboBox = GetTemplateChild("comboBox") as ComboBox; if (comboBox != null) { this.comboBox = comboBox; this.comboBox.ItemsSource = Format.GetListOfNames(formats); this.comboBox.SelectedItem = currentlySelectedFormat.Name; this.comboBox.SelectionChanged += ComboBox_SelectionChanged; } // get richTextBox from template var richTextBox = GetTemplateChild("richTextBox") as RichTextBox; if (richTextBox != null) { this.richTextBox = richTextBox; this.richTextBox.KeyDown += RichTextBox_KeyDown; } } private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { // match all formats with the name selected in the combobox var matchingFormats = formats.Where(format => format.Name == (string)this.comboBox!.SelectedItem); // check if exactly one format was matched if(matchingFormats.Count() != 1) { throw new Exception($"{nameof(ComboBox_SelectionChanged)} could not match a correct amount of formats"); } // set currently selected format this.currentlySelectedFormat = matchingFormats.First(); } private void RichTextBox_KeyDown(object sender, KeyEventArgs e) { // guard combobox null if (comboBox == null) throw new Exception($"{nameof(comboBox)} cannot be null"); // if key is invalid for this format => ignore it (handled = true) if(this.currentlySelectedFormat.IsKeyValid(e.Key) == false) { e.Handled = true; } } }