簡體   English   中英

動態更新標簽元素

[英]Dynamically update label element

我正在學習wpf,並且嘗試根據RichTextBox中的字符數更改標簽值,我想我可以在richTextBox_TextChanged()方法中執行此操作,其中當前邏輯是刪除輸入的所有傳遞140個字符的文本。

private void richTextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        TextRange range = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);
        var text = range.Text.Trim();
        label.Content = text.Length;
        if (text.Length > 140)
        {
            int split = 0;
            while (richTextBox.CaretPosition.DeleteTextInRun(-1) == 0)
            {
                richTextBox.CaretPosition.GetPositionAtOffset(--split);

            }
        }
    }

由於此行label.Content = text.Length;而在運行時崩潰label.Content = text.Length; 我在那條線中查看開始時是否可以將長度設為零,以查看它是否可以工作。 錯誤為:“ ApplicationName.exe中發生類型'System.NullReferenceException'的異常,但未在用戶代碼中處理”。

僅允許140個字符的邏輯工作正常,名稱“ label”也是我的標簽UI元素的名稱。

我需要做些什么來將標簽的值更改為text字段的長度,並隨用戶的輸入更改標簽的值。

如果沒有良好的最小,完整和可驗證的代碼示例 ,就不可能確定問題出在哪里。 您可以在此處找到有關如何調試,診斷和修復NullReferenceException的許多有用建議: 什么是NullReferenceException,以及如何解決?

就是說,我認為問題很可能是由初始化label字段之前TextChanged事件引起的,可能是InitializeComponent()方法執行的一部分。 只是事情順序錯誤。

您可以通過在嘗試使用label字段之前檢查其null來解決該問題。 但是a)這增加了代碼的復雜性,並且b)在您顯式設置它或以后更改文本之前,可能未初始化Label控件。

實際上,更好的方法是采用保留視圖模型並將其綁定的常規WPF范例。 正如Ed 在評論中指出的那樣 ,由於RichTextBox維護其內容的方式,尤其是由於沒有可跟蹤的便捷,僅string屬性,您可能仍希望隱藏代碼來處理TextChanged事件。 但是在那種情況下,您仍然可以訪問適當的視圖模型並讓其完成工作。

為此,WPF將確保它不會嘗試取消引用null值,以及確保在最終初始化Label控件時,將其正確初始化為您期望的值。

這是一個簡單的視圖模型,具有一個為此目的的單一屬性,並且包含任何視圖模型類都典型的樣板邏輯(您可以在Stack Overflow上找到所有這些示例……我只是為了方便和保持一致性而在此處提供此示例)和這篇文章的其余部分):

class ViewModel : INotifyPropertyChanged
{
    private int _textLength;

    public int TextLength
    {
        get { return _textLength; }
        set { _UpdateField(ref _textLength, value); }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void _UpdateField<T>(ref T field, T newValue, [CallerMemberName] string propertyName = null)
    {
        if (!EqualityComparer<T>.Default.Equals(field, newValue))
        {
            field = newValue;
            _OnPropertyChanged(propertyName);
        }
    }

    private void _OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

        switch (propertyName)
        {
            // you can add "case nameof(...):" cases here to handle
            // specific property changes, rather than polluting the
            // property setters themselves
        }
    }
}

使用這樣的視圖模型,則可以編寫XAML:

<Window x:Class="TestSO42984032TextLengthLabel.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:l="clr-namespace:TestSO42984032TextLengthLabel"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
  <Window.DataContext>
    <l:ViewModel/>
  </Window.DataContext>
  <StackPanel>
    <RichTextBox TextChanged="RichTextBox_TextChanged"/>
    <Label Content="{Binding TextLength}"/>
  </StackPanel>
</Window>

當然,您將需要以下代碼:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void RichTextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        ViewModel model = (ViewModel)DataContext;
        RichTextBox richTextBox = (RichTextBox)sender;
        TextRange range = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);
        var text = range.Text.Trim();

        model.TextLength = text.Length;
        if (text.Length > 140)
        {
            int split = 0;
            while (richTextBox.CaretPosition.DeleteTextInRun(-1) == 0)
            {
                richTextBox.CaretPosition.GetPositionAtOffset(--split);

            }
        }
    }
}

現在,每當文本更改時,事件處理程序都會被調用,並且作為其操作的一部分,它將使用正確的值更新視圖模型屬性。 肯定會在這一點上設置DataContext ,因此您可以安全地使用它,而無需擔心使用null引用。

如果也由於某種原因而擁有純文本信息也很有用,則可以擴展視圖模型以包括以下內容:

class ViewModel : INotifyPropertyChanged
{
    private string _text;
    private int _textLength;

    public string Text
    {
        get { return _text; }
        set { _UpdateField(ref _text, value); }
    }

    public int TextLength
    {
        get { return _textLength; }
        set { _UpdateField(ref _textLength, value); }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void _UpdateField<T>(ref T field, T newValue, [CallerMemberName] string propertyName = null)
    {
        if (!EqualityComparer<T>.Default.Equals(field, newValue))
        {
            field = newValue;
            _OnPropertyChanged(propertyName);
        }
    }

    private void _OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

        switch (propertyName)
        {
            case nameof(Text):
                TextLength = Text.Length;
                break;
        }
    }
}

請注意,在這里,我使用switch語句更新TextLength屬性。 您的后台代碼看起來像這樣:

private void RichTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
    ViewModel model = (ViewModel)DataContext;
    RichTextBox richTextBox = (RichTextBox)sender;
    TextRange range = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);
    var text = range.Text.Trim();

    model.Text = text;
    if (text.Length > 140)
    {
        int split = 0;
        while (richTextBox.CaretPosition.DeleteTextInRun(-1) == 0)
        {
            richTextBox.CaretPosition.GetPositionAtOffset(--split);

        }
    }
}

最后,請注意,綁定可以使用屬性路徑,而不僅僅是簡單的屬性名稱。 因此,如果需要,可以完全省略TextLength屬性:

class ViewModel : INotifyPropertyChanged
{
    private string _text = string.Empty;

    public string Text
    {
        get { return _text; }
        set { _UpdateField(ref _text, value); }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void _UpdateField<T>(ref T field, T newValue, [CallerMemberName] string propertyName = null)
    {
        if (!EqualityComparer<T>.Default.Equals(field, newValue))
        {
            field = newValue;
            _OnPropertyChanged(propertyName);
        }
    }

    private void _OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

        switch (propertyName)
        {
            // empty
        }
    }
}

並將XAML更改為此:

<Window x:Class="TestSO42984032TextLengthLabel.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:l="clr-namespace:TestSO42984032TextLengthLabel"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
  <Window.DataContext>
    <l:ViewModel/>
  </Window.DataContext>
  <StackPanel>
    <RichTextBox TextChanged="RichTextBox_TextChanged"/>
    <Label Content="{Binding Text.Length}"/>
  </StackPanel>
</Window>

請注意,在這種情況下,您需要初始化視圖模型字段,以確保它具有實際的非null字符串值。 如果沒有所做的更改,程序將運行,但是您的Label最初將沒有設置任何值。

希望能有所幫助。 如您所見,即使在視圖模型范例中,也存在許多變化,具體取決於程序其余部分中的重要內容。 但這應該使您指向正確的方向。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM