动态更新标签元素

时间:2017-03-23 18:22:07

标签: c# .net wpf

我正在学习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;,我在运行时崩溃了,我在这里使用这一行来查看我是否可以在开始时获得一个零长度,看它是否可以正常工作。错误是:“ApplicationName.exe中发生了'System.NullReferenceException'类型的异常,但未在用户代码中处理”。

仅允许140个字符的逻辑工作正常,名称'label'也是我的标签UI元素的名称。

我需要做什么才能将标签的值更改为text字段的长度,并在用户输入时更改它。

1 个答案:

答案 0 :(得分:0)

如果没有好Minimal, Complete, and Verifiable code example,就不可能确定问题是什么。您可以在此处找到有关如何调试,诊断和修复NullReferenceException的许多有用建议:What is a NullReferenceException, and how do I fix it?

也就是说,我认为这个问题可能是由TextChanged字段初始化之前提出的label事件引起的,可能是InitializeComponent()的一部分方法执行。只是事情的顺序错误。

您可以在尝试使用label之前检查null字段,从而解决问题。但是a)这增加了代码的复杂性,并且b)可能会使您的Label控件保持未初始化状态,直到您明确设置它,或者文本稍后更改。

实际上,更好的方法是采用保持视图模型和绑定它的正常WPF范例。正如Ed记录in his comment,由于RichTextBox如何维护其内容,特别是因为没有方便,简单string - 只能跟踪的属性,您可能仍然需要代码-behind处理TextChanged事件。但在这种情况下,您仍然可以访问正确的视图模型并让它完成工作。

这样做,WPF将确保它不会尝试取消引用null值,并且如果Label控件最终被初始化,那么它就是&#39}正确初始化为您期望的值。

这里是一个简单的视图模型,它具有用于此目的的单个属性,并且包含对于任何视图模型类都是典型的样板逻辑(您可以在Stack Overflow上找到这些示例...我' m为了方便和与本文其余部分的一致性,这里提供这个):

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最初将没有设置值。

希望有所帮助。正如您所看到的,即使在视图模型范例中,也有很多变化,具体取决于程序其余部分的重要性。但这应该让你指出正确的方向。