Як відформатувати кількість десяткових знаків у wpf за допомогою стилю / шаблону?


94

Я пишу програму WPF і намагаюся знайти спосіб форматування даних у TextBox за допомогою якогось повторюваного методу, такого як стиль або шаблон. У мене багато TextBox (точніше 95), і кожен з них прив’язаний до власних числових даних, кожна з яких може мати власну роздільну здатність. Наприклад, якщо дані дорівнюють 99.123 з роздільною здатністю 2, вони повинні відображати 99.12. Аналогічним чином значення даних 99 та роздільна здатність 3 повинні відображатися як 99 000 (а не 99). Чи є спосіб зробити це?

Редагувати: слід уточнити, на поточному екрані, над яким я працюю, є 95 TextBox, але я хочу, щоб кожен TextBox на різних екранах моєї програми відображав правильну кількість знаків після коми. Тепер, коли я замислююся над цим, деякі з них є TextBoxes (наприклад, екран, над яким я зараз працюю), а деякі - DataGrids або ListViews, але якщо я можу зрозуміти, як змусити його працювати для TextBoxes, я впевнений, що можу зрозуміти це також для інших елементів управління.

У цьому випадку не так багато коду, яким можна поділитися, але я спробую зробити це зрозумілішим:

У мене є модель перегляду, яка містить такі властивості (vb.net):

    Public ReadOnly Property Resolution As Integer
        Get
            Return _signal.DisplayResolution
        End Get
    End Property

    Public ReadOnly Property Value As Single
        Get
            Return Math.Round(_signal.DisplayValue, Resolution)
        End Get
    End Property

і в XAML я маю:

<UserControl.Resources>
    <vm:SignalViewModel x:Key="Signal" SignalPath="SomeSignal"/>
</UserControl.Resources>
<TextBox Grid.Column="3" IsEnabled="False" Text="{Binding Path=Value, Source={StaticResource Signal}, Mode=OneWay}" />

EDIT2 (моє рішення): Виявляється, трохи відійшовши від комп’ютера, я повернувся, щоб знайти просту відповідь, яка дивилася мені в обличчя. Відформатуйте дані в моделі подання!

    Public ReadOnly Property Value As String
        Get
            Return (Strings.FormatNumber(Math.Round(_signal.DisplayValue, _signal.DisplayResolution), _signal.DisplayResolution))
        End Get
    End Property

1
використовувати a IValueConverter? Передайте фактичне значення та роздільну здатність конвертеру, і нехай він робить округлення для вас у собі. Важко запропонувати, StringFormatне знаючи, як саме TextBoxгенеруються ці 95 .
Viv

Опублікуйте поточний код та XAML. Інакше це все спекуляції та безглузді здогади.
Федеріко Берасатегі

Я додав до запитання ще трохи інформації, яка, сподіваємось, повинна зробити її більш зрозумілою.
AXG1010,

Відповіді:


198

Ви повинні використовувати StringFormatна Binding. Ви можете використовувати як стандартні формати рядків , так і власні формати рядків :

<TextBox Text="{Binding Value, StringFormat=N2}" />
<TextBox Text="{Binding Value, StringFormat={}{0:#,#.00}}" />

Зверніть увагу, що StringFormatєдине працює, коли цільова властивість має тип string. Якщо ви намагаєтесь встановити щось на зразок Contentвластивості ( typeof(object)), вам потрібно буде скористатися користувацьким StringFormatConverter( як тут ) і передати рядок формату як ConverterParameter.

Змінити для оновленого запитання

Отже, якщо ваша ViewModelточність визначає точність, я б рекомендував зробити це як MultiBindingта створити свою власну IMultiValueConverter. На практиці це досить дратує: перейти від простого прив'язки до того, який потрібно розширити, до a MultiBinding, але якщо точність невідома під час компіляції, це майже все, що ви можете зробити. Вам IMultiValueConverterпотрібно буде взяти значення і точність, і вивести відформатований рядок. Ви зможете зробити це за допомогою String.Format.

Однак для таких речей, як a ContentControl, ви можете набагато простіше зробити це за допомогою a Style:

<Style TargetType="{x:Type ContentControl}">
    <Setter Property="ContentStringFormat" 
            Value="{Binding Resolution, StringFormat=N{0}}" />
</Style>

Будь-який елемент керування, який виставляє a, ContentStringFormatможна використовувати таким чином. На жаль, TextBoxне має нічого подібного.


6
Приклад із встановленим для StringFormat значенням #,#.00не компілюється - кома інтерпретується як роздільник атрибутів у Bindingрозширенні розмітки.
Gigi

@Gigi, ви маєте рацію, але ви можете легко використовувати його як так: StringFormat={}{0:#,#.00}. Я оновлю відповідь, щоб вона працювала правильно.
Abe Heidebrecht

"StringFormat = N {0}" чудово працює. Для точності 2 я б хотів, щоб відображалися два знаки після коми, за винятком "10,00", у цьому випадку я хочу, щоб відображалося "10". Чи є спосіб зробити це, прив'язуючи до точності? Здається, мені доведеться використовувати конвертер.
Гордон Сліш,

Я не думаю, що існує спосіб змінити десяткові крапки, представлені за допомогою форматів рядків .NET, тому вам, мабуть, краще написати конвертер.
Abe Heidebrecht

Чи можете ви пояснити, чому використовувати два специфікатори 0: #, #. 00, чи не достатньо одного з них?
Лей Ян

7

Прийнята відповідь не відображає 0 в цілому місці при введенні, наприклад 0,299. Він відображає .3 в інтерфейсі WPF. Тож моя пропозиція використовувати наступний формат рядка

<TextBox Text="{Binding Value,  StringFormat={}{0:#,0.0}}" 

Привіт, з вашим рішенням все гаразд, але я б скоріше хотів використовувати ключове слово N1 (2,3 ...), оскільки воно дозволяє уникнути помилок при друкарській помилці, і принаймні ви впевнені, як воно відображатиметься. Але справді друга пропозиція не відображає 0, якщо значення <0, як ви згадали.
Кевін ВДФ,

-2
    void NumericTextBoxInput(object sender, TextCompositionEventArgs e)
    {
        TextBox txt = (TextBox)sender;
        var regex = new Regex(@"^[0-9]*(?:\.[0-9]{0,1})?$");
        string str = txt.Text + e.Text.ToString();
        int cntPrc = 0;
        if (str.Contains('.'))
        {
            string[] tokens = str.Split('.');
            if (tokens.Count() > 0)
            {
                string result = tokens[1];
                char[] prc = result.ToCharArray();
                cntPrc = prc.Count();
            }
        }
        if (regex.IsMatch(e.Text) && !(e.Text == "." && ((TextBox)sender).Text.Contains(e.Text)) && (cntPrc < 3))
        {
            e.Handled = false;
        }
        else
        {
            e.Handled = true;
        }
    }

9
Деякі пояснення значно покращать якість вашої відповіді.
mrun
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.