none
タッチによるテキストの選択について RRS feed

  • 質問

  • こんにちは。

    WPFを利用したデスクトップアプリケーションを開発しています。

    タッチ操作によるテキストの選択についてストアアプリは、マイクロソフトが提供するデスクトップアプリでは、タッチ操作にてテキストを選択する際に、

    長くタッチすることでWordを選択でき、そして、選択範囲を調整するためのガイドバー(?)が表示されると思います。

    この機能を、WPFを利用したデスクトップアプリケーションで実現するためには、どのようにすればよいかご存知でしょうか?

    よろしくお願いします。

    2013年12月6日 15:39

回答

  • グリッパーを作るなら、こんなかんじ。
    #こまかいことを実装してる時間がないのでマウスでグリッパーの処理のみです。(本当はAdornerでやるのが理想)

    <UserControl x:Class="WpfApplication1.AnchorCaretTextBox"
                 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" 
                 d:DesignHeight="300" d:DesignWidth="300">
        <UserControl.Resources>
            <Style TargetType="Thumb" x:Key="anchor">
                <EventSetter Event="DragDelta" Handler="Thumb_DragDelta" />
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="Thumb">
                            <Grid Canvas.Left="100" Canvas.Top="100">
                                <Grid.RowDefinitions>
                                    <RowDefinition />
                                    <RowDefinition />
                                </Grid.RowDefinitions>
                                <Rectangle Width="2" Height="{Binding FontSize,ElementName=txb}" Fill="Black" />
                                <Ellipse Grid.Row="1" Width="20" Height="20" Stroke="Black" StrokeThickness="2" Fill="White" />
                            </Grid>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </UserControl.Resources>
        <Grid>
            <TextBox Text="AAAAAAAAAAAAA" FontSize="30" DockPanel.Dock="Top"  AcceptsReturn="true"
                         CaretBrush="Transparent"  
                         SelectionChanged="TextBox_SelectionChanged"
                         ScrollViewer.ScrollChanged="TextBox_ScrollChanged"
                         x:Name="txb">
            </TextBox>
            <Canvas x:Name="canvas" Visibility="Visible" >
                <Thumb Style="{StaticResource anchor}" x:Name="thumbA" />
                <Thumb Style="{StaticResource anchor}" x:Name="thumbB"  />
            </Canvas>
        </Grid>
    </UserControl>
    namespace WpfApplication1
    {
        using System.Windows;
        using System.Windows.Controls;
        using System.Windows.Input;
        using System.Windows.Controls.Primitives;
    
        public partial class AnchorCaretTextBox : UserControl
        {
            public AnchorCaretTextBox()
            {
                InitializeComponent();
            }
    
            public override void OnApplyTemplate()
            {
                base.OnApplyTemplate();
                thumbStart = thumbA ;
                thumbEnd = thumbB;
            }
    
            /// <summary>選択範囲が変わったらアンカー用のThumbの位置を移動させる</summary>
            private void TextBox_SelectionChanged(object sender, RoutedEventArgs e)
            {
                TextBox txb = (TextBox)sender;
                UpdateThumbPosition(txb);
            }
    
            /// <summary>スクロールで選択範囲の描画位置が変わったらThumbを移動させる</summary>
            private void TextBox_ScrollChanged(object sender, ScrollChangedEventArgs e)
            {
                UpdateThumbPosition((TextBox)sender);
            }
    
            /// <summary>Thumbを選択範囲に一致させる</summary>
            private void UpdateThumbPosition(TextBox txb)
            {
                    Point pStart = txb.GetRectFromCharacterIndex(txb.SelectionStart).Location;
                    Point pEnd = txb.GetRectFromCharacterIndex(txb.SelectionStart + txb.SelectionLength).Location;
    
                    SetCanvas(thumbStart, pStart, true);
                    SetCanvas(thumbEnd, pEnd, true);
            }
    
            private void SetCanvas(Control ctl, Point p, bool isVisible)
            {
                if (ctl != null)
                {
                    if (isVisible)
                    {
                        ctl.Visibility = System.Windows.Visibility.Visible;
                        if (!double.IsInfinity(p.X))
                        {
                            Canvas.SetLeft(ctl, p.X - ctl.ActualWidth / 2);
                        }
                        if (!double.IsInfinity(p.Y))
                        {
                            Canvas.SetTop(ctl, p.Y);
                        }
                    }
                    else
                    {
                        ctl.Visibility = System.Windows.Visibility.Hidden;
                    }
    
                }
            }
    
            /// <summary>Thumbが移動されたらTextBoxの選択範囲を変更する</summary>
            private void Thumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
            {
                Thumb CaretThumb = (Thumb)sender;
                Thumb anchorThumb;
                var p = Mouse.GetPosition(txb);
                if (!isDragging)
                {
                    isDragging = true;
                    CaretThumb.MouseUp += CaretThumb_MouseUp;
    
                    offset = Mouse.GetPosition(CaretThumb).Y / 2;
                }
                p = new Point(p.X, p.Y - offset);
                
                int caretIndex = txb.GetCharacterIndexFromPoint(p, true);
                int anchorIndex;
                if (CaretThumb == thumbStart)
                {
                    anchorThumb = thumbEnd;
                    anchorIndex = txb.SelectionStart + txb.SelectionLength;
                }
                else
                {
                    anchorThumb = thumbStart;
                    anchorIndex = txb.SelectionStart;
                }
    
                if (caretIndex < anchorIndex)
                {
                    txb.SelectionStart = caretIndex;
                    txb.SelectionLength = anchorIndex - caretIndex;
    
                    thumbStart = CaretThumb;
                    thumbEnd = anchorThumb;
                }
                else
                {
                    txb.SelectionStart = anchorIndex;
                    txb.SelectionLength = caretIndex - anchorIndex;
                    thumbStart = anchorThumb;
                    thumbEnd = CaretThumb;
                }
            }
    
            private void CaretThumb_MouseUp(object sender, MouseButtonEventArgs e)
            {
                ((Thumb)sender).MouseUp -= CaretThumb_MouseUp;
                isDragging = false;
            }
    
            private Thumb thumbStart;
            private Thumb thumbEnd;
            private bool isDragging;
            private double offset;
        }
    }
    

    長押ししたらワード単位に選択範囲を広げる処理とグリッパーの表示を切り替える処理を追加してやってください。
    ワード選択はRichTextBoxならParagraphなどで文字列をある程度は解析できるので、それでワード単位を解析してやるとか。


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答の候補に設定 星 睦美 2013年12月11日 8:17
    • 回答としてマーク 星 睦美 2013年12月16日 7:38
    • 回答としてマークされていない I.TMK 2013年12月25日 6:26
    • 回答としてマーク I.TMK 2013年12月25日 6:26
    2013年12月8日 13:42