WPF 如何创建具有验证和绑定的自定义文本框

2023-12-04

我正在开发一个用于货币编辑的自定义文本框。
我见过一些现成的,但它们很复杂和/或并不真正可用,迫使您采取不良做法(例如对应该在控件上使用的名称进行硬编码)。
因此,我决定自己完成此操作,但在使用绑定选项时遇到了麻烦,因为分配给绑定属性的属性必须是小数,但 TextBox 控件的 Text 属性接受字符串。
我想的答案是,也许可以重写基类(TextBox)中 Text 属性的访问方法(getter 和 setter),但这是不允许的。
我的绑定应该设置为值,该值将 TextBox 的文本属性设置为文本(带有货币符号和所有内容),但在 Get 方法上将其转换回数字数据类型。
这是我到目前为止所取得的成就:

public class CurrencyTextBox : TextBox
    {
        private bool IsValidKey(Key key)
        {
            int k = (int)key;
            return ((k >= 34 && k <= 43) //digits 0 to 9
                || (k >= 74 && k <= 83) //numeric keypad 0 to 9
                || (k == 2) //back space
                || (k == 32) //delete
                );
        }
        private void Format()
        {
            //formatting decimal to currency text here
            //Done! no problems here
        }
        private void FormatBack()
        {
            //formatting currency text to decimal here
            //Done! no problems here
        }
        private void ValueChanged(object sender, TextChangedEventArgs e)
        {
            this.Format();
        }
        private void MouseClicked(object sender, MouseButtonEventArgs e)
        {
            this.Format();
            // Prevent changing the caret index
            this.CaretIndex = this.Text.Length;
            e.Handled = true;
        }
        private void MouseReleased(object sender, MouseButtonEventArgs e)
        {
            this.Format();
            // Prevent changing the caret index
            this.CaretIndex = this.Text.Length;
            e.Handled = true;
        }
        private void KeyPressed(object sender, KeyEventArgs e)
        {
            if (IsValidKey(e.Key))
                e.Handled = true;
            if (Keyboard.Modifiers != ModifierKeys.None)
                return;
            this.Format();
        }
        private void PastingEventHandler(object sender, DataObjectEventArgs e)
        {
            // Prevent copy/paste
            e.CancelCommand();
        }
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            // Disable copy/paste
            DataObject.AddCopyingHandler(this, PastingEventHandler);
            DataObject.AddPastingHandler(this, PastingEventHandler);
            this.CaretIndex = this.Text.Length;
            this.PreviewKeyUp += KeyPressed;
            this.PreviewMouseDown += MouseClicked;
            this.PreviewMouseUp += MouseReleased;
            this.TextChanged += ValueChanged;
            this.Format();
        }
    }

这是 XAML:

<MyNamespace:CurrencyTextBox x:Name="TxbCurrency" Text="{Binding Path=DataContext.Element.Currency, ValidatesOnDataErrors=True}" />

到目前为止,一切都很好!从小数属性到 TextBox 文本的绑定是“正确的”。但如何在编辑后的文本中取回小数点是现在的问题。
从十进制到 .Text 的绑定使用装箱来隐藏 ToString() 方法。
问题在这里:在这种情况下,如何从十进制重载 Parse() 方法以使用 FormatBack() 方法从 TextBox 的文本中获取小数?


好吧,为了将来的目的,如果有人遇到同样的麻烦,这里是货币文本框的完整代码。随意使用它、修改它、出售它(你不要认为它有价值),或者随心所欲地使用它!

/*
 * the necessary usings:
 * using System.Globalization;
 * using System.Windows;
 * using System.Windows.Controls;
 * using System.Windows.Input;
 * using System.Threading;
 * And don't forget to change the currency settings on the XAML
 * or in the defaults (on the contructor)
 * It's set by default to Brazilian Real (R$)
 */
public class CurrencyTextBox : TextBox
{
    public CurrencyTextBox()
    {
        CurrencySymbol = "R$ ";
        CurrencyDecimalPlaces = 2;
        DecimalSeparator = ",";
        ThousandSeparator = ".";
        Culture = "pt-BR";
    }
    public string CurrencySymbol { get; set; }
    private int CurrencyDecimalPlaces { get; set; }
    public string DecimalSeparator { get; set; }
    public string ThousandSeparator { get; set; }
    public string Culture { get; set; }
    private bool IsValidKey(int k)
    {
        return (k >= 34 && k <= 43) //digits 0 to 9
            || (k >= 74 && k <= 83) //numeric keypad 0 to 9
            || (k == 2) //back space
            || (k == 32) //delete
            ;
    }
    private string Format(string text)
    {
        string unformatedString = text == string.Empty ? "0,00" : text; //Initial state is always string.empty
        unformatedString = unformatedString.Replace(CurrencySymbol, ""); //Remove currency symbol from text
        unformatedString = unformatedString.Replace(DecimalSeparator, ""); //Remove separators (decimal)
        unformatedString = unformatedString.Replace(ThousandSeparator, ""); //Remove separators (thousands)
        decimal number = decimal.Parse(unformatedString) / (decimal)Math.Pow(10, CurrencyDecimalPlaces); //The value will have 'x' decimal places, so divide it by 10^x
        unformatedString = number.ToString("C", CultureInfo.CreateSpecificCulture(Culture));
        return unformatedString;
    }
    private decimal FormatBack(string text)
    {
        string unformatedString = text == string.Empty ? "0.00" : text;
        unformatedString = unformatedString.Replace(CurrencySymbol, ""); //Remove currency symbol from text
        unformatedString = unformatedString.Replace(ThousandSeparator, ""); //Remove separators (thousands);
        CultureInfo current = Thread.CurrentThread.CurrentUICulture; //Let's change the culture to avoid "Input string was in an incorrect format"
        Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(Culture);
        decimal returnValue = decimal.Parse(unformatedString);
        Thread.CurrentThread.CurrentUICulture = current; //And now change it back, cuz we don't own the world, right?
        return returnValue;
    }
    private void ValueChanged(object sender, TextChangedEventArgs e)
    {
        // Keep the caret at the end
        this.CaretIndex = this.Text.Length;
    }
    private void MouseClicked(object sender, MouseButtonEventArgs e)
    {
        // Prevent changing the caret index
        e.Handled = true;
        this.Focus();
    }
    private void MouseReleased(object sender, MouseButtonEventArgs e)
    {
        // Prevent changing the caret index
        e.Handled = true;
        this.Focus();
    }
    private void KeyReleased(object sender, KeyEventArgs e)
    {
        this.Text = Format(this.Text);
        this.Value = FormatBack(this.Text);
    }
    private void KeyPressed(object sender, KeyEventArgs e)
    {
        if (IsValidKey((int)e.Key))
            return;
        e.Handled = true;
        this.CaretIndex = this.Text.Length;
    }
    private void PastingEventHandler(object sender, DataObjectEventArgs e)
    {
        // Prevent/disable paste
        e.CancelCommand();
    }
    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        DataObject.AddCopyingHandler(this, PastingEventHandler);
        DataObject.AddPastingHandler(this, PastingEventHandler);
        this.CaretIndex = this.Text.Length;
        this.KeyDown += KeyPressed;
        this.KeyUp += KeyReleased;
        this.PreviewMouseDown += MouseClicked;
        this.PreviewMouseUp += MouseReleased;
        this.TextChanged += ValueChanged;
        this.Text = Format(string.Empty);
    }
    public decimal? Value
    {
        get { return (decimal?)this.GetValue(ValueProperty); }
        set { this.SetValue(ValueProperty, value); }
    }
    public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
        "Value",
        typeof(decimal?),
        typeof(CurrencyTextBox),
        new FrameworkPropertyMetadata(new decimal?(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(ValuePropertyChanged)));
    private static void ValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ((CurrencyTextBox)d).Value = ((CurrencyTextBox)d).FormatBack(e.NewValue.ToString());
    }
}

和 xaml:

<myNamespace:CurrencyTextBox
    Value="{Binding Path=DataContext.MyDecimalProperty, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"
    CurrencySymbol="R$ "
    Culture="pt-BR"
    CurrencyDecimalPlaces="2"
    DecimalSeparator=","
    ThousandSeparator="." />
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

