แถบเลื่อนแนวตั้งอัตโนมัติใน WPF TextBlock?


336

ฉันมีTextBlockใน WPF ฉันเขียนหลายบรรทัดลงไปเกินความสูงในแนวตั้ง ฉันคาดว่าแถบเลื่อนแนวตั้งจะปรากฏขึ้นโดยอัตโนมัติเมื่อสิ่งนั้นเกิดขึ้น แต่ก็ไม่ได้ ฉันพยายามค้นหาคุณสมบัติแถบเลื่อนในบานหน้าต่างคุณสมบัติ แต่ไม่พบ

ฉันจะสร้างแถบเลื่อนแนวตั้งอัตโนมัติได้อย่างไรTextBlockเมื่อเนื้อหามีความสูงเกิน

การชี้แจง: ฉันอยากทำจากนักออกแบบไม่ใช่เขียนโดยตรงไปที่ XAML


1
เมื่ออ่านคำถามนี้อีกครั้งฉันสังเกตว่าคุณพูดถึงTextBlockสองTextBoxครั้ง
Drew Noakes

คำตอบ:


555

รวมไว้ในตัวแสดงการเลื่อน:

<ScrollViewer>
    <TextBlock />
</ScrollViewer>

หมายเหตุคำตอบนี้ใช้กับTextBlockองค์ประกอบข้อความ (อ่านอย่างเดียว) ตามที่ถามในคำถามเดิม

หากคุณต้องการแสดงแถบเลื่อนในTextBox(องค์ประกอบข้อความที่แก้ไขได้) ให้ใช้ScrollViewerคุณสมบัติที่แนบมา:

<TextBox ScrollViewer.HorizontalScrollBarVisibility="Disabled"
         ScrollViewer.VerticalScrollBarVisibility="Auto" />

ค่าที่ถูกต้องสำหรับทั้งสองมีคุณสมบัติDisabled, Auto, และHiddenVisible


2
ฉันจะทำมันจากนักออกแบบได้อย่างไร
Bab Yogoo

16
ขออภัยฉันไม่แน่ใจว่าฉันไม่ได้ใช้นักออกแบบ WPF ฉันคิดว่าถ้าคุณเพิ่ม XAML โดยตรงผู้ออกแบบจะอัปเดตตัวเอง
Drew Noakes

5
@conqenator TextBox.ScrollToEnd ();
Petey B

2
@ Greg คำถามคือเกี่ยวกับการไม่ได้TextBlock TextBox
Drew Noakes

7
บางครั้งต้องใช้ MaxHeight บน Scrollviewer เพื่อบังคับให้ scoll ปรากฏขึ้นหากองค์ประกอบล้อมรอบไม่บังคับใช้ความสูงใด ๆ
HackerBaloo

106

สามารถใช้สิ่งต่อไปนี้ได้ในขณะนี้:

<TextBox Name="myTextBox" 
         ScrollViewer.HorizontalScrollBarVisibility="Auto"
         ScrollViewer.VerticalScrollBarVisibility="Auto"
         ScrollViewer.CanContentScroll="True">SOME TEXT
</TextBox>

19
@jjnguy ผมตีความคำถามเดิมว่าเป็นเกี่ยวกับการTextBlockไม่ได้TextBox(ในขณะที่ชื่อและเปิดสาย) TextBoxแต่วรรคสองที่กล่าวถึง เพื่อความชัดเจนคำตอบนี้เป็นวิธีที่ดีที่สุดสำหรับกล่องข้อความและของฉันคือสิ่งที่ดีที่สุดที่ฉันรู้จักสำหรับบล็อคข้อความ:)
Drew Noakes

@ ดึง, อา, ทำให้รู้สึก ขอขอบคุณสำหรับการชี้แจง.
jjnguy

2
ทำงานได้ดีขึ้นสำหรับฉันเช่นกัน อย่างน้อย TextBox เมื่อใช้ ScrollViewer รอบ ๆ เช่นในคำตอบที่ยอมรับขอบเขตของกล่องข้อความจะหายไปเนื่องจากตัวควบคุมทั้งหมดถูกเลื่อนและไม่เพียง แต่เนื้อหาเท่านั้น
เติมเชื้อเพลิง

20

สิ่งที่ดีกว่าจะเป็น:

<Grid Width="Your-specified-value" >
    <ScrollViewer>
         <TextBlock Width="Auto" TextWrapping="Wrap" />
    </ScrollViewer>
</Grid>

สิ่งนี้ทำให้แน่ใจว่าข้อความใน textblock ของคุณไม่ล้นและซ้อนทับองค์ประกอบด้านล่าง textblock อย่างเช่นในกรณีที่คุณไม่ได้ใช้กริด ที่เกิดขึ้นกับฉันเมื่อฉันพยายามแก้ไขปัญหาอื่น ๆ แม้ว่า textblock นั้นมีกริดที่มีองค์ประกอบอื่นอยู่แล้ว โปรดทราบว่าความกว้างของบล็อกข้อความควรเป็นอัตโนมัติและคุณควรระบุสิ่งที่ต้องการด้วยในองค์ประกอบกริด ฉันทำสิ่งนี้ในรหัสของฉันและมันทำงานได้อย่างสวยงาม HTH


7
<ScrollViewer Height="239" VerticalScrollBarVisibility="Auto">
    <TextBox AcceptsReturn="True" TextWrapping="Wrap" LineHeight="10" />
</ScrollViewer>

นี่คือวิธีใช้กล่องข้อความเลื่อนใน XAML และใช้เป็นพื้นที่ข้อความ


1
คำถามที่เกี่ยวข้องกับการไม่ได้TextBlock TextBox
Afzaal Ahmad Zeeshan

ตอบไม่ค่อยถูกต้อง แต่ฉันพบว่า VerticalScrollBarVisibility เป็นคำใบ้ที่มีประโยชน์ดังนั้น +1
Malachi

4

คำตอบนี้อธิบายถึงวิธีการแก้ปัญหาโดยใช้ MVVM

โซลูชันนี้ยอดเยี่ยมถ้าคุณต้องการเพิ่มกล่องบันทึกไปยังหน้าต่างซึ่งจะเลื่อนไปที่ด้านล่างโดยอัตโนมัติทุกครั้งที่มีการเพิ่มข้อความบันทึกใหม่

เมื่อเพิ่มคุณสมบัติที่แนบมาเหล่านี้แล้วสามารถนำกลับมาใช้ใหม่ได้ทุกที่ดังนั้นจึงสร้างซอฟต์แวร์แบบแยกส่วนและใช้ซ้ำได้

เพิ่ม XAML นี้:

<TextBox IsReadOnly="True"   
         Foreground="Gainsboro"                           
         FontSize="13" 
         ScrollViewer.HorizontalScrollBarVisibility="Auto"
         ScrollViewer.VerticalScrollBarVisibility="Auto"
         ScrollViewer.CanContentScroll="True"
         attachedBehaviors:TextBoxApppendBehaviors.AppendText="{Binding LogBoxViewModel.AttachedPropertyAppend}"                                       
         attachedBehaviors:TextBoxClearBehavior.TextBoxClear="{Binding LogBoxViewModel.AttachedPropertyClear}"                                    
         TextWrapping="Wrap">

เพิ่มคุณสมบัติที่แนบมานี้:

public static class TextBoxApppendBehaviors
{
    #region AppendText Attached Property
    public static readonly DependencyProperty AppendTextProperty =
        DependencyProperty.RegisterAttached(
            "AppendText",
            typeof (string),
            typeof (TextBoxApppendBehaviors),
            new UIPropertyMetadata(null, OnAppendTextChanged));

    public static string GetAppendText(TextBox textBox)
    {
        return (string)textBox.GetValue(AppendTextProperty);
    }

    public static void SetAppendText(
        TextBox textBox,
        string value)
    {
        textBox.SetValue(AppendTextProperty, value);
    }

    private static void OnAppendTextChanged(
        DependencyObject d,
        DependencyPropertyChangedEventArgs args)
    {
        if (args.NewValue == null)
        {
            return;
        }

        string toAppend = args.NewValue.ToString();

        if (toAppend == "")
        {
            return;
        }

        TextBox textBox = d as TextBox;
        textBox?.AppendText(toAppend);
        textBox?.ScrollToEnd();
    }
    #endregion
}

และสิ่งที่แนบมานี้ (เพื่อล้างกล่อง):

public static class TextBoxClearBehavior
{
    public static readonly DependencyProperty TextBoxClearProperty =
        DependencyProperty.RegisterAttached(
            "TextBoxClear",
            typeof(bool),
            typeof(TextBoxClearBehavior),
            new UIPropertyMetadata(false, OnTextBoxClearPropertyChanged));

    public static bool GetTextBoxClear(DependencyObject obj)
    {
        return (bool)obj.GetValue(TextBoxClearProperty);
    }

    public static void SetTextBoxClear(DependencyObject obj, bool value)
    {
        obj.SetValue(TextBoxClearProperty, value);
    }

    private static void OnTextBoxClearPropertyChanged(
        DependencyObject d,
        DependencyPropertyChangedEventArgs args)
    {
        if ((bool)args.NewValue == false)
        {
            return;
        }

        var textBox = (TextBox)d;
        textBox?.Clear();
    }
}   

