動的にバインドする datatemplate 上のボタン付きのテキスト ボックスを見つけることができる拡張された richetextbox が 1 つあります。削除ボタンをクリックしたときの問題は機能しません。どこが間違っているのか助けてください。
拡張されたリッチ テキスト ボックス ファイルは次のとおりです。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
namespace Rabbit.Controls
{
public class RichTextBoxEx:RichTextBox
{
public bool AutoAddWhiteSpaceAfterTriggered
{
get { return (bool)GetValue(AutoAddWhiteSpaceAfterTriggeredProperty); }
set { SetValue(AutoAddWhiteSpaceAfterTriggeredProperty, value); }
}
// Using a DependencyProperty as the backing store for AutoAddWhiteSpaceAfterTriggered. This enables animation, styling, binding, etc...
public static readonly DependencyProperty AutoAddWhiteSpaceAfterTriggeredProperty =
DependencyProperty.Register("AutoAddWhiteSpaceAfterTriggered", typeof(bool), typeof(RichTextBoxEx), new UIPropertyMetadata(true));
public IList<String> ContentAssistSource
{
get { return (IList<String>)GetValue(ContentAssistSourceProperty); }
set { SetValue(ContentAssistSourceProperty, value); }
}
// Using a DependencyProperty as the backing store for ContentAssistSource. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ContentAssistSourceProperty =
DependencyProperty.Register("ContentAssistSource", typeof(IList<String>), typeof(RichTextBoxEx), new UIPropertyMetadata(new List<string>()));
public IList<char> ContentAssistTriggers
{
get { return (IList<char>)GetValue(ContentAssistTriggersProperty); }
set { SetValue(ContentAssistTriggersProperty, value); }
}
// Using a DependencyProperty as the backing store for ContentAssistSource. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ContentAssistTriggersProperty =
DependencyProperty.Register("ContentAssistTriggers", typeof(IList<char>), typeof(RichTextBoxEx), new UIPropertyMetadata(new List<char>()));
public static readonly DependencyProperty TokenTemplateProperty =
DependencyProperty.Register("TokenTemplate", typeof(DataTemplate), typeof(RichTextBoxEx));
#region constructure
public RichTextBoxEx()
{
this.Loaded += new RoutedEventHandler(RichTextBoxEx_Loaded);
TextChanged += OnTokenTextChanged;
}
void RichTextBoxEx_Loaded(object sender, RoutedEventArgs e)
{
//init the assist list box
if (this.Parent.GetType() != typeof(Grid))
{
throw new Exception("this control must be put in Grid control");
}
if (ContentAssistTriggers.Count == 0)
{
ContentAssistTriggers.Add('@');
}
(this.Parent as Grid).Children.Add(AssistListBox);
AssistListBox.MaxHeight = 100;
AssistListBox.MinWidth = 100;
AssistListBox.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
AssistListBox.VerticalAlignment = System.Windows.VerticalAlignment.Top;
AssistListBox.Visibility = System.Windows.Visibility.Collapsed;
AssistListBox.MouseDoubleClick += new MouseButtonEventHandler(AssistListBox_MouseDoubleClick);
AssistListBox.PreviewKeyDown += new KeyEventHandler(AssistListBox_PreviewKeyDown);
}
void AssistListBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
//if Enter\Tab\Space key is pressed, insert current selected item to richtextbox
if (e.Key == Key.Enter || e.Key == Key.Tab || e.Key == Key.Space)
{
InsertAssistWord();
OnTokenTextChanged(sender, null);
e.Handled = true;
}
else if (e.Key == Key.Back)
{
//Baskspace key is pressed, set focus to richtext box
if (sbLastWords.Length >= 1)
{
sbLastWords.Remove(sbLastWords.Length - 1, 1);
}
this.Focus();
}
}
void AssistListBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
InsertAssistWord();
}
private bool InsertAssistWord()
{
bool isInserted = false;
if (AssistListBox.SelectedIndex != -1)
{
string selectedString = AssistListBox.SelectedItem.ToString().Remove(0, sbLastWords.Length);
if (AutoAddWhiteSpaceAfterTriggered)
{
selectedString += ";";
}
this.InsertText(selectedString);
isInserted = true;
}
AssistListBox.Visibility = System.Windows.Visibility.Collapsed;
sbLastWords.Clear();
IsAssistKeyPressed = false;
return isInserted;
}
#endregion
#region check richtextbox's document.blocks is available
private void CheckMyDocumentAvailable()
{
if (this.Document == null)
{
this.Document = new System.Windows.Documents.FlowDocument();
}
if (Document.Blocks.Count == 0)
{
Paragraph para = new Paragraph();
Document.Blocks.Add(para);
}
}
#endregion
#region Insert Text
public void InsertText(string text)
{
Focus();
CaretPosition.InsertTextInRun(text);
TextPointer pointer = CaretPosition.GetPositionAtOffset(text.Length);
if (pointer != null)
{
CaretPosition = pointer;
}
}
#endregion
#region Content Assist
private bool IsAssistKeyPressed = false;
private System.Text.StringBuilder sbLastWords = new System.Text.StringBuilder();
private ListBox AssistListBox = new ListBox();
protected override void OnPreviewKeyDown(System.Windows.Input.KeyEventArgs e)
{
if (!IsAssistKeyPressed)
{
base.OnPreviewKeyDown(e);
return;
}
ResetAssistListBoxLocation();
if (e.Key == System.Windows.Input.Key.Back)
{
if (sbLastWords.Length > 0)
{
sbLastWords.Remove(sbLastWords.Length - 1, 1);
FilterAssistBoxItemsSource();
}
else
{
IsAssistKeyPressed = false;
sbLastWords.Clear();
AssistListBox.Visibility = System.Windows.Visibility.Collapsed;
}
}
//enter key pressed, insert the first item to richtextbox
if ((e.Key == Key.Enter || e.Key == Key.Space || e.Key == Key.Tab))
{
AssistListBox.SelectedIndex = 0;
if (InsertAssistWord())
{
e.Handled = true;
}
}
if (e.Key == Key.Down)
{
AssistListBox.Focus();
}
base.OnPreviewKeyDown(e);
}
private void FilterAssistBoxItemsSource()
{
IEnumerable<string> temp = ContentAssistSource.Where(s => s.ToUpper().StartsWith(sbLastWords.ToString().ToUpper()));
AssistListBox.ItemsSource = temp;
AssistListBox.SelectedIndex = 0;
if (temp.Count() == 0)
{
AssistListBox.Visibility = System.Windows.Visibility.Collapsed;
}
else
{
AssistListBox.Visibility = System.Windows.Visibility.Visible;
}
}
protected override void OnTextInput(System.Windows.Input.TextCompositionEventArgs e)
{
base.OnTextInput(e);
if (IsAssistKeyPressed == false && e.Text.Length == 1)
{
if (ContentAssistTriggers.Contains(char.Parse(e.Text)))
{
ResetAssistListBoxLocation();
IsAssistKeyPressed = true;
FilterAssistBoxItemsSource();
return;
}
}
if (IsAssistKeyPressed)
{
sbLastWords.Append(e.Text);
FilterAssistBoxItemsSource();
}
}
private void ResetAssistListBoxLocation()
{
Rect rect = this.CaretPosition.GetCharacterRect(LogicalDirection.Forward);
double left = rect.X >= 20 ? rect.X : 20;
double top = rect.Y >= 20 ? rect.Y + 20 : 20;
left += this.Padding.Left;
top += this.Padding.Top;
AssistListBox.SetCurrentValue(ListBox.MarginProperty, new Thickness(left, top, 0, 0));
}
#endregion
public DataTemplate TokenTemplate
{
get { return (DataTemplate)GetValue(TokenTemplateProperty); }
set { SetValue(TokenTemplateProperty, value); }
}
public Func<string, object> TokenMatcher { get; set; }
private void OnTokenTextChanged(object sender, TextChangedEventArgs e)
{
var text = CaretPosition.GetTextInRun(LogicalDirection.Backward);
if (TokenMatcher != null)
{
var token = TokenMatcher(text);
if (token != null)
{
ReplaceTextWithToken(text, token);
}
}
}
private void ReplaceTextWithToken(string inputText, object token)
{
// Remove the handler temporarily as we will be modifying tokens below, causing more TextChanged events
TextChanged -= OnTokenTextChanged;
var para = 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);
}
}
TextChanged += OnTokenTextChanged;
}
private InlineUIContainer CreateTokenContainer(string inputText, object token)
{
// Note: we are not using the inputText here, but could be used in future
var presenter = new ContentPresenter()
{
Content = token,
ContentTemplate = TokenTemplate,
};
// BaselineAlignment is needed to align with Run
return new InlineUIContainer(presenter) { BaselineAlignment = BaselineAlignment.TextBottom };
}
}
}
こちらがメインウィンドウ Xmal Code
<Window x:Class="RichTextBoxWithIntellisense.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
Title="Intellisense Rich Text Demo" Height="350" Width="525" xmlns:rabbit="clr-namespace:Rabbit.Controls;assembly=Rabbit.Controls">
<Window.Resources>
<LinearGradientBrush x:Key="ClickGradient">
<GradientStop Color="#FF5C2014"/>
<GradientStop Color="#FFB0452C"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HoverGradient">
<GradientStop Color="#FF757474"/>
<GradientStop Color="#FF918F8F"/>
</LinearGradientBrush>
<!--<Style x:Key="DeleteButton" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>-->
<ControlTemplate x:Key="CloseButton" TargetType="{x:Type Button}">
<Grid x:Name="MainGrid">
<Rectangle
x:Name="MainRectangle"
Fill="#00000000"
RadiusX="5"
RadiusY="5"/>
<ContentPresenter
x:Name="Presenter"
HorizontalAlignment="Center"
VerticalAlignment="Center"
TextBlock.Foreground="#BB225588"/>
<Path x:Name="Cross" Data="M0,0 L1,1 M0,1 L1,0" Stretch="Fill"
Stroke="Black" StrokeThickness="2" Width="8" Height="8" />
</Grid>
<!--<StackPanel Orientation="Vertical">
<Path Width="16" Height="16" Fill="#FFFF0000" Stretch="Uniform"
Data="M19.85228,12.08996L12.093,19.849201 24.242323,31.997846 12.094,44.145998 19.852051,51.904958 32.001186,39.756277 44.150543,51.904958 51.909,44.145994 39.760246,31.997501 51.909,19.849201 44.15049,12.08996 32.001431,24.238849z M32,0C49.671021,3.1599484E-07 64,14.329407 64,31.998501 64,49.677606 49.671021,63.997003 32,63.997003 14.328003,63.997003 0,49.677606 0,31.998501 0,14.329407 14.328003,3.1599484E-07 32,0z">
</Path>
</StackPanel>-->
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="MainRectangle" Property="Fill" Value="{StaticResource HoverGradient}"/>
<Setter TargetName="MainRectangle" Property="Stroke" Value="Transparent"/>
<Setter TargetName="Cross" Property="Stroke" Value="White" />
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="MainRectangle" Property="Fill" Value="{StaticResource ClickGradient}"/>
<Setter TargetName="Cross" Property="Stroke" Value="White" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<!--</Setter.Value>
</Setter>
</Style>-->
<DataTemplate x:Key="NameTokenTemplate">
<DataTemplate.Resources>
<Storyboard x:Key="OnLoaded1">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="border">
<SplineDoubleKeyFrame KeyTime="0" Value="0"/>
<SplineDoubleKeyFrame KeyTime="0:0:0.5" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Style x:Key="BtnStyle" TargetType="{x:Type Button}">
<EventSetter Event="Click" Handler="Button_Click"/>
</Style>
</DataTemplate.Resources>
<Border x:Name="border" BorderBrush="#FF7E7E7E" BorderThickness="2" CornerRadius="5" Height="Auto" d:DesignWidth="139" d:DesignHeight="40" Padding="5,3" Margin="3,0,3,3">
<Border.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="White" Offset="0"/>
<GradientStop Color="LightGray" Offset="1"/>
</LinearGradientBrush>
</Border.Background>
<Grid HorizontalAlignment="Left" Width="Auto">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.79*"/>
<ColumnDefinition Width="0.21*"/>
</Grid.ColumnDefinitions>
<TextBlock TextWrapping="NoWrap" Grid.Column="0" Text="{Binding}" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="5,0,0,0" FontWeight="Bold"/>
<Button x:Name="DelBtn" Grid.Column="1" ClickMode="Press" Width="15" Visibility="Visible" HorizontalAlignment="Center" Margin="1" Padding="1,1.5,1,1.5"
Template="{StaticResource CloseButton}"
Style="{StaticResource BtnStyle}">
<Button.Content>
<Path x:Name="Cross" Data="M0,0 L1,1 M0,1 L1,0" Stretch="Fill" Stroke="Black" StrokeThickness="2" Width="8" Height="8" />
</Button.Content>
</Button>
</Grid>
</Border>
<DataTemplate.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard Storyboard="{StaticResource OnLoaded1}"/>
</EventTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</Window.Resources>
<Grid Margin="25">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="Try to type @ or . in the following rich text box:"/>
<Grid Grid.Row="1">
<rabbit:RichTextBoxEx Name="richTextBoxEx1" TokenTemplate="{DynamicResource NameTokenTemplate}"
AutoAddWhiteSpaceAfterTriggered="{Binding IsChecked,ElementName=chkAutoAddWhitespace}"
ContentAssistTriggers="{Binding ContentAssistTriggers}"
ContentAssistSource="{Binding ContentAssistSource}"/>
</Grid>
<CheckBox x:Name="chkAutoAddWhitespace" Grid.Row="2" Content="AutoAdd White Space After Trigger" IsChecked="True" />
</Grid>
</Window>
ここにメインウィンドウのコードビハインド
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace RichTextBoxWithIntellisense
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public List<String> ContentAssistSource
{
get { return (List<String>)GetValue(ContentAssistSourceProperty); }
set { SetValue(ContentAssistSourceProperty, value); }
}
// Using a DependencyProperty as the backing store for ContentAssisteSource. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ContentAssistSourceProperty =
DependencyProperty.Register("ContentAssistSource", typeof(List<String>), typeof(MainWindow), new UIPropertyMetadata(new List<string>()));
public List<char> ContentAssistTriggers
{
get { return (List<char>)GetValue(ContentAssistTriggersProperty); }
set { SetValue(ContentAssistTriggersProperty, value); }
}
// Using a DependencyProperty as the backing store for ContentAssistTriggers. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ContentAssistTriggersProperty =
DependencyProperty.Register("ContentAssistTriggers", typeof(List<char>), typeof(MainWindow), new UIPropertyMetadata(new List<char>()));
public MainWindow()
{
InitializeComponent();
InitRichTextBoxSource();
InitRichTextBoxIntellisenseTrigger();
Loaded += delegate
{
richTextBoxEx1.Focus();
};
//richTextBoxEx1.Focus();
DataContext = this;
richTextBoxEx1.TokenMatcher = text =>
{
if (text.EndsWith(";"))
{
// Remove the ';'
return text.Substring(0, text.Length - 1).Trim().ToUpper();
}
return null;
};
}
private void InitRichTextBoxIntellisenseTrigger()
{
ContentAssistTriggers.Add('@');
ContentAssistTriggers.Add('.');
}
private void InitRichTextBoxSource()
{
ContentAssistSource.Add("aaal");
ContentAssistSource.Add("as");
ContentAssistSource.Add("aacp");
ContentAssistSource.Add("aid");
ContentAssistSource.Add("asap");
ContentAssistSource.Add("boy");
ContentAssistSource.Add("big");
ContentAssistSource.Add("before");
ContentAssistSource.Add("belong");
ContentAssistSource.Add("can");
ContentAssistSource.Add("clever");
ContentAssistSource.Add("cool");
ContentAssistSource.Add("data");
ContentAssistSource.Add("delete");
}
private void Button_Click(object sender, RoutedEventArgs e)
{
// Debug not comin here
}
}
}