如何在KeyDown操作上触发转换器?

时间:2017-10-30 12:22:20

标签: c# wpf

我的任务是创建一个转换器,它将更改TextBox中的现有表达式。它应该像:

  1. 我们有一个非空的TextBox。 | 1000 |
  2. 我们输入一些额外的:
    • 数字:| 1000 + 5 * 2 |
    • 字符:| 1000 + abc |
  3. 现在,当按下Enter键时,转换器应该完成其工作(更改TextBox中的Text):
    • 数字(结果):| 1010 |
    • 字符(结果):| 1000 + abc |
  4. 正如您所看到的,我想评估一个数字表达式,而不对混合表达式执行任何操作。

    目前,只要TextBox发生任何变化,我的转换器就会自动运行。

    我的转换器:

    using System;
    using System.Data;
    using System.Windows.Data;
    using System.Globalization;
    using System.Windows;
    
    namespace TextBoxCalc
    {
    
        public class EvaluateTextBox : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                string exp = "";
                bool allow = true;
    
                foreach (char i in value.ToString())
                {
                    if (Int32.TryParse(i.ToString(), out int result) || i == '+' || i == '-' || i == '*' || i == '/' || i == '(' || i == ')')
                    {
                        exp += i;
                    }
                    else
                    {
                        exp += i;
                        allow = false;
                        continue;
                    }
                }
                if(exp != null && exp.Length != 0)
                if (exp[0] == '+' || exp[0] == '-' || exp[0] == '*' || exp[0] == '/')
                {
                    allow = false;
                }
    
                if (exp != null && exp.Length != 0)
                    if (exp[exp.Length - 1] == '+' || exp[exp.Length - 1] == '-' || exp[exp.Length - 1] == '*' || exp[exp.Length - 1] == '/')
                {
                    allow = false;
                }
    
                DataTable dt = new DataTable();
                if (allow)
                {
                    var v = dt.Compute(exp, "");
                    return v.ToString();
                }
    
                return exp;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                throw new NotSupportedException("ConvertBack should never be called");
            }
        }
    }
    

    我的XAML:

    <Window x:Class="TextBoxCalc.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:local="clr-namespace:TextBoxCalc"
            xmlns:conv="clr-namespace:TextBoxCalc"
            mc:Ignorable="d"
            Title="MainWindow" Height="350" Width="525">
        <Window.Resources>
            <conv:EvaluateTextBox x:Key="EvaluateTextBox"/>
        </Window.Resources>
        <Grid>
            <StackPanel>
                <TextBox x:Name="textBox" Height="50" Width="200"  Text="{Binding ElementName=textBox, Path=Text, Converter={StaticResource EvaluateTextBox}, Mode=OneWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}" />
            </StackPanel>
        </Grid>
    </Window>
    

3 个答案:

答案 0 :(得分:0)

在绑定表达式中,将UpdateSourceTrigger更改为显式。 然后在按键事件中,检查您的状况,并添加以下行。

BindingExpression binding = textBox.GetBindingExpression(TextBox.TextProperty);
binding.UpdateSource();

执行此操作意味着只有在按Enter键时才会更新数据绑定源。由于每当更新绑定时都会应用转换器,因此只有在单击按钮时才会执行转换。

另一种方法是添加一个IsDefault属性设置为true的按钮,并在按钮的Click事件处理程序中使用逻辑。只要按下回车键,该按钮就会被“点击”。

答案 1 :(得分:0)

将AcceptsReturn值设置为true:

<TextBox x:Name="textBox" AcceptsReturn="True" Text="{Binding ElementName=textBox, Path=Text, Converter={StaticResource EvaluateTextBox}, Mode=OneWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}"></TextBox>

更改转换器以检查换行符:

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (((string)value).EndsWith(Environment.NewLine))
        {
            string exp = "";
            bool allow = true;

            foreach (char i in value.ToString().TrimEnd(Environment.NewLine.ToCharArray()))
            {
                if (Int32.TryParse(i.ToString(), out int result) || i == '+' || i == '-' || i == '*' || i == '/' || i == '(' || i == ')')
                {
                    exp += i;
                }
                else
                {
                    exp += i;
                    allow = false;
                    continue;
                }
            }
            if (exp != null && exp.Length != 0)
                if (exp[0] == '+' || exp[0] == '-' || exp[0] == '*' || exp[0] == '/')
                {
                    allow = false;
                }

            if (exp != null && exp.Length != 0)
                if (exp[exp.Length - 1] == '+' || exp[exp.Length - 1] == '-' || exp[exp.Length - 1] == '*' || exp[exp.Length - 1] == '/')
                {
                    allow = false;
                }

            DataTable dt = new DataTable();
            if (allow)
            {
                var v = dt.Compute(exp, "");
                return v.ToString();
            }

            return exp;
        }
        else
        {
            return value;
        }
    }

答案 2 :(得分:0)

正如CKII所说,UpdateSourceTrigger=PropertyChanged与你想要的相反。

首先,我们将计算代码移动到静态辅助方法中。我不确定你的验证是做什么的所以我写了一个快速的测试。如果您对转换器逻辑感到满意,只需将其粘贴即可。

public static class Helpers
{
    public static String Calculate(object value)
    {
        return new DataTable().Compute($"{value}", "").ToString();
    }
}

我们将摆脱您在Binding上设置的大多数属性。 UpdateSourceTrigger=Explicit表示&#34;只有在我以编程方式调用UpdateSource()时才会执行此操作&#34;。我将使用{RelativeSource Self}因为它使XAML更容易复制和粘贴;我使用ElementName=textbox对其进行了测试,其中x:Name属性仍然存在,并且它的工作方式相同。

<TextBox
    KeyDown="textBox_KeyDown"
    Text="{Binding Text, RelativeSource={RelativeSource Self}, Converter={StaticResource Eval}, UpdateSourceTrigger=Explicit}"
    />

我们的keydown事件处理程序将显式更新仅返回的绑定,这将导致转换器启动。

private void textBox_KeyDown(object sender, KeyEventArgs e)
{
    if (e.Key == Key.Enter)
    {
        (sender as TextBox)?.GetBindingExpression(TextBox.TextProperty)?.UpdateSource();
    }
}

最后,您希望ConvertBack中的转换能够正常工作。我已将转换器重命名为EvaluateExpression,因为它没有以任何方式绑定到TextBoxes。

public class EvaluateExpression : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return value;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        try
        {
            return Helpers.Calculate(value);
        }
        catch
        {
            return value;
        }
    }
}

我使用viewmodel属性测试了相同的场景,它的工作方式相同:

<TextBox
    KeyDown="textBox_KeyDown"
    Text="{Binding InputText, Converter={StaticResource EvaluateExpression}, UpdateSourceTrigger=Explicit}"
    />

您可以轻松编写一个附加属性来设置update-source-on-return keydown事件。如果您只执行一次,那就太过分了,但如果您的应用程序周围散布了多个文本框,只需要在返回时进行更新,那么这值得做。