WPF 如何创建具有验证和绑定的自定义文本框 的相关文章

  • 向 Nhibernate 发出 SQL 查询

    如何将此 SQL 查询发送给 Nhibernate SELECT Customer name FROM Company INNER JOIN Customer ON Company CompanyId Customer CompanyId
  • 启动时出现 OData v4 错误:找不到段“Whatever”的资源

    我正在构建新的 v4 服务 一切进展顺利 直到我为新模型 实体添加了新控制器 并在启动站点进行测试运行时收到此错误 控制器似乎编码正确 就像其他控制器一样 控制器 CustomersOData 中的操作 GetFeed 上的路径模板 Cus
  • 如何在 C# 中从 UNIX 纪元时间转换并考虑夏令时?

    我有一个从 unix 纪元时间转换为 NET DateTime 值的函数 public static DateTime FromUnixEpochTime double unixTime DateTime d new DateTime 19
  • 如何修复此错误“GDI+ 中发生一般错误”?

    从默认名称打开图像并以默认名称保存 覆盖它 我需要从 Image Default jpg 制作图形 将其放在 picturebox1 image 上并在 picurebox1 上绘制一些图形 它有效 这不是我的问题 但我无法保存 pictu
  • 单元测试一起运行时失败,单独运行时通过

    所以我的单元测试遇到了一些问题 我不能只是将它们复制并粘贴到这里 但我会尽力而为 问题似乎是 如果我一项一项地运行测试 一切都会按预期进行 但如果我告诉它一起运行测试 则 1 5 将通过 TestMethod public void Obj
  • 用于检查项目文件中的项目变量和引用路径的 api

    我正在研究一个 net application VS2010 与 x 没有 解和变量号这些解决方案中的项目数量 我需要检查项目属性 特定于一定数量的项目 是否同质 并且检查 验证构建期间的参考路径 有没有一个API是这样的吗 如果没有 我该
  • 关于在 Windows 上使用 WiFi Direct Api?

    我目前正在开发一个应用程序 我需要在其中创建链接 阅读 无线网络连接 在桌面应用程序 在 Windows 10 上 和平板电脑 Android 但无关紧要 之间 工作流程 按钮 gt 如果需要提升权限 gt 创建类似托管网络的 WiFi 网
  • 单击 form2 上的按钮触发 form 1 中的方法

    我对 Windows 窗体很陌生 我想知道是否可以通过单击表单 2 中的按钮来触发表单 1 中的方法 我的表格 1 有一个组合框 我的 Form 2 有一个 保存 按钮 我想要实现的是 当用户单击表单 2 中的 保存 时 我需要检查表单 1
  • 使用 JNI 从 Java 代码中检索 String 值的内存泄漏

    我使用 GetStringUTFChars 从使用 JNI 的 java 代码中检索字符串的值 并使用 ReleaseStringUTFChars 释放该字符串 当代码在 JRE 1 4 上运行时 不会出现内存泄漏 但如果相同的代码在 JR
  • PlaySound 可在 Visual Studio 中运行,但不能在独立 exe 中运行

    我正在尝试使用 Visual Studio 在 C 中播放 wav 文件 我将文件 my wav 放入项目目录中并使用代码 PlaySound TEXT my wav NULL SND FILENAME SND SYNC 我按下播放按钮 或
  • 如何使用 Mongodb C# 驱动程序连接多个集合

    我需要将 3 个集合与多个集合合并在一起 lookup我在 C 驱动程序中尝试过 它允许我 lookup用户采集但无法执行秒 lookup用于设置集合 有人可以帮忙吗 db Transactions aggregate lookup fro
  • 如何编写一个同时需要请求和响应Dtos的ServiceStack插件

    我需要提供本地化数据服务 所有本地化的响应 Dto 都共享相同的属性 IE 我定义了一个接口 ILocalizedDto 来标记那些 Dto 在请求端 有一个ILocalizedRequest对于需要本地化的请求 Using IPlugin
  • 私有模板函数

    我有一堂课 C h class C private template
  • HttpWebRequest 在第二次调用时超时

    为什么以下代码在第二次 及后续 运行时超时 代码挂在 using Stream objStream request GetResponse GetResponseStream 然后引发 WebException 表示请求已超时 我已经尝试过
  • .NET中的LinkedList是循环链表吗?

    我需要一个循环链表 所以我想知道是否LinkedList是循环链表吗 每当您想要移动列表中的 下一个 块时 以循环方式使用它的快速解决方案 current current Next current List First 电流在哪里Linke
  • 在wpf中移动鼠标

    我目前正在寻找一种在 wpf 中移动鼠标的方法 我发现的只是我无法可靠实现的非托管方法调用 有没有一种简单的方法可以将鼠标光标移动到某个地方 即 双击后 我肯定在这里遗漏了一些东西 添加对System Windows Forms dll的引
  • 有没有办法强制显示工具提示?

    我有一个验证字段的方法 如果无法验证 该字段将被清除并标记为红色 我还希望在框上方弹出一个工具提示 并向用户显示该值无效的消息 有没有办法做到这一点 并且可以控制工具提示显示的时间 我怎样才能让它自己弹出而不是鼠标悬停时弹出 If the
  • 当另一个线程可能设置共享布尔标志(最多一次)时,是否可以读取共享布尔标志而不锁定它?

    我希望我的线程能够更优雅地关闭 因此我尝试实现一个简单的信号机制 我不认为我想要一个完全事件驱动的线程 所以我有一个工作人员有一种方法可以使用关键部分优雅地停止它Monitor 相当于C lock我相信 绘图线程 h class Drawi
  • 如何使用 Word Automation 获取页面范围

    如何使用办公自动化找到 Microsoft Word 中第 n 页的范围 似乎没有 getPageRange n 函数 并且不清楚它们是如何划分的 这就是您从 VBA 执行此操作的方法 转换为 Matlab COM 调用应该相当简单 Pub
  • 在客户端系统中安装后桌面应用程序无法打开

    我目前正在使用 Visual Studio 2017 和 4 6 1 net 框架 我为桌面应用程序创建了安装文件 安装程序在我的系统中完美安装并运行 问题是安装程序在其他计算机上成功安装 但应用程序无法打开 edit 在客户端系统中下载了

随机推荐