トップ回答者
FlowDocumentReader内にあるTableのセルにボタンを表示させ、そのボタンのContentを検索させる方法

質問
-
ご存じの方がいれば考え方を教えていただきたいです。
XAMLにFlowDocumentReaderを利用しております。
FlowDocumentReaderには、標準で検索機能がついており、
内部のTableのParagraphで囲んだテキストを検索させることができます。
ここで、テキストの代わりに、ボタンを表示させ、そのボタンのContextを検索させたいと考えております。
(FlowDocument内で、別のページにジャンプさせるボタンを設置させたいと考えました。)
おそらくカスタマイズが必要ではないかと考え、FlowDocumentReaderのテンプレートを確認すると、
検索ボタンには、以下のようなテンプレートがセットしてあり、ApplicationCommands.Findというコマンドがバインドしてありました。
ApplicationCommandsというのはビルトインコマンドとの説明があったのですが、
このコマンドをカスタマイズすることは可能でしょうか?
もしくは、やりたいことを実現するためには、コマンドを自作する必要があるのでしょうか?
やりたいこと
XAML(メイン画面)
<Grid> <FlowDocumentReader IsPrintEnabled="False" IsTwoPageViewEnabled="False" Template="{StaticResource FlowDocumentReaderControlTemplate1}"> <FlowDocument FontFamily="メイリオ" ColumnWidth="1000"> <Table> <Table.Columns> <TableColumn Width="60"/> <TableColumn Width="120*"/> <TableColumn Width="*"/> </Table.Columns> <TableRowGroup> <TableRow> <TableCell ColumnSpan="4" TextAlignment="Left"> <Paragraph FontSize="16pt">History</Paragraph> </TableCell> </TableRow> <TableRow Background="LightGoldenrodYellow"> <TableCell ColumnSpan="4"> <Paragraph Margin="2,0,0,0">2013/10/28</Paragraph> </TableCell> </TableRow> <TableRow> <TableCell> <Paragraph FontSize="13" Margin="5,0,0,0">13:52</Paragraph> </TableCell> <TableCell> <Paragraph FontSize="13" Foreground="Blue"> A <Button Content="A" Margin="-5,0,0,0" Cursor="Hand"/> </Paragraph> </TableCell> <TableCell> </TableCell> </TableRow> <TableRow Background="lightgray"> <TableCell> <Paragraph FontSize="13" Margin="5,0,0,0">13:54</Paragraph> </TableCell> <TableCell> <Paragraph FontSize="13">B</Paragraph> </TableCell> <TableCell> <Paragraph></Paragraph> </TableCell> </TableRow> <TableRow> <TableCell> <Paragraph FontSize="13" Margin="5,0,0,0">13:56</Paragraph> </TableCell> <TableCell> <Paragraph FontSize="13">C</Paragraph> </TableCell> <TableCell> <Paragraph></Paragraph> </TableCell> </TableRow> </TableRowGroup> </Table> </FlowDocument> </FlowDocumentReader> </Grid>
XAMLの(FlowDocumentのテンプレートから検索ボタンのところを抜粋)
<StackPanel Grid.Column="0" Orientation="Horizontal" Uid="StackPanel_5"> <ToggleButton x:Name="FindButton" Command="ApplicationCommands.Find" Focusable="False" Margin="3,0" ToolTip="検索" Uid="ToggleButton_1"> <ToggleButton.Style> <Style TargetType="{x:Type ToggleButton}"> <Style.BasedOn> <Style TargetType="{x:Type ButtonBase}"> <Setter Property="Focusable" Value="False"/> <Setter Property="Opacity" Value="0.5"/> <Setter Property="Cursor" Value="Hand"/> <Setter Property="Background" Value="Transparent"/> <Setter Property="Padding" Value="3,1"/> <Setter Property="BorderBrush" Value="{x:Null}"/> <Setter Property="BorderThickness" Value="0"/> <Setter Property="MinWidth" Value="0"/> <Setter Property="MinHeight" Value="0"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ButtonBase}"> <Border Background="{TemplateBinding Background}" Uid="Border_39"> <ContentPresenter x:Name="Content" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" RenderTransformOrigin="0.5,0.5" Uid="ContentPresenter_2"/> </Border> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="RenderTransform" TargetName="Content"> <Setter.Value> <ScaleTransform ScaleY="1.1" ScaleX="1.1"/> </Setter.Value> </Setter> </Trigger> <Trigger Property="IsPressed" Value="True"> <Setter Property="RenderTransform" TargetName="Content"> <Setter.Value> <ScaleTransform ScaleY="0.9" ScaleX="0.9"/> </Setter.Value> </Setter> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> <Style.Triggers> <Trigger Property="IsEnabled" Value="False"> <Setter Property="Opacity" Value="0.3"/> </Trigger> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Opacity" Value="1"/> </Trigger> </Style.Triggers> </Style> </Style.BasedOn> <Style.Triggers> <Trigger Property="IsEnabled" Value="False"> <Setter Property="Visibility" Value="Collapsed"/> </Trigger> <Trigger Property="IsChecked" Value="True"> <Setter Property="Opacity" Value="1"/> </Trigger> </Style.Triggers> </Style> </ToggleButton.Style> <Path Stroke="{TemplateBinding Foreground}" Uid="Path_23" VerticalAlignment="Center"> <Path.Data> <GeometryGroup> <RectangleGeometry RadiusY="1" RadiusX="1" Rect="0.5,0.5,19,19"/> <EllipseGeometry Center="12,8" RadiusY="5" RadiusX="5"/> <EllipseGeometry Center="12,8" RadiusY="4" RadiusX="4"/> <LineGeometry EndPoint="9,10" StartPoint="2.5,16.5"/> <LineGeometry EndPoint="9.5,10.5" StartPoint="3,17"/> <LineGeometry EndPoint="10,11" StartPoint="3.5,17.5"/> </GeometryGroup> </Path.Data> </Path> </ToggleButton> <Border x:Name="PART_FindToolBarHost" HorizontalAlignment="Left" Uid="Border_64" Visibility="Collapsed" VerticalAlignment="Center"/> </StackPanel>
- 編集済み sasagaki 2013年12月3日 0:38
回答
-
こんな。
FlowDocumentReaderにおいてApplicationCommands.Findは検索文字入力のTextBoxの表示非表示を切り替えてるだけで、検索機能を実装しているわけではないようだ。
Findコマンドを捕まえてもあまり意味がなく、直接検索処理すれば済むことです。<Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="200" Width="525"> <Grid> <Frame NavigationUIVisibility="Hidden" PreviewKeyDown="Frame_PreviewKeyDown" Navigating="Frame_Navigating"> <UIElement.CommandBindings> <CommandBinding Command="NavigationCommands.Search" CanExecute="SearchCommandBinding_CanExecute" Executed="SearchCommandBinding_Executed"/> </UIElement.CommandBindings> <Frame.Content> <FlowDocumentReader x:Name="fdreader" IsPrintEnabled="False" IsTwoPageViewEnabled="False"> <FlowDocument FontFamily="メイリオ" ColumnWidth="1000"> <Table> <Table.Columns> <TableColumn Width="60"/><TableColumn Width="120*"/><TableColumn Width="*"/> </Table.Columns> <TableRowGroup> <TableRow> <TableCell ColumnSpan="4" TextAlignment="Left"><Paragraph FontSize="16pt">History</Paragraph></TableCell> </TableRow> <TableRow Background="LightGoldenrodYellow"> <TableCell ColumnSpan="4"><Paragraph Margin="2,0,0,0">2013/10/28</Paragraph></TableCell> </TableRow> <TableRow> <TableCell><Paragraph FontSize="13" Margin="5,0,0,0">13:52</Paragraph></TableCell> <TableCell> <Paragraph FontSize="13" Foreground="Blue"> A <Hyperlink NavigateUri="#T1400" Background="LightBlue" > A1 </Hyperlink> <Hyperlink NavigateUri="Page1.xaml" Background="LightPink" >A</Hyperlink> <Button Content="A2" Command="{Binding Commands[Test]}" /> </Paragraph> </TableCell> <TableCell></TableCell> </TableRow> <TableRow ><TableCell/></TableRow> <TableRow ><TableCell/></TableRow> <TableRow ><TableCell/></TableRow> <TableRow ><TableCell/></TableRow> <TableRow ><TableCell/></TableRow> <TableRow ><TableCell/></TableRow> <TableRow Background="lightgray"> <TableCell><Paragraph FontSize="13" Margin="5,0,0,0">13:54</Paragraph></TableCell> <TableCell><Paragraph FontSize="13">B</Paragraph></TableCell> <TableCell><Paragraph/></TableCell> </TableRow> <TableRow> <TableCell><Paragraph FontSize="13" Margin="5,0,0,0" Name="T1356">13:56</Paragraph></TableCell> <TableCell><Paragraph FontSize="13">C</Paragraph></TableCell> <TableCell><Paragraph/></TableCell> </TableRow> <TableRow><TableCell><Paragraph FontSize="13" Margin="5,0,0,0">13:58</Paragraph></TableCell></TableRow> <TableRow> <TableCell><Paragraph FontSize="13" Margin="5,0,0,0" Name="T1400">14:00</Paragraph></TableCell> <TableCell> <Paragraph>A</Paragraph></TableCell></TableRow> <TableRow><TableCell><Paragraph FontSize="13" Margin="5,0,0,0">14:02</Paragraph> </TableCell> </TableRow> <TableRow><TableCell><Paragraph FontSize="13" Margin="5,0,0,0">14:04</Paragraph></TableCell></TableRow> <TableRow><TableCell><Paragraph FontSize="13" Margin="5,0,0,0">14:06</Paragraph></TableCell></TableRow> </TableRowGroup> </Table> </FlowDocument> </FlowDocumentReader> </Frame.Content> </Frame> </Grid> </Window>
using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Navigation; namespace WpfApplication1 { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void Frame_PreviewKeyDown(object sender, KeyEventArgs e) { if ((OriginalSourceIsFindTextBox(e) && e.Key == Key.Enter) || e.Key == Key.F3) { //Searchコマンドを発行する必要はないけど試しに使ってみる NavigationCommands.Search.Execute(string.Empty, Keyboard.FocusedElement); //Search(); e.Handled = true; } } private TextBox findTextBox = null; //検索文字入力用のTextBox private FrameworkElement findToolBarHost = null;//検索文字入力用のコントロール /// <summary>キー入力されたTextBoxが検索文字入力用か調べる</summary> /// <param name="e"></param> /// <returns></returns> private bool OriginalSourceIsFindTextBox(KeyEventArgs e) { TextBox txb = e.OriginalSource as TextBox; if (txb != null && txb.Name == "FindTextBox") { if (txb == findTextBox) { return true; } DependencyObject d = txb; do { FrameworkElement fe = d as FrameworkElement; if (fe != null && fe.Name == "PART_FindToolBarHost") { findToolBarHost = fe; findTextBox = txb; return true; } d = VisualTreeHelper.GetParent(d); } while (d != null); } return false; } private void SearchCommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e) { //Searchコマンドが実行可能か e.CanExecute = true; e.Handled = true; } private void SearchCommandBinding_Executed(object sender, ExecutedRoutedEventArgs e) { //Searchコマンドを実行する Search(); } private void Search() { if (findTextBox != null && findTextBox.IsVisible && !string.IsNullOrEmpty(findTextBox.Text)) { //検索用TextBox内でのEnterかF3での検索の場合に検索文字が入力済みなら自前で検索する Search(this.fdreader, findTextBox.Text); } } /// <summary>FlowDocumentReaderでInlineUIContainerの文字列も検索してみる</summary> /// <param name="fdreader"></param> /// <param name="find"></param> private static void Search(FlowDocumentReader fdreader, string find) { if (string.IsNullOrEmpty(find)) { return; } FlowDocument doc = fdreader.Document; TextPointer pointer = fdreader.Selection.End; while (pointer != null) { var context = pointer.GetPointerContext(LogicalDirection.Forward); switch (context) { case TextPointerContext.EmbeddedElement: var container = pointer.Parent as InlineUIContainer; if (container != null) { var cont = container.Child as ContentControl;//ContentControlのみを検索対象にする string s = cont.Content as string; if (s != null && s.Contains(find)) { fdreader.Selection.Select(pointer, pointer.GetNextContextPosition(LogicalDirection.Forward)); //cont.Focus();//検索続行できなくなるよ return; } } pointer = pointer.GetNextContextPosition(LogicalDirection.Forward); break; case TextPointerContext.Text: string text = pointer.GetTextInRun(LogicalDirection.Forward); int index = text.IndexOf(find); if (index >= 0) { var endpointer = pointer.GetPositionAtOffset(find.Length); //fdreader.Focus(); fdreader.Selection.Select(pointer, endpointer); if (pointer.Paragraph != null) { //見つかった文字列のParagraphの位置を表示できるなら表示 pointer.Paragraph.BringIntoView(); } pointer = endpointer; return; } else { pointer = pointer.GetPositionAtOffset(text.Length); } break; default: pointer = pointer.GetNextContextPosition(LogicalDirection.Forward); break; } } MessageBox.Show(Application.Current.MainWindow, "最後まで検索しました"); } /// <summary>おまけ。HyperLinkで移動する時</summary> /// <param name="sender"></param> /// <param name="e"></param> private void Frame_Navigating(object sender, NavigatingCancelEventArgs e) { if (e.Uri != null && !e.Uri.OriginalString.StartsWith("#")) {//別ページに移動するときにはFlowDocumentの中身を変えてみる //同一Document内の移動(#)は何もしないでFrameに任せる e.Cancel = true; FlowDocument doc = new FlowDocument(); Section sec = new Section(); Paragraph p = new Paragraph(); p.Inlines.Add(new Run("404 Page not found")); sec.Blocks.Add(p); doc.Blocks.Add(sec); fdreader.Document = doc; } } } }
試しにNavigationCommands.Searchを使う実装をしていますが、FlowDocumentReaderがF3キーを使ってしまっていて CommandBindingsがうまく動かないので、PreviewKeyDownでSearchコマンドを発行するという変なやり方になってます。
#別ページへのジャンプならHyperLinkの方が素直な実装な気がする。
個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)
- 回答としてマーク sasagaki 2013年12月5日 5:39
すべての返信
-
こんな。
FlowDocumentReaderにおいてApplicationCommands.Findは検索文字入力のTextBoxの表示非表示を切り替えてるだけで、検索機能を実装しているわけではないようだ。
Findコマンドを捕まえてもあまり意味がなく、直接検索処理すれば済むことです。<Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="200" Width="525"> <Grid> <Frame NavigationUIVisibility="Hidden" PreviewKeyDown="Frame_PreviewKeyDown" Navigating="Frame_Navigating"> <UIElement.CommandBindings> <CommandBinding Command="NavigationCommands.Search" CanExecute="SearchCommandBinding_CanExecute" Executed="SearchCommandBinding_Executed"/> </UIElement.CommandBindings> <Frame.Content> <FlowDocumentReader x:Name="fdreader" IsPrintEnabled="False" IsTwoPageViewEnabled="False"> <FlowDocument FontFamily="メイリオ" ColumnWidth="1000"> <Table> <Table.Columns> <TableColumn Width="60"/><TableColumn Width="120*"/><TableColumn Width="*"/> </Table.Columns> <TableRowGroup> <TableRow> <TableCell ColumnSpan="4" TextAlignment="Left"><Paragraph FontSize="16pt">History</Paragraph></TableCell> </TableRow> <TableRow Background="LightGoldenrodYellow"> <TableCell ColumnSpan="4"><Paragraph Margin="2,0,0,0">2013/10/28</Paragraph></TableCell> </TableRow> <TableRow> <TableCell><Paragraph FontSize="13" Margin="5,0,0,0">13:52</Paragraph></TableCell> <TableCell> <Paragraph FontSize="13" Foreground="Blue"> A <Hyperlink NavigateUri="#T1400" Background="LightBlue" > A1 </Hyperlink> <Hyperlink NavigateUri="Page1.xaml" Background="LightPink" >A</Hyperlink> <Button Content="A2" Command="{Binding Commands[Test]}" /> </Paragraph> </TableCell> <TableCell></TableCell> </TableRow> <TableRow ><TableCell/></TableRow> <TableRow ><TableCell/></TableRow> <TableRow ><TableCell/></TableRow> <TableRow ><TableCell/></TableRow> <TableRow ><TableCell/></TableRow> <TableRow ><TableCell/></TableRow> <TableRow Background="lightgray"> <TableCell><Paragraph FontSize="13" Margin="5,0,0,0">13:54</Paragraph></TableCell> <TableCell><Paragraph FontSize="13">B</Paragraph></TableCell> <TableCell><Paragraph/></TableCell> </TableRow> <TableRow> <TableCell><Paragraph FontSize="13" Margin="5,0,0,0" Name="T1356">13:56</Paragraph></TableCell> <TableCell><Paragraph FontSize="13">C</Paragraph></TableCell> <TableCell><Paragraph/></TableCell> </TableRow> <TableRow><TableCell><Paragraph FontSize="13" Margin="5,0,0,0">13:58</Paragraph></TableCell></TableRow> <TableRow> <TableCell><Paragraph FontSize="13" Margin="5,0,0,0" Name="T1400">14:00</Paragraph></TableCell> <TableCell> <Paragraph>A</Paragraph></TableCell></TableRow> <TableRow><TableCell><Paragraph FontSize="13" Margin="5,0,0,0">14:02</Paragraph> </TableCell> </TableRow> <TableRow><TableCell><Paragraph FontSize="13" Margin="5,0,0,0">14:04</Paragraph></TableCell></TableRow> <TableRow><TableCell><Paragraph FontSize="13" Margin="5,0,0,0">14:06</Paragraph></TableCell></TableRow> </TableRowGroup> </Table> </FlowDocument> </FlowDocumentReader> </Frame.Content> </Frame> </Grid> </Window>
using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Navigation; namespace WpfApplication1 { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void Frame_PreviewKeyDown(object sender, KeyEventArgs e) { if ((OriginalSourceIsFindTextBox(e) && e.Key == Key.Enter) || e.Key == Key.F3) { //Searchコマンドを発行する必要はないけど試しに使ってみる NavigationCommands.Search.Execute(string.Empty, Keyboard.FocusedElement); //Search(); e.Handled = true; } } private TextBox findTextBox = null; //検索文字入力用のTextBox private FrameworkElement findToolBarHost = null;//検索文字入力用のコントロール /// <summary>キー入力されたTextBoxが検索文字入力用か調べる</summary> /// <param name="e"></param> /// <returns></returns> private bool OriginalSourceIsFindTextBox(KeyEventArgs e) { TextBox txb = e.OriginalSource as TextBox; if (txb != null && txb.Name == "FindTextBox") { if (txb == findTextBox) { return true; } DependencyObject d = txb; do { FrameworkElement fe = d as FrameworkElement; if (fe != null && fe.Name == "PART_FindToolBarHost") { findToolBarHost = fe; findTextBox = txb; return true; } d = VisualTreeHelper.GetParent(d); } while (d != null); } return false; } private void SearchCommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e) { //Searchコマンドが実行可能か e.CanExecute = true; e.Handled = true; } private void SearchCommandBinding_Executed(object sender, ExecutedRoutedEventArgs e) { //Searchコマンドを実行する Search(); } private void Search() { if (findTextBox != null && findTextBox.IsVisible && !string.IsNullOrEmpty(findTextBox.Text)) { //検索用TextBox内でのEnterかF3での検索の場合に検索文字が入力済みなら自前で検索する Search(this.fdreader, findTextBox.Text); } } /// <summary>FlowDocumentReaderでInlineUIContainerの文字列も検索してみる</summary> /// <param name="fdreader"></param> /// <param name="find"></param> private static void Search(FlowDocumentReader fdreader, string find) { if (string.IsNullOrEmpty(find)) { return; } FlowDocument doc = fdreader.Document; TextPointer pointer = fdreader.Selection.End; while (pointer != null) { var context = pointer.GetPointerContext(LogicalDirection.Forward); switch (context) { case TextPointerContext.EmbeddedElement: var container = pointer.Parent as InlineUIContainer; if (container != null) { var cont = container.Child as ContentControl;//ContentControlのみを検索対象にする string s = cont.Content as string; if (s != null && s.Contains(find)) { fdreader.Selection.Select(pointer, pointer.GetNextContextPosition(LogicalDirection.Forward)); //cont.Focus();//検索続行できなくなるよ return; } } pointer = pointer.GetNextContextPosition(LogicalDirection.Forward); break; case TextPointerContext.Text: string text = pointer.GetTextInRun(LogicalDirection.Forward); int index = text.IndexOf(find); if (index >= 0) { var endpointer = pointer.GetPositionAtOffset(find.Length); //fdreader.Focus(); fdreader.Selection.Select(pointer, endpointer); if (pointer.Paragraph != null) { //見つかった文字列のParagraphの位置を表示できるなら表示 pointer.Paragraph.BringIntoView(); } pointer = endpointer; return; } else { pointer = pointer.GetPositionAtOffset(text.Length); } break; default: pointer = pointer.GetNextContextPosition(LogicalDirection.Forward); break; } } MessageBox.Show(Application.Current.MainWindow, "最後まで検索しました"); } /// <summary>おまけ。HyperLinkで移動する時</summary> /// <param name="sender"></param> /// <param name="e"></param> private void Frame_Navigating(object sender, NavigatingCancelEventArgs e) { if (e.Uri != null && !e.Uri.OriginalString.StartsWith("#")) {//別ページに移動するときにはFlowDocumentの中身を変えてみる //同一Document内の移動(#)は何もしないでFrameに任せる e.Cancel = true; FlowDocument doc = new FlowDocument(); Section sec = new Section(); Paragraph p = new Paragraph(); p.Inlines.Add(new Run("404 Page not found")); sec.Blocks.Add(p); doc.Blocks.Add(sec); fdreader.Document = doc; } } } }
試しにNavigationCommands.Searchを使う実装をしていますが、FlowDocumentReaderがF3キーを使ってしまっていて CommandBindingsがうまく動かないので、PreviewKeyDownでSearchコマンドを発行するという変なやり方になってます。
#別ページへのジャンプならHyperLinkの方が素直な実装な気がする。
個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)
- 回答としてマーク sasagaki 2013年12月5日 5:39
-