จากนั้นหากคุณใช้เฟรมเวิร์กการฉีดพึ่งพาเช่น MEF คุณสามารถวางรหัสเฉพาะบันทึกทั้งหมดลงใน ViewModel ของตัวเอง:

public interface ILogBoxViewModel
{
    void CmdAppend(string toAppend);
    void CmdClear();

    bool AttachedPropertyClear { get; set; }

    string AttachedPropertyAppend { get; set; }
}

[Export(typeof(ILogBoxViewModel))]
public class LogBoxViewModel : ILogBoxViewModel, INotifyPropertyChanged
{
    private readonly ILog _log = LogManager.GetLogger<LogBoxViewModel>();

    private bool _attachedPropertyClear;
    private string _attachedPropertyAppend;

    public void CmdAppend(string toAppend)
    {
        string toLog = $"{DateTime.Now:HH:mm:ss} - {toAppend}\n";

        // Attached properties only fire on a change. This means it will still work if we publish the same message twice.
        AttachedPropertyAppend = "";
        AttachedPropertyAppend = toLog;

        _log.Info($"Appended to log box: {toAppend}.");
    }

    public void CmdClear()
    {
        AttachedPropertyClear = false;
        AttachedPropertyClear = true;

        _log.Info($"Cleared the GUI log box.");
    }

    public bool AttachedPropertyClear
    {
        get { return _attachedPropertyClear; }
        set { _attachedPropertyClear = value; OnPropertyChanged(); }
    }

    public string AttachedPropertyAppend
    {
        get { return _attachedPropertyAppend; }
        set { _attachedPropertyAppend = value; OnPropertyChanged(); }
    }

    #region INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion
}

นี่คือวิธีการทำงาน:

  • ViewModel สลับคุณสมบัติที่แนบมาเพื่อควบคุมกล่องข้อความ
  • ขณะที่ใช้งาน "ผนวก" มันเร็วมาก
  • ViewModel อื่น ๆ สามารถสร้างข้อความบันทึกโดยเรียกวิธีการในการบันทึก ViewModel
  • เมื่อเราใช้ ScrollViewer ในกล่องข้อความเราสามารถทำให้มันเลื่อนไปที่ด้านล่างของกล่องข้อความโดยอัตโนมัติทุกครั้งที่มีการเพิ่มข้อความใหม่

4
<ScrollViewer MaxHeight="50"  
              Width="Auto" 
              HorizontalScrollBarVisibility="Disabled"
              VerticalScrollBarVisibility="Auto">
     <TextBlock Text="{Binding Path=}" 
                Style="{StaticResource TextStyle_Data}" 
                TextWrapping="Wrap" />
</ScrollViewer>

ฉันกำลังทำสิ่งนี้ในอีกทางหนึ่งโดยใส่ MaxHeight ใน ScrollViewer

เพียงปรับ MaxHeight เพื่อแสดงบรรทัดข้อความมากขึ้นหรือน้อยลง ง่าย.



1

ฉันพยายามรับข้อเสนอแนะเหล่านี้ให้ทำงานกับ textblock แต่ไม่สามารถใช้งานได้ ฉันยังพยายามทำให้มันใช้งานได้จากผู้ออกแบบ (ดูในเลย์เอาต์และขยายรายการโดยคลิกที่ลูกศรชี้ลง "V" ที่ด้านล่าง) ฉันลองตั้งค่า scrollviewer เป็นVisibleจากนั้นอัตโนมัติแต่ก็ยังใช้งานไม่ได้

ในที่สุดฉันก็ยอมแพ้และเปลี่ยนTextBlockเป็น a TextBoxด้วยชุดคุณลักษณะReadonlyและมันทำงานได้อย่างมีเสน่ห์


0

ไม่ทราบว่ามีคนอื่นมีปัญหานี้หรือไม่ แต่ห่อฉันTextBlockไว้ในที่ ๆScrollViewerทำให้สับสน UI ของฉัน - เป็นวิธีแก้ปัญหาง่ายๆฉันคิดว่าแทนที่สิ่งนี้TextBlockด้วยสิ่งTextBoxนี้

<TextBox  Name="textBlock" SelectionBrush="Transparent" Cursor="Arrow" IsReadOnly="True" Text="My Text" VerticalScrollBarVisibility="Auto">

สร้างสิ่งTextBoxที่มีลักษณะและทำงานเหมือนกับ a TextBlockด้วยแถบเลื่อน (และคุณสามารถทำได้ทุกอย่างในตัวออกแบบ)


0

นี่เป็นคำตอบง่ายๆสำหรับคำถามนั้น เลื่อนแนวตั้งจะเปิดใช้งานเฉพาะเมื่อข้อความล้น

<TextBox Text="Try typing some text here " ScrollViewer.VerticalScrollBarVisibility="Auto" TextWrapping="WrapWithOverflow" />

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.