Browse Source

added new control TokenizedTextBox. Not ready for use yet, still working on architecture and funtionality.

pull/1645/head
brianlagunas_cp 15 years ago
parent
commit
0499cd1b56
  1. 3
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/Themes/Generic.xaml
  2. BIN
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/TokenizedTextBox/Images/delete8.png
  3. 20
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/TokenizedTextBox/Implementation/TokenItem.cs
  4. 276
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/TokenizedTextBox/Implementation/TokenizedTextBox.cs
  5. 14
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/TokenizedTextBox/Implementation/TokenizedTextBoxCommands.cs
  6. 59
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/TokenizedTextBox/Themes/Generic.xaml
  7. 10
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/WPFToolkit.Extended.csproj

3
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/Themes/Generic.xaml

@ -21,9 +21,8 @@
<ResourceDictionary Source="/WPFToolkit.Extended;component/SplitButton/Themes/Generic.xaml" />
<ResourceDictionary Source="/WPFToolkit.Extended;component/PropertyGrid/Themes/Generic.xaml" />
<ResourceDictionary Source="/WPFToolkit.Extended;component/TimePicker/Themes/Generic.xaml" />
<ResourceDictionary Source="/WPFToolkit.Extended;component/TokenizedTextBox/Themes/Generic.xaml" />
<ResourceDictionary Source="/WPFToolkit.Extended;component/WatermarkTextBox/Themes/Generic.xaml" />
<ResourceDictionary Source="/WPFToolkit.Extended;component/Wizard/Themes/Generic.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

BIN
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/TokenizedTextBox/Images/delete8.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 B

20
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/TokenizedTextBox/Implementation/TokenItem.cs

@ -0,0 +1,20 @@
using System.Windows;
using System.Windows.Controls;
namespace Microsoft.Windows.Controls
{
public class TokenItem : ContentControl
{
static TokenItem()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(TokenItem), new FrameworkPropertyMetadata(typeof(TokenItem)));
}
public static readonly DependencyProperty TokenKeyProperty = DependencyProperty.Register("TokenKey", typeof(string), typeof(TokenItem), new UIPropertyMetadata(null));
public string TokenKey
{
get { return (string)GetValue(TokenKeyProperty); }
set { SetValue(TokenKeyProperty, value); }
}
}
}

276
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/TokenizedTextBox/Implementation/TokenizedTextBox.cs

