Странная ситуация со свойством TextBox.Text в WPF - PullRequest
3 голосов
/ 09 сентября 2010

Это меня поставило в тупик. Я нашел код , опубликованный Рэй Бернсом , чтобы создать DependencyProperty для TextBox, который позволяет вам ограничить, какие символы могут быть удалены пользователем. Я немного изменил его, чтобы вместо этого ограничить, какие символы могут быть вставлены , и использовал это для создания текстовых полей, которые принимают только числовой ввод (плюс десятичную точку).

Это прекрасно работает для ввода текста с клавиатуры, вставки, перетаскивания и т. Д. Единственная проблема возникает при настройке текста с помощью кода. Там он позволяет вводить нечисловой текст, что само по себе не является проблемой. Проблема заключается в том, что если после этого вы проверяете значение свойства Text TextBox, оно говорит, что это пустая строка.

Вот код, демонстрирующий, что я имею в виду. Простое окно WPF:

<Window x:Class="TestApp.Test"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:my="clr-namespace:TestApp"
        Title="Test" Height="125" Width="200">
    <Canvas>
        <TextBox x:Name="txtTest" Canvas.Left="10" Canvas.Top="10" Width="100" my:TextBoxRestriction.RestrictInsertTo=".0123456789"></TextBox>
        <Button Canvas.Left="10" Canvas.Top="40" Click="Button_Click">Enter Text</Button>
        <Button Canvas.Left="75" Canvas.Top="40" Click="Button_Click_1">Check Value</Button>
    </Canvas>
</Window>

Код:

using System;
using System.Windows;

namespace TestApp
{
    public partial class Test : Window
    {
        public Test()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            txtTest.Text = "Test";
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            MessageBox.Show(txtTest.Text, "Length = " + txtTest.Text.Length.ToString());
        }
    }
}

И моя модификация класса Рэя:

using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;

namespace TestApp
{
    //Based on code by Ray Burns at /3223453/kak-otsledit-kakoi-simvol-udalen-v-textbox-v-wpf#3223466.
    public class TextBoxRestriction : DependencyObject
    {
        //RestrictInsertTo: Set this to the characters that may be inserted.
        public static string GetRestrictInsertTo(DependencyObject obj)
        {
            return (string)obj.GetValue(RestrictInsertToProperty);
        }

        public static void SetRestrictInsertTo(DependencyObject obj, string value)
        {
            obj.SetValue(RestrictInsertToProperty, value);
        }

        public static readonly DependencyProperty RestrictInsertToProperty = DependencyProperty.RegisterAttached("RestrictInsertTo", typeof(string), typeof(TextBoxRestriction), new PropertyMetadata
        {
            PropertyChangedCallback = (obj, e) =>
            {
                var box = (TextBox)obj;

                box.TextChanged += (obj2, changeEvent) =>
                {
                    var oldText = GetUnrestrictedText(box);
                    var allowedChars = GetRestrictInsertTo(box);

                    if (box.Text == oldText || allowedChars == null) return;

                    foreach (var change in changeEvent.Changes)
                    {
                        if (change.AddedLength > 0)
                        {
                            string added = box.Text.Substring(change.Offset, change.AddedLength);

                            if (added.Any(ch => !allowedChars.Contains(ch)))
                            {
                                var ss = box.SelectionStart;
                                var sl = box.SelectionLength;

                                box.Text = oldText;
                                box.SelectionStart = ss;
                                box.SelectionLength = sl;
                            }
                        }
                    }

                    SetUnrestrictedText(box, box.Text);
                };
            }
        });

        //UnrestrictedText: Bind or access this property to update the Text property bypassing all restrictions.
        public static string GetUnrestrictedText(DependencyObject obj)
        {
            return (string)obj.GetValue(UnrestrictedTextProperty);
        }

        public static void SetUnrestrictedText(DependencyObject obj, string value)
        {
            obj.SetValue(UnrestrictedTextProperty, value);
        }

        public static readonly DependencyProperty UnrestrictedTextProperty = DependencyProperty.RegisterAttached("UnrestrictedText", typeof(string), typeof(TextBoxRestriction), new PropertyMetadata
        {
            DefaultValue = "",
            PropertyChangedCallback = (obj, e) =>
            {
                var box = (TextBox)obj;

                box.Text = (string)e.NewValue;
            }
        });
    }
}

Если вы попытаетесь набрать текстовый блок, вы увидите, что он работает почти так же, как и ожидалось, и что нажатие кнопки «Проверить значение» точно отражает текст; однако, если вы нажмете кнопку «Ввести текст», а затем нажмите кнопку «Проверить значение», вы увидите, что приложение считает, что свойство Text в TextBox представляет собой пустую строку (даже несмотря на то, что в пользовательском интерфейсе текст четко виден). Если вы каким-либо образом измените текст в пользовательском интерфейсе (скажем, удалите символ), а затем нажмите «Проверить значение», теперь он распознает правильный текст.

Может кто-нибудь пролить свет на то, почему это происходит? Возможно, я упускаю что-то явно очевидное, но я просто не могу понять это.

Заранее спасибо!

Jeff

1 Ответ

2 голосов
/ 09 сентября 2010

Вы должны также сделать obj.SetValue(TextProperty, value) в вашем SetUnrestrictedText методе, я думаю.

Это первое предположение.Я рассмотрю это немного более подробно, если это не так, и никто не отвечает.

Я попытался сделать это с вашим примером кода на моей машине, VS2010, .NET 4. Присоединенное свойство сбрасываетсясвойство Text TextBox каждый раз.Я пробовал несколько вариантов неверного ввода.

Если вы хотите, чтобы объект DependencyObject отфильтровал присваивание в коде, отклоняя недопустимые символы и сохраняя действительные, тогда вам нужен тест, отличный от if (added.Any(ch => !allowedChars.Contains(ch))).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...