WPF TextBox для ўводу дзесятковых значэнняў

Ці ёсць годны спосаб, каб атрымаць кантроль над WPF, які звязаны з дзесятковым значэннем?

Калі я толькі звязаць TextBox або DataGridTextColumn да дзесятковай сістэме, увод дадзеных смокча вялікі час.


Калі я спрабую ўвесці «0,5» ў гэтым TextBox я атрымаць "5" у якасці выніку. Гэта амаль немагчыма ўвесці «0,5» на ўсіх (акрамя ўводу 1,5 і замена «1» з «0»).

Пры выкарыстанні ўводу дадзеных StringFormat ўсё яшчэ смокча малы час:


Цяпер, калі я спрабую ўвесці «0,5» Я ў канчатковым выніку з «0,5,0», які да гэтага часу не так, але, па меншай меры, я магу выдаліць буксіроўку », 0" без асаблівых праблем.

Тым не менш, увод дзесятковых знакаў з выкарыстаннем wpf смокча вялікі час, таму што гэты запіс поля з'яўляюцца вельмі схільныя да памылак ўводу дадзеных, якая з'яўляецца рэальнай болем, асабліва для значэнняў!

Так што я павінен выкарыстоўваць для ўводу дзесятковых дадзеных у WPF? Ці ж Microsoft не падтрымлівае дзесятковыя дадзеныя ??

13

9 адказы

Я ў цяперашні час выкарыстоўваю гэта паводзіны для лічбавага і дзесятковага уводу:

public class TextBoxInputBehavior : Behavior
{
    const NumberStyles validNumberStyles = NumberStyles.AllowDecimalPoint |
                                               NumberStyles.AllowThousands |
                                               NumberStyles.AllowLeadingSign;
    public TextBoxInputBehavior()
    {
        this.InputMode = TextBoxInputMode.None;
        this.JustPositivDecimalInput = false;
    }

    public TextBoxInputMode InputMode { get; set; }


    public static readonly DependencyProperty JustPositivDecimalInputProperty =
     DependencyProperty.Register("JustPositivDecimalInput", typeof(bool),
     typeof(TextBoxInputBehavior), new FrameworkPropertyMetadata(false));

    public bool JustPositivDecimalInput
    {
        get { return (bool)GetValue(JustPositivDecimalInputProperty); }
        set { SetValue(JustPositivDecimalInputProperty, value); }
    }

    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.PreviewTextInput += AssociatedObjectPreviewTextInput;
        AssociatedObject.PreviewKeyDown += AssociatedObjectPreviewKeyDown;

        DataObject.AddPastingHandler(AssociatedObject, Pasting);

    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.PreviewTextInput -= AssociatedObjectPreviewTextInput;
        AssociatedObject.PreviewKeyDown -= AssociatedObjectPreviewKeyDown;