@ -0,0 +1,276 @@
//Based of the code written by Pavan Podila
//http://blog.pixelingene.com/2010/10/tokenizing-control-convert-text-to-tokens/
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
namespace Microsoft.Windows.Controls
{
public class TokenizedTextBox : ItemsControl
{
#region Members
private System.Windows.Controls.RichTextBox _rtb = null;
private bool _surpressTextChanged = false;
private bool _surpressTextChangedEvent = false;
#endregion //Members
#region Properties
public static readonly DependencyProperty SearchMemberPathProperty = DependencyProperty.Register("SearchMemberPath", typeof(string), typeof(TokenizedTextBox), new UIPropertyMetadata(String.Empty));
public string SearchMemberPath
{
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
{
get { return (string)GetValue(TokenDelimiterProperty); }
set { SetValue(TokenDelimiterProperty, value); }
}
public static readonly DependencyProperty TokenTemplateProperty = DependencyProperty.Register("TokenTemplate", typeof(DataTemplate), typeof(TokenizedTextBox), new UIPropertyMetadata(null));
public DataTemplate TokenTemplate
{
get { return (DataTemplate)GetValue(TokenTemplateProperty); }
set { SetValue(TokenTemplateProperty, value); }
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(TokenizedTextBox), new UIPropertyMetadata(null, OnTextChanged));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
private static void OnTextChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
TokenizedTextBox tokenizedTextBox = o as TokenizedTextBox;
if (tokenizedTextBox != null)
tokenizedTextBox.OnTextChanged((string)e.OldValue, (string)e.NewValue);
}
protected virtual void OnTextChanged(string oldValue, string newValue)
{
if (_rtb == null || _surpressTextChanged)
return;
}
public static readonly DependencyProperty ValueMemberPathProperty = DependencyProperty.Register("ValueMemberPath", typeof(string), typeof(TokenizedTextBox), new UIPropertyMetadata(String.Empty));
public string ValueMemberPath
{
get { return (string)GetValue(ValueMemberPathProperty); }
set { SetValue(ValueMemberPathProperty, value); }
}
#endregion //Properties
#region Constructors
static TokenizedTextBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(TokenizedTextBox), new FrameworkPropertyMetadata(typeof(TokenizedTextBox)));
}
public TokenizedTextBox()
{
CommandBindings.Add(new CommandBinding(TokenizedTextBoxCommands.Delete, DeleteToken));
}
#endregion //Constructors
#region Base Class Overrides
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
if (_rtb != null)
_rtb.TextChanged -= OnTextChanged;
_rtb = GetTemplateChild("PART_ContentHost") as System.Windows.Controls.RichTextBox;
if (_rtb != null)
_rtb.TextChanged += OnTextChanged;
InitializeTokensFromText();
}
#endregion //Base Class Overrides
#region Event Handlers
private void OnTextChanged(object sender, TextChangedEventArgs e)
{
if (_surpressTextChangedEvent)
return;
var text = _rtb.CaretPosition.GetTextInRun(LogicalDirection.Backward);
var token = ResolveToken(text);
if (token != null)
{
ReplaceTextWithToken(text.Trim(), token);
}
}
#endregion //Event Handlers
#region Methods
private void InitializeTokensFromText()
{
if (!String.IsNullOrEmpty(Text))
{
string[] tokens = Text.Split(new string[] { TokenDelimiter }, StringSplitOptions.RemoveEmptyEntries);
foreach (string token in tokens)
{
var para = _rtb.CaretPosition.Paragraph;
para.Inlines.Add(CreateTokenContainer(String.Format("{0}{1}", token, TokenDelimiter), ResolveTokenFromItemsSource(token)));
}
}
}
private object ResolveToken(string text)
{
if (text.EndsWith(TokenDelimiter))
return ResolveTokenFromItemsSource(text.Substring(0, text.Length - 1).Trim());
return null;
}
private object ResolveTokenBySearchMemberPath(string searchText)
{
if (ItemsSource != null)
{
foreach (object item in ItemsSource)
{
var property = item.GetType().GetProperty(SearchMemberPath);
if (property != null)
{
var value = property.GetValue(item, null);
if (searchText.Equals(value.ToString(), StringComparison.InvariantCultureIgnoreCase))
{
return item;
}
}
}
}
return searchText;
}
private object ResolveTokenFromItemsSource(string tokenKey)
{
if (ItemsSource != null)
{
foreach (object item in ItemsSource)
{
var property = item.GetType().GetProperty(ValueMemberPath);
if (property != null)
{
var value = property.GetValue(item, null);
if (tokenKey.Equals(value.ToString(), StringComparison.InvariantCultureIgnoreCase))
return item;
}
}
}
return tokenKey;
}
private void ReplaceTextWithToken(string inputText, object token)
{
_surpressTextChangedEvent = true;
var para = _rtb.CaretPosition.Paragraph;
var matchedRun = para.Inlines.FirstOrDefault(inline =>
{
var run = inline as Run;
return (run != null && run.Text.EndsWith(inputText));
}) as Run;
if (matchedRun != null) // Found a Run that matched the inputText
{
var tokenContainer = CreateTokenContainer(inputText, token);
para.Inlines.InsertBefore(matchedRun, tokenContainer);
// Remove only if the Text in the Run is the same as inputText, else split up
if (matchedRun.Text == inputText)
{
para.Inlines.Remove(matchedRun);
}
else // Split up
{
var index = matchedRun.Text.IndexOf(inputText) + inputText.Length;
var tailEnd = new Run(matchedRun.Text.Substring(index));
para.Inlines.InsertAfter(matchedRun, tailEnd);
para.Inlines.Remove(matchedRun);
}
//now append the Text
SetTextInternal(Text + inputText);
}
_surpressTextChangedEvent = false;
}
private InlineUIContainer CreateTokenContainer(string tokenKey, object token)
{
return new InlineUIContainer(CreateTokenItem(tokenKey, token)) { BaselineAlignment = BaselineAlignment.Center };
}
private TokenItem CreateTokenItem(string tokenKey, object token)
{
var tokenItem = new TokenItem();
tokenItem.TokenKey = tokenKey;
tokenItem.Content = token;
tokenItem.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);
if (property != null)
{
var value = property.GetValue(token, null);
if (value != null)
tokenItem.Content = value;
}
}
}
return tokenItem;
}
private void DeleteToken(object sender, ExecutedRoutedEventArgs e)
{
var para = _rtb.CaretPosition.Paragraph;
Inline inlineToRemove = para.Inlines.SingleOrDefault(x => ((x as InlineUIContainer).Child as TokenItem).TokenKey.Equals(e.Parameter));
if (inlineToRemove != null)
para.Inlines.Remove(inlineToRemove);
//update Text to remove delimited value
SetTextInternal(Text.Replace(e.Parameter.ToString(), ""));
}
private void SetTextInternal(string text)
{
_surpressTextChanged = true;
Text = text;
_surpressTextChanged = false;
}
#endregion //Methods
}
}

