none
TextBoxで行数を制限したい RRS feed

  • 質問

  • TextBoxで行の最大数は制限できないでしょうか?

    いまアプリでTextBoxを使って複数行の入力をさせていますが、

    テキスト入力したものを保存した後に、そのままの大きさで印刷します。

    印刷する枠は大きさが限られていますので、テキストボックスもそれに合わせて大きさを固定しているのですが、

    テキストボックスはスクロールバーを非表示にしてもマウスホイールスクロールできてしまい、

    想定以上に長く文章を書かれてしまいます。

    文章を打つ人と印刷する人が違う人なので、文章を打つ人は印刷プレビューを確認せずに保存して満足してしまい、

    印刷するときに問題が発覚しています。

    書く長さに制限をかけたくて下記の試行錯誤をしてみましたがどれもぴったりくるものがありません。

    ①文字数で制限をかける

      改行があるため、想定以上の行数を打ててしまいます。

    ②Enterの数で制限をかける

      長い文章の折り返しがあるため制限以上に打ててしまいます。

    ③MaxLinesを設定する

      行数を制限するためのプロパティではないようで制限はかかりませんでした。

    ④ScrollViewer.CanContentScroll="False"を設定する

      想定以上に改行できてもスクロールできずに見えなければと思ったのですが、

      スクロールできなくするプロパティではないようでした。

    ⑤印刷するときにViewBoxを使って縮小させる

      少ない文字では想定外に大きな文字に拡大される、改行が思ったようにされない、不自然に縦長横長になる

      など思ったように縮小できませんでした。

    TextBoxで行数を制限できれば万事解決なのですが、何か良い方法はないものでしょうか?

    2015年9月30日 11:27

回答

  • こんにちは。

    LineCountプロパティから判断してはどうでしょうか。
    折り返しを考慮した行数で判断できそうですし。

    TextBox.LineCount プロパティ

    <TextBox Name="txt" Margin="5" AcceptsReturn="True" TextWrapping="Wrap" KeyDown="txt_KeyDown" />
    private void txt_KeyDown(object sender, KeyEventArgs e)
    {
        e.Handled = txt.LineCount > 4;
    }
    
    とりあえずKeyDownでやってしまいましたが、ペーストなど考慮できてませんので

    考慮する必要があります。

    あとはせいぜい添付ビヘイビアにしてやるくらいでしょうか。

    • 回答としてマーク dandan369 2015年10月1日 3:52
    2015年9月30日 15:24
    モデレータ
  • LineCount等は良いアイディアだとは思うのですが、この手の問題は、コピーして貼り付けられるという抜け道が発生する場合がありますので、それに対応することを忘れないようにしなければなりません。この対応を、どのタイミングでどのように行うかはいろいろ検討の余地があるとは思いますが、とりあえず簡単に思い付いたのはエラーチェックです。少し試したところ、以下で最初の文字の位置と最後の文字の位置が取れますから、その差がTextBoxの高さを超えていたらエラーにできるのではないかと思いました。

    textBox.GetRectFromCharacterIndex(0).Bottom;
    textBox.GetRectFromCharacterIndex(textBox.Text.Length).Bottom;

    #上記ではBottomプロパティを使っていますが、差が取れれば良いのでBottomじゃなくてもOKです。


    ★良い回答には回答済みマークを付けよう! MVP - .NET  http://d.hatena.ne.jp/trapemiya/


    • 編集済み trapemiyaModerator 2015年10月1日 2:16 コードの誤字修正
    • 回答としてマーク dandan369 2015年10月1日 3:52
    2015年10月1日 2:15
    モデレータ