        DataObject.RemovePastingHandler(AssociatedObject, Pasting);
    }

    private void Pasting(object sender, DataObjectPastingEventArgs e)
    {
        if (e.DataObject.GetDataPresent(typeof(string)))
        {
            var pastedText = (string)e.DataObject.GetData(typeof(string));

            if (!this.IsValidInput(this.GetText(pastedText)))
            {
                System.Media.SystemSounds.Beep.Play();
                e.CancelCommand();
            }
        }
        else
        {
            System.Media.SystemSounds.Beep.Play();
            e.CancelCommand();
        }
     }

     private void AssociatedObjectPreviewKeyDown(object sender, KeyEventArgs e)
     {
        if (e.Key == Key.Space)
        {
            if (!this.IsValidInput(this.GetText(" ")))
            {
                System.Media.SystemSounds.Beep.Play();
                e.Handled = true;
            }
        }
     }

     private void AssociatedObjectPreviewTextInput(object sender, TextCompositionEventArgs e)
     {
        if (!this.IsValidInput(this.GetText(e.Text)))
        {
            System.Media.SystemSounds.Beep.Play();
            e.Handled = true;
        }
     }

     private string GetText(string input)
     {
        var txt = this.AssociatedObject;

        int selectionStart = txt.SelectionStart;
        if (txt.Text.Length < selectionStart) 
            selectionStart = txt.Text.Length;

        int selectionLength = txt.SelectionLength;
        if (txt.Text.Length < selectionStart + selectionLength) 
            selectionLength = txt.Text.Length - selectionStart;

        var realtext = txt.Text.Remove(selectionStart, selectionLength);

        int caretIndex = txt.CaretIndex;
        if (realtext.Length < caretIndex) 
            caretIndex = realtext.Length;

        var newtext = realtext.Insert(caretIndex, input);

        return newtext;
     }

     private bool IsValidInput(string input)
     {
        switch (InputMode)
        {
            case TextBoxInputMode.None:
                return true;
            case TextBoxInputMode.DigitInput:
                return CheckIsDigit(input);

            case TextBoxInputMode.DecimalInput:
                decimal d;
                //wen mehr als ein Komma
                if (input.ToCharArray().Where(x => x == ',').Count() > 1)
                    return false;


                if (input.Contains("-"))
                {
                     if (this.JustPositivDecimalInput) 
                        return false;


                     if (input.IndexOf("-",StringComparison.Ordinal) > 0) 
                          return false;

                      if(input.ToCharArray().Count(x=>x=='-') > 1)
                          return false;

                        //minus einmal am anfang zulässig
                       if (input.Length == 1) 
                           return true;
                    }

                    var result = decimal.TryParse(input, validNumberStyles, CultureInfo.CurrentCulture, out d);
                    return result;



            default: throw new ArgumentException("Unknown TextBoxInputMode");

        }
        return true;
     }

     private bool CheckIsDigit(string wert)
     {
        return wert.ToCharArray().All(Char.IsDigit);
     }
}

 public enum TextBoxInputMode
 {
  None,
  DecimalInput,
  DigitInput
  }

Выкарыстанне XAML выглядае наступным чынам:


    
        
    

19
дададзена
Дзякуй, але не працуюць нармальна, калі дадаць StringFormat: n0 да яго. Як выкарыстоўваць яго з StringFormat?
дададзена аўтар ar.gorgin, крыніца
Ці будзе зірнуць на гэта, дзякуй! Вы ведаеце, як выкарыстоўваць яго з DataGridTextColumn?
дададзена аўтар Sam, крыніца
чаму downvote без каментароў. гэта не мае ніякага сэнсу
дададзена аўтар blindmeis, крыніца
@JMGH тнх для вашага каментара. я ўсталяваў «мінус» праблемы :)
дададзена аўтар blindmeis, крыніца
я не выкарыстоўваю StringFormat. гэта выкарыстоўваць канвэртар для гэтага матэрыялу замест гэтага. унутр = "{Binding Path = MyValue, Mode = TwoWay, UpdateSourceTrigger = LostFocus, ValidatesOnExceptions = дакладна, ValidatesOnDataErrors = дакладна, NotifyOnValidationError = True, TargetNullValue = {х: Статычная сістэма: String.Empty}, канвэртар = {StaticResource MyStringToDecimalConverter}, ConverterParameter = # \, ## 0,00} »
дададзена аўтар blindmeis, крыніца
@Markus для капіявання і ўставіць або Прабелы вам трэба больш кода :), але вы не павінны выкарыстоўваць 160 радкоў. яго толькі адно рашэнне.
дададзена аўтар blindmeis, крыніца
@Morgoth дадаць спасылку на System.Windows.Interactivity
дададзена аўтар SmartyP, крыніца
160 радкоў кода толькі прымаючы нумара ў тэкставым полі ... Я аддаю перавагу ПОЦЕЛУЙ - трымаць яго проста і па-дурному. <�Код> PreviewTextInput і апрацоўваецца = True можа быць прачытана і зразумета значна хутчэй ...
дададзена аўтар MarkusEgle, крыніца
Я бачыў нейкае дзіўнае паводзіны (гэта дазваляе любому знаку), калі вы паставіце знак мінус першым, у адваротным выпадку гэта добрае рашэнне! Я збіраюся паспрабаваць знайсці памылку ў кодзе, але я думаю, што гэта можа заняць некаторы час, так як гэта крыху extense. Дзякуй за ваша рашэнне.
дададзена аўтар JMGH, крыніца
XAML дае мне гэта :. <�Код> Узаемадзеянне не падтрымліваецца ў праекце Windows Presentation Foundation (WPF)
дададзена аўтар Morgoth, крыніца
Для таго, каб атрымаць Паводзіны дадаць спасылку> Пашырэнні> System.Windows.Interactivity
дададзена аўтар Morgoth, крыніца
XMLNS: я = "CLR-імёнаў: System.Windows.Interactivity; зборка & ZWNJ; = System.Windows.Inte & ZWNJ; ractivity"
дададзена аўтар Heisenberg, крыніца
Што прэфікс прасторы імёнаў "я" вызначаецца як?
дададзена аўтар Heisenberg, крыніца