14
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/TokenizedTextBox/Implementation/TokenizedTextBoxCommands.cs

@ -0,0 +1,14 @@
using System.Windows.Input;
namespace Microsoft.Windows.Controls
{
public static class TokenizedTextBoxCommands
{
private static RoutedCommand _deleteCommand = new RoutedCommand();
public static RoutedCommand Delete
{
get { return _deleteCommand; }
}
}
}

59
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/TokenizedTextBox/Themes/Generic.xaml

@ -0,0 +1,59 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Microsoft.Windows.Controls" >
<Style TargetType="{x:Type local:TokenItem}">
<Setter Property="Background" Value="#F3F7FD" />
<Setter Property="BorderBrush" Value="#BBD8FB" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Cursor" Value="Arrow" />
<Setter Property="Padding" Value="5,1,5,1" />
<Setter Property="Margin" Value="1,0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:TokenItem}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
Margin="{TemplateBinding Margin}">
<StackPanel Orientation="Horizontal" Margin="1">
<ContentPresenter ContentTemplate="{TemplateBinding ContentTemplate}" />
<Button Margin="3,0,0,0" Cursor="Hand" Command="local:TokenizedTextBoxCommands.Delete" CommandParameter="{TemplateBinding TokenKey}" >
<Button.Template>
<ControlTemplate TargetType="Button">
<ContentPresenter />
</ControlTemplate>
</Button.Template>
<Image Source="/WPFToolkit.Extended;component/TokenizedTextBox/Images/delete8.png" Width="8" Height="8" />
</Button>
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type local:TokenizedTextBox}" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:TokenizedTextBox}">
<Border x:Name="Bd" Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<RichTextBox x:Name="PART_ContentHost" AcceptsReturn="False" IsDocumentEnabled="True" Padding="-3,0,0,0" />
<!--<Popup>
<Grid Background="White">
<ItemsPresenter />
</Grid>
</Popup>-->
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

10
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/WPFToolkit.Extended.csproj

@ -159,6 +159,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="TokenizedTextBox\Themes\Generic.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="WatermarkTextBox\Themes\Generic.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
@ -291,6 +295,9 @@
<Compile Include="TimePicker\Implementation\TimeFormat.cs" />
<Compile Include="TimePicker\Implementation\TimeItem.cs" />
<Compile Include="TimePicker\Implementation\TimePicker.cs" />
<Compile Include="TokenizedTextBox\Implementation\TokenItem.cs" />
<Compile Include="TokenizedTextBox\Implementation\TokenizedTextBox.cs" />
<Compile Include="TokenizedTextBox\Implementation\TokenizedTextBoxCommands.cs" />
<Compile Include="VisualStates.cs" />
<Compile Include="MessageBox\Implementation\VisualStates.MessageBox.cs" />
<Compile Include="WatermarkTextBox\Implementation\WatermarkTextBox.cs" />
@ -376,6 +383,9 @@
<ItemGroup>
<Resource Include="MultiLineTextEditor\Images\Notes16.png" />
</ItemGroup>
<ItemGroup>
<Resource Include="TokenizedTextBox\Images\delete8.png" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

Loading…
Cancel
Save