使用IMultiValueConverter

时间:2019-06-18 16:04:23

标签: wpf data-binding imultivalueconverter

我正在处理的应用程序要求限制绑定到多行TextBox的字符串中的行数和每行字符。这些限制可以在应用程序运行时更改。绑定到TextBox的viewmodel属性是一个字符串列表。应该防止用户输入超出限制的字符。

我创建了一个IMultiValueConverter,它可以完成两件事:1)使用Environment.NewLines将字符串列表转换为单个字符串,并在列表的元素之间添加2)根据用户类型将行和字符限制在一行。

这是我的转换器;绑定参数是字符串列表,最大行数和每行最大字符数:

    public class MultiLineTextConverter : IMultiValueConverter
    {
        private int MaxLines { get; set; }
        private int MaxCharactersPerLine { get; set; }

        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            if (!(values[1] is int maxLines) || !(values[2] is int maxCharactersPerLine))
            {
                return DependencyProperty.UnsetValue;
            }

            MaxLines = maxLines;
            MaxCharactersPerLine = maxCharactersPerLine;

            if (!(values[0] is IEnumerable<string> lines))
            {
                return DependencyProperty.UnsetValue;
            }

            return string.Join(Environment.NewLine, ClampLines(lines));
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            var text = (string) value;
            var lines = text.Split(new[] {Environment.NewLine}, StringSplitOptions.None);
            return new[] {ClampLines(lines), Binding.DoNothing, Binding.DoNothing};
        }

        private List<string> ClampLines(IEnumerable<string> lines)
        {
            return lines.Take(MaxLines).Select(l => l.Substring(0, Math.Min(l.Length, MaxCharactersPerLine))).ToList();
        }
    }

转换器在视图中的使用方式如下(转换器资源定义为x:Shared =“ False”,因为它对ConvertBack使用字符串限制状态):

                <TextBox
                    TextWrapping="Wrap"
                    AcceptsReturn="True">
                    <TextBox.Text>
                        <MultiBinding
                            UpdateSourceTrigger="PropertyChanged"
                            Converter="{StaticResource MultiLineTextConverter}">
                            <Binding
                                Mode="TwoWay"
                                Path="TextLines" />
                            <Binding
                                Path="LineLimit"
                                Mode="OneWay" />
                            <Binding
                                Source="{StaticResource CharactersPerLine}"
                                Mode="OneTime" />
                        </MultiBinding>
                    </TextBox.Text>
                </TextBox>

并且viewmodel属性为:

        private IReadOnlyCollection<string> _textLines;
        public IReadOnlyCollection<string> TextLines
        {
            get => _textLines;
            set
            {
                _textLines= value;
                OnPropertyChanged();
            }
        }

除一种情况外,转换器的运行效果非常好。如果最大行数为3,我可以输入:

A
B
C

,它阻止我在“ C”之后点击return。但是,如果我将光标放在B后面并按回车键,则转换器会将其转换回:

A
B
\r\n

将其正确设置为viewmodel属性设置器,从而引发INPC,这将导致调用viewmodel属性getter,该方法返回正确的值,然后通过Convert方法返回:

A
B
\r\n

因此,正确的值最终会使整个往返行程正确进行,就像您键入的任何其他值一样,但是视图仍显示:

A
B
\r\n
C

真正让我感到奇怪的是,如果我将Snoop附加到应用程序并检查视图,则数据上下文看起来正确(AB\r\n),而一旦检查了TextBox,视图就会刷新并看起来正确。从那时起,即使没有附加Snoop,它也可以正常工作。

我不经常这样说,但是这次我要使用“这看起来像是框架中的错误”的老板栗。但是,我想知道是否有解决方法,或者这是TextBox的已知错误。除此之外,我可能会将功能分解为附加行为和转换器。

0 个答案:

没有答案