The WPF Extended toolkit has a DecimalUpDown control that may suit your needs. It's free to use, and it's better to use this than to try and roll your own.

Што тычыцца праверкі ўваходу на яго, існуе цэлы шэраг спосабаў прымянення валідацыю, тут гэта адзін падрабязна ў MSDN. Я падрабязна іншы падыход для карыстацкага Bindable праверка ў двух паведамленняў на маім блогу (вы ўжылі б праверку на Значэнне ўласцівасць звязвання на кантролі DecimalUpDown).

5
дададзена
    private void DecimalTextBox_PreviewTextInput(object sender, System.Windows.Input.TextCompositionEventArgs e)
    {
        bool approvedDecimalPoint = false;

        if (e.Text == ".")
        {
            if (!((TextBox)sender).Text.Contains("."))
                approvedDecimalPoint = true;
        }

        if (!(char.IsDigit(e.Text, e.Text.Length - 1) || approvedDecimalPoint))
            e.Handled = true;
    }
5
дададзена
Растлумачце, чаму гэты код будзе вырашыць праблему OP замест таго, што прыняты адказ ...
дададзена аўтар bcesars, крыніца
Гэта будзе таксама працаваць у якасці альтэрнатывы. Так як дзесятковы лік павінна змясціць кропкі «» характар, яна правяраецца на наяўнасць. Толькі адзін асобнік дапускаецца. Нашмат менш кода.
дададзена аўтар CMarsden, крыніца

Я таксама наткнуўся на гэтае пытанне; з UpdateSourceTrigger = PropertyChanged , здаецца, што звязальныя спрабуе абнавіць тэкст, як вы набіраеце яго. Каб ліквідаваць гэтую праблему, мы змянілі нашы палі ўводу, так што UpdateSourceTrigger = LostFocus , напрыклад.:


Вы можаце вызначыць свае ўласныя памылкі праверкі з дапамогай IDataErrorInfo інтэрфейс. Вам проста трэба дадаць наступнае да вашай мадэлі асновы:

 public class MyModel : IDataErrorInfo
 {
    /* my properties */

    public string Error { get { return null; } }
    public string this[string name]
    {
       get
       {
          switch (name)
          {
             case "MyDecimal":
                return NumberHelper.IsValidValue(MyDecimal) ? message : null;
             default: return null;
          }
       }
    }
    private string message = "Invalid value";
 }
3
дададзена
Праблема заключаецца ў наступным: UpdateSourceTrigger быў усталяваны па прычыне, так як у адваротным выпадку прывязкі значэнне не можа быць абноўлена, калі карыстальнік выклікае кнопку з дапамогай камбінацыі клавіш.
дададзена аўтар Sam, крыніца
@ Сэм, ах, у такім выпадку гэта, верагодна, не будзе дапамагаць. Я пакіну яго, хоць, таму што ў тым выпадку, калі вам не трэба PropertyChanged, гэта простае рашэнне, ИМО.
дададзена аўтар testpattern, крыніца
@dotsamuelswan, так, але гэта спрабуе адказаць на пытанне, я хачу, каб гэта было відавочна, што адбываецца.
дададзена аўтар testpattern, крыніца
LostFocus гэта значэнне UpdateSourceTrigger па змаўчанні для ўласцівасці Text. Не ўсталёўваючы яго на ўсё будзе мець той жа эфект.
дададзена аўтар samuelesque, крыніца

