首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >动态更新标签元素

动态更新标签元素
EN

Stack Overflow用户
提问于 2017-03-23 18:22:06
回答 1查看 1.3K关注 0票数 2

我正在学习wpf,并且我试图根据我的RichTextBox中的字符数来更改一个标签值,我认为我可以在我的richTextBox_TextChanged()方法中这样做,在这个方法中,当前的逻辑是删除输入的所有文本,输入传递的140个字符。

代码语言:javascript
运行
复制
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;中,我使用这一行查看是否可以在开始时获得零的长度,以查看它是否有效。错误是:“类型'System.NullReferenceException‘的异常发生在ApplicationName.exe中,但未在用户代码中处理”。

只允许140个字符的逻辑工作得很好,而名称' label‘也是my元素的名称。

我需要做什么才能将标签的值更改为text字段的长度,并使其随着用户类型的变化而改变。

EN

Stack Overflow用户

回答已采纳

发布于 2017-03-23 19:24:29

如果没有一个好的Minimal, Complete, and Verifiable code example,就不可能确切地知道问题的所在。您可以在这里找到许多关于如何调试、诊断和修复NullReferenceException的有用建议:What is a NullReferenceException, and how do I fix it?

尽管如此,我认为这个问题很可能是由在初始化TextChanged字段之前引发的label事件引起的,可能是作为InitializeComponent()方法执行的一部分。只是事情的顺序不对。

您可以通过在尝试使用label字段之前检查null字段来解决这个问题。但是,这增加了代码的复杂性,并且( b)可能在显式设置Label控件之前不对其进行初始化,或者在以后更改文本。

更好的方法是,事实上,采用常规的WPF范式来保持视图模型并绑定到它。正如in his comment所指出的,由于RichTextBox是如何维护其内容的,特别是因为您无法跟踪一个方便、简单的string-only属性,所以您可能仍然希望代码隐藏来处理TextChanged事件。但是在这种情况下,您仍然可以访问一个适当的视图模型,并让它来完成工作。

这样做,WPF将确保它不会试图取消对null值的引用,并且如果和当Label控件最终初始化时,它将被正确地初始化为您期望的值。

下面是一个简单的视图模型,它有一个用于此目的的属性,它包含典型于任何视图模型类的样板逻辑(您可以在堆栈溢出…中找到这些示例)我在这里提供这篇文章只是为了方便和与本帖子的其他部分保持一致):

代码语言:javascript
运行
复制
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:

代码语言:javascript
运行
复制
<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>

当然,您需要代码隐藏:

代码语言:javascript
运行
复制
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引用。

如果出于某种原因,拥有纯文本信息也是有用的,则可以扩展视图模型以包括以下内容:

代码语言:javascript
运行
复制
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属性。相反,您的代码隐藏应该是这样的:

代码语言:javascript
运行
复制
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属性:

代码语言:javascript
运行
复制
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更改为:

代码语言:javascript
运行
复制
<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最初将没有值设置。

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

票数 1
EN
查看全部 1 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/42984032

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档