すべての返信

  • こんにちは。

    LineCountプロパティから判断してはどうでしょうか。
    折り返しを考慮した行数で判断できそうですし。

    TextBox.LineCount プロパティ

    <TextBox Name="txt" Margin="5" AcceptsReturn="True" TextWrapping="Wrap" KeyDown="txt_KeyDown" />
    private void txt_KeyDown(object sender, KeyEventArgs e)
    {
        e.Handled = txt.LineCount > 4;
    }
    
    とりあえずKeyDownでやってしまいましたが、ペーストなど考慮できてませんので

    考慮する必要があります。

    あとはせいぜい添付ビヘイビアにしてやるくらいでしょうか。

    • 回答としてマーク dandan369 2015年10月1日 3:52
    2015年9月30日 15:24
    モデレータ
  • スクロールしないようにテンプレートでScrollViewer無しにしてしまうとか。
    #OSによってフォントや行間が微妙に違ったりするので、行数で制限しても誤差ではみ出る可能性があったりしますが。

    <Window x:Class="WpfApplication7.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Classic" 
            Title="MainWindow" Height="300" Width="300">
        <Window.Resources>
            <Style x:Key="TextBoxStyle1" TargetType="{x:Type TextBox}">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type TextBox}">
                            <Border x:Name="Bd" SnapsToDevicePixels="true"
                                      BorderBrush="{TemplateBinding BorderBrush}"
                                      BorderThickness="{TemplateBinding BorderThickness}"
                                      Background="{TemplateBinding Background}">
                                <!-- https://msdn.microsoft.com/en-us/library/ms753930%28v=vs.85%29.aspxを参考 -->
                                <!--<ScrollViewer x:Name="PART_ContentHost"/>-->
                                <Decorator x:Name="PART_ContentHost" />
                            </Border>
                            <ControlTemplate.Triggers>
                                <Trigger Property="IsEnabled" Value="false">
                                    <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
                                    <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
    
        </Window.Resources>
        <Grid TextElement.FontSize="20">
            <UniformGrid Columns="2">
                <TextBox Width="100" Height="100" AcceptsReturn="True" TextWrapping="Wrap" 
                         Text="{Binding Path=Text,ElementName=t2}"/>
                
                <TextBox Width="100" Height="100" AcceptsReturn="true" TextWrapping="Wrap" x:Name="t2"
                     Style="{DynamicResource TextBoxStyle1}" BorderBrush="Black" BorderThickness="1" 
                     Text="ABCDEFGHIJKLMOPQRSTUVWXYZABCDEFGHIJKLMOPQRSTUVWXYZABCDEFGHIJKLMOPQRSTUVWXYZ" />
            </UniformGrid>
        </Grid>
    </Window>
    #IMEのカーソルが変になってるような…

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

    • 編集済み gekkaMVP 2015年9月30日 15:56
    2015年9月30日 15:55
  • あと、⑤のViewBoxですけど、
    StretchDirectionプロパティを指定してやると縮小のみ、など指定できます。
    ので少ない文字云々は問題ないと思います。

    <Viewbox StretchDirection="DownOnly">
        <!-- -->
    </Viewbox>

    ただ改行位置などによっては、仰るとおり不自然な形になることがあるので
    MeasureOverrideをオーバーライドしてサイズの計算をしてやらなければなりませんが。


    2015年10月1日 0:17
    モデレータ
  • LineCount等は良いアイディアだとは思うのですが、この手の問題は、コピーして貼り付けられるという抜け道が発生する場合がありますので、それに対応することを忘れないようにしなければなりません。この対応を、どのタイミングでどのように行うかはいろいろ検討の余地があるとは思いますが、とりあえず簡単に思い付いたのはエラーチェックです。少し試したところ、以下で最初の文字の位置と最後の文字の位置が取れますから、その差がTextBoxの高さを超えていたらエラーにできるのではないかと思いました。

    textBox.GetRectFromCharacterIndex(0).Bottom;
    textBox.GetRectFromCharacterIndex(textBox.Text.Length).Bottom;

    #上記ではBottomプロパティを使っていますが、差が取れれば良いのでBottomじゃなくてもOKです。


    ★良い回答には回答済みマークを付けよう! MVP - .NET  http://d.hatena.ne.jp/trapemiya/


    • 編集済み trapemiyaModerator 2015年10月1日 2:16 コードの誤字修正
    • 回答としてマーク dandan369 2015年10月1日 3:52
    2015年10月1日 2:15
    モデレータ
  • 皆様、いろいろなアイディアありがとうございます。

    LineCounttextBox.GetRectFromCharacterIndex(0).Bottom;

    を使えば、印刷からはみ出ることを検知することはできそうで、希望が見えてきました。

    textBox.GetRectFromCharacterIndex(0).Bottom は横方向印刷切れを検知することにも

    応用できそうで、とても助かります。


    あとはどのタイミングでチェックするかですが、

    TextChangedイベントでしたら、コピペをした時でもイベントを出してくれるようですので、

    ただテキストは変わってしまいますので、エラーメッセージを出して対応するか、

    WPFのエラーチェックは経験がありませんので、あとは単純に保存時にエラーメッセージで対応するか、

    いろいろ検討してみようと思います。

    2015年10月1日 3:52