Browse Source

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:

<extToolkit:TokenizedTextBox x:Name="_textBox"
                                     DisplayMemberPath="FullName" //value to display
                                     SearchMemberPath="FirstName" //value to search for when typing
                                     ValueMemberPath="Id" //value to store in text property />

            //code behind
            _textBox.Text = "1;2;"; //list of object ids
            _textBox.ItemsSource = new List<Email>() //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" },
            };
pull/1645/head
brianlagunas_cp 15 years ago
parent
commit
bdcfc9c093
  1. 23
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/TokenizedTextBox/Implementation/Token.cs
  2. 82
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/TokenizedTextBox/Implementation/TokenizedTextBox.cs
  3. 1
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/WPFToolkit.Extended.csproj

23
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;
}
}
}

82
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;
}

1
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/WPFToolkit.Extended.csproj

@ -295,6 +295,7 @@
<Compile Include="TimePicker\Implementation\TimeFormat.cs" />
<Compile Include="TimePicker\Implementation\TimeItem.cs" />
<Compile Include="TimePicker\Implementation\TimePicker.cs" />
<Compile Include="TokenizedTextBox\Implementation\Token.cs" />
<Compile Include="TokenizedTextBox\Implementation\TokenItem.cs" />
<Compile Include="TokenizedTextBox\Implementation\TokenizedTextBox.cs" />
<Compile Include="TokenizedTextBox\Implementation\TokenizedTextBoxCommands.cs" />

Loading…
Cancel
Save