From bdcfc9c093f72d46c5dfc13c17d66da7df198967 Mon Sep 17 00:00:00 2001 From: brianlagunas_cp Date: Tue, 11 Oct 2011 22:26:53 +0000 Subject: [PATCH] TokenizedTextBox: got the basics working. No autocomplete yet, but the value is looked up from the ItemsSource. This control can be used with or without an itemsrouce backing values. If no ItemsSource is define, the Text property will be a list of semi-colon separated values. If backed by an ItemsSource the Text property will be a semi-colon separated list of values based on the ValueMemberPath property: //code behind _textBox.Text = "1;2;"; //list of object ids _textBox.ItemsSource = new List() //use as lookup values { new Email() { Id = 1, FirstName = "John", LastName = "Doe", EmailAddress = "john@test.com" }, new Email() { Id = 2, FirstName = "Jane", LastName = "Doe", EmailAddress = "jane@test.com" }, }; --- .../TokenizedTextBox/Implementation/Token.cs | 23 ++++++ .../Implementation/TokenizedTextBox.cs | 82 ++++++++++++------- .../WPFToolkit.Extended.csproj | 1 + 3 files changed, 78 insertions(+), 28 deletions(-) create mode 100644 ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/TokenizedTextBox/Implementation/Token.cs diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/TokenizedTextBox/Implementation/Token.cs b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/TokenizedTextBox/Implementation/Token.cs new file mode 100644 index 00000000..53d69025 --- /dev/null +++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/TokenizedTextBox/Implementation/Token.cs @@ -0,0 +1,23 @@ +using System; +using System.Linq; + +namespace Microsoft.Windows.Controls +{ + internal class Token + { + public string Delimiter { get; private set; } + public object Item { get; set; } + + private string _tokenKey; + public string TokenKey + { + get { return _tokenKey; } + set { _tokenKey = String.Format("{0}{1}", value, Delimiter); } + } + + public Token(string delimiter) + { + Delimiter = delimiter; + } + } +} diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/TokenizedTextBox/Implementation/TokenizedTextBox.cs b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/TokenizedTextBox/Implementation/TokenizedTextBox.cs index a19dfcec..fbb3bb42 100644 --- a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/TokenizedTextBox/Implementation/TokenizedTextBox.cs +++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/TokenizedTextBox/Implementation/TokenizedTextBox.cs @@ -26,8 +26,7 @@ namespace Microsoft.Windows.Controls { get { return (string)GetValue(SearchMemberPathProperty); } set { SetValue(SearchMemberPathProperty, value); } - } - + } public static readonly DependencyProperty TokenDelimiterProperty = DependencyProperty.Register("TokenDelimiter", typeof(string), typeof(TokenizedTextBox), new UIPropertyMetadata(";")); public string TokenDelimiter @@ -43,6 +42,8 @@ namespace Microsoft.Windows.Controls set { SetValue(TokenTemplateProperty, value); } } + #region Text + public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(TokenizedTextBox), new UIPropertyMetadata(null, OnTextChanged)); public string Text { @@ -61,8 +62,12 @@ namespace Microsoft.Windows.Controls { if (_rtb == null || _surpressTextChanged) return; + + //TODO: when text changes update tokens } + #endregion //Text + public static readonly DependencyProperty ValueMemberPathProperty = DependencyProperty.Register("ValueMemberPath", typeof(string), typeof(TokenizedTextBox), new UIPropertyMetadata(String.Empty)); public string ValueMemberPath { @@ -127,45 +132,62 @@ namespace Microsoft.Windows.Controls { if (!String.IsNullOrEmpty(Text)) { - string[] tokens = Text.Split(new string[] { TokenDelimiter }, StringSplitOptions.RemoveEmptyEntries); - foreach (string token in tokens) + string[] tokenKeys = Text.Split(new string[] { TokenDelimiter }, StringSplitOptions.RemoveEmptyEntries); + foreach (string tokenKey in tokenKeys) { var para = _rtb.CaretPosition.Paragraph; - para.Inlines.Add(CreateTokenContainer(String.Format("{0}{1}", token, TokenDelimiter), ResolveTokenFromItemsSource(token))); + var token = new Token(TokenDelimiter) + { + TokenKey = tokenKey, + Item = ResolveItemByTokenKey(tokenKey) + }; + para.Inlines.Add(CreateTokenContainer(token)); } } } - private object ResolveToken(string text) + private Token ResolveToken(string text) { if (text.EndsWith(TokenDelimiter)) - return ResolveTokenFromItemsSource(text.Substring(0, text.Length - 1).Trim()); + return ResolveTokenBySearchMemberPath(text.Substring(0, text.Length - 1).Trim()); return null; } - private object ResolveTokenBySearchMemberPath(string searchText) + private Token ResolveTokenBySearchMemberPath(string searchText) { + //create a new token and default the settings to the search text + var token = new Token(TokenDelimiter) + { + TokenKey = searchText, + Item = searchText + }; + if (ItemsSource != null) { foreach (object item in ItemsSource) { - var property = item.GetType().GetProperty(SearchMemberPath); - if (property != null) + var searchProperty = item.GetType().GetProperty(SearchMemberPath); + if (searchProperty != null) { - var value = property.GetValue(item, null); - if (searchText.Equals(value.ToString(), StringComparison.InvariantCultureIgnoreCase)) + var searchValue = searchProperty.GetValue(item, null); + if (searchText.Equals(searchValue.ToString(), StringComparison.InvariantCultureIgnoreCase)) { - return item; + var valueProperty = item.GetType().GetProperty(ValueMemberPath); + if (valueProperty != null) + token.TokenKey = valueProperty.GetValue(item, null).ToString(); + + token.Item = item; + break; } } } } - return searchText; + return token; } - private object ResolveTokenFromItemsSource(string tokenKey) + private object ResolveItemByTokenKey(string tokenKey) { if (ItemsSource != null) { @@ -184,7 +206,7 @@ namespace Microsoft.Windows.Controls return tokenKey; } - private void ReplaceTextWithToken(string inputText, object token) + private void ReplaceTextWithToken(string inputText, Token token) { _surpressTextChangedEvent = true; @@ -198,7 +220,7 @@ namespace Microsoft.Windows.Controls if (matchedRun != null) // Found a Run that matched the inputText { - var tokenContainer = CreateTokenContainer(inputText, token); + var tokenContainer = CreateTokenContainer(token); para.Inlines.InsertBefore(matchedRun, tokenContainer); // Remove only if the Text in the Run is the same as inputText, else split up @@ -214,34 +236,38 @@ namespace Microsoft.Windows.Controls para.Inlines.Remove(matchedRun); } - //now append the Text - SetTextInternal(Text + inputText); + //now append the Text with the token key + SetTextInternal(Text + token.TokenKey); } _surpressTextChangedEvent = false; } - private InlineUIContainer CreateTokenContainer(string tokenKey, object token) + private InlineUIContainer CreateTokenContainer(Token token) { - return new InlineUIContainer(CreateTokenItem(tokenKey, token)) { BaselineAlignment = BaselineAlignment.Center }; + return new InlineUIContainer(CreateTokenItem(token)) { BaselineAlignment = BaselineAlignment.Center }; } - private TokenItem CreateTokenItem(string tokenKey, object token) + private TokenItem CreateTokenItem(Token token) { - var tokenItem = new TokenItem(); - tokenItem.TokenKey = tokenKey; - tokenItem.Content = token; - tokenItem.ContentTemplate = TokenTemplate; + object item = token.Item; + + var tokenItem = new TokenItem() + { + TokenKey = token.TokenKey, + Content = item, + ContentTemplate = TokenTemplate + }; if (TokenTemplate == null) { //if no template was supplied let's try to get a value from the object using the DisplayMemberPath if (!String.IsNullOrEmpty(DisplayMemberPath)) { - var property = token.GetType().GetProperty(DisplayMemberPath); + var property = item.GetType().GetProperty(DisplayMemberPath); if (property != null) { - var value = property.GetValue(token, null); + var value = property.GetValue(item, null); if (value != null) tokenItem.Content = value; } diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/WPFToolkit.Extended.csproj b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/WPFToolkit.Extended.csproj index 85f40a74..427355de 100644 --- a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/WPFToolkit.Extended.csproj +++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/WPFToolkit.Extended.csproj @@ -295,6 +295,7 @@ +