Я рэалізаваў свой уласны TextBox. Ён абнаўляе крыніца, калі ёсць лічба ў тэксце, няйначай. На страціў фокус, я прачытаў ўласцівасці крыніцы. Усё, што вам трэба зрабіць, гэта замяніць TextBox з гэтым класам і прывязаць «Колькасць» Уласцівасць, якое мае тып двайніком.

public class DoubleTextBox: TextBox
{
    public DoubleTextBox()
    {
        TextChanged += DoubleTextBox_TextChanged;
        LostFocus += DoubleTextBox_LostFocus;
    }

    void DoubleTextBox_LostFocus(object sender, System.Windows.RoutedEventArgs e)
    {
        Text = Number.ToString("N2");
    }

    void DoubleTextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        double zahl;
        if (string.IsNullOrWhiteSpace(Text))
        {
            Number = 0;
        }
        else if (double.TryParse(Text, out zahl))
        {
            Number = Double.Parse(zahl.ToString("N2"));
        }
        else
        {
            ValidationError validationError =
                new ValidationError(new ExceptionValidationRule(), GetBindingExpression(NumberProperty));

            validationError.ErrorContent = "Keine gültige Zahl";

            Validation.MarkInvalid(
                GetBindingExpression(NumberProperty),
                validationError);

        }
    }

    public double Number
    {
        get { return (double)this.GetValue(NumberProperty); }
        set { this.SetValue(NumberProperty, value); }
    }

    public static readonly DependencyProperty NumberProperty = DependencyProperty.Register(
        "Number", typeof(double), typeof(DoubleTextBox), 
        new FrameworkPropertyMetadata
            (
                0d,
                FrameworkPropertyMetadataOptions.BindsTwoWayByDefault
            )
    );
}
2
дададзена

Гэта рэгулярны выраз працы

private void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
  {
   Regex regex = new Regex("^[.][0-9]+$|^[0-9]*[.]{0,1}[0-9]*$");
   e.Handled = !regex.IsMatch((sender as TextBox).Text.Insert((sender as TextBox).SelectionStart,e.Text));
  }
1
дададзена

Im новы, так што я не магу каментаваць яго адказ, але я выправіў праблемы адмоўнага ліку ў blindmeis код 's.

проста мадыфікаваць

if (input.Contains("-"))

раздзел <�моцны> IsValidInput (), каб ...

                if (input.Contains("-"))
                {
                    if (this.JustPositivDecimalInput)
                        return false;

                    //minus einmal am anfang zulässig
                    //minus once at the beginning
                    if (input.IndexOf("-", StringComparison.Ordinal) == 0 && input.ToCharArray().Count(x => x == '-') == 1)
                    {
                        if(input.Length == 1)
                        {
                            //INPUT IS "-"
                            return true;
                        }
                        else if (input.Length == 2)
                        {
                            //VALIDATE NEGATIVE DECIMALS...INPUT IS "-."
                            if (input.IndexOf(".", StringComparison.Ordinal) == 1)
                            {
                                return true;
                            }
                        }
                        else 
                        {
                            return decimal.TryParse(input, validNumberStyles, CultureInfo.CurrentCulture, out d);
                        }
                    }
                }
1
дададзена

калі вы хочаце, каб тэкставае поле, каб толькі дзесятковыя затым запісаць previewinputtext падзеі для гэтага тэкставага поля. то ў гэтым выпадку пісаць гэты код

decimal result;
e.Handled=!decimal.TryParse((sender as TextBox).Text + e.Text, out result)
1
дададзена

Гэта дазволіць толькі дзесятковыя, каб увесці ў тэкставае поле і больш нічога.

ViewModel выглядае наступным чынам:

    private string _decimalVal = "0";
    public string decimalVal
    {
        get { return _decimalVal.ToString(); }
        set
        {
            if (string.IsNullOrEmpty(value))
                value = "0";

            if (value == "-" || Decimal.TryParse(value, out decimal newVal))
                SetProperty(ref _decimalVal, value);
        }
    }

Выкарыстанне XAML выглядае наступным чынам:


1
дададзена