WPF- DataTemplate中TextBox的问题

时间:2010-10-10 18:43:21

标签: wpf textbox datatemplate

我正在开发一个应用程序,其中Repository个对象通过DataTemplate显示,该DataTemplate包含TextBox版本的SelectionStart,支持绑定到SelectionLength,{ {1}}和VerticalOffset

DataTemplate看起来像这样:

<DataTemplate DataType="{x:Type m:Repository}">
<controls:ModdedTextBox 
x:Name="textBox" Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}"  
BindableSelectionStart="{Binding SelectionStart, UpdateSourceTrigger=PropertyChanged}" 
BindableSelectionLength="{Binding SelectionLength, UpdateSourceTrigger=PropertyChanged}"
BindableVerticalOffset="{Binding VerticalOffset, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>

问题是当我更改当前显示的Repository时; SelectionStartSelectionLengthVerticalOffset似乎都设置为0,即使Repository对象的属性不是0。

我认为,当SelectionStartSelectionLengthVerticalOffset不能超过0时,会在文本显示之前发生这种情况。这不仅会设置TextBox的实际属性为零,但也更新绑定并将Repository对象的属性设置为零。

有什么方法可以防止这种情况发生吗?

- 编辑 -

我不知道发布dl链接到项目是否是禁止或不是SO,但这里是我创建的项目的链接,以证明我遇到的问题:modified

当您运行演示应用程序时,可以单击“切换存储库”按钮以更改文本框中显示的存储库。如果查看文本框的右侧,当切换到另一个时,当前存储库的属性都将设置为零。

此演示与我的实际应用程序之间的区别在于,我的应用程序存储库将通过热键而不是按钮进行切换。

3 个答案:

答案 0 :(得分:1)

问题是由于绑定是以串行方式进行评估的,并且当Text属性被更改时,它会导致所有选择信息被删除(您可以通过在{{1}上放置断点来看到这一点。事件处理程序)。由于BindableSelection ...绑定在此时仍处于活动状态,因此会重置选择信息。

根据您想要的确切行为,可能有办法解决这个问题,但您需要了解更多细节......

根据评论进行编辑: 这个解决方案不是完全回答你的原始问题,它可能不是很好的做法,但它确实至少有用......

尝试更改ModdedTextBox,以便不显示选择信息的可绑定属性,而是公开一个类型为Repository的DP并绑定到它:

ModdedTextBox

然后在DP上处理已更改的事件以设置文本框属性:

<local:ModdedTextBox
               x:Name="textBox" 
                Repository="{Binding CurrentRepository}"
               TextWrapping="Wrap"
               />

这基本上消除了绑定评估的串行性质。

注意:你也可以使用附加属性实现相同的功能,这比继承TextBox更好,但这更接近你原来的尝试,所以我觉得它更容易解释!

答案 1 :(得分:0)

这是对其他解决方案的重写。这个考虑了未在其他属性之前绑定的text属性。


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;

namespace WpfApplication1
{
    public class SelectionBindingTextBox : TextBox
    {
        public static readonly DependencyProperty BindableSelectionStartProperty =
            DependencyProperty.Register(
            "BindableSelectionStart",
            typeof(int),
            typeof(SelectionBindingTextBox),
            new PropertyMetadata(OnBindableSelectionStartChanged));

        public static readonly DependencyProperty BindableSelectionLengthProperty =
            DependencyProperty.Register(
            "BindableSelectionLength",
            typeof(int),
            typeof(SelectionBindingTextBox),
            new PropertyMetadata(OnBindableSelectionLengthChanged));

        private bool isBindingComplete = false;

        public SelectionBindingTextBox()
            : base()
        {
            this.SelectionChanged += this.OnSelectionChanged;
            this.TextChanged += this.OnTextChanged;
        }

        public int BindableSelectionStart
        {
            get
            {
                return (int)this.GetValue(BindableSelectionStartProperty);
            }

            set
            {
                this.SetValue(BindableSelectionStartProperty, value);
            }
        }

        public int BindableSelectionLength
        {
            get
            {
                return (int)this.GetValue(BindableSelectionLengthProperty);
            }

            set
            {
                this.SetValue(BindableSelectionLengthProperty, value);
            }
        }


        private static void OnBindableSelectionStartChanged(DependencyObject dependencyObject, 
            DependencyPropertyChangedEventArgs args)
        {
            var textBox = dependencyObject as SelectionBindingTextBox;

            if (textBox.isBindingComplete)
            {
                textBox.SetupSelection();
            }
        }

        private static void OnBindableSelectionLengthChanged(DependencyObject dependencyObject, 
            DependencyPropertyChangedEventArgs args)
        {
            var textBox = dependencyObject as SelectionBindingTextBox;
            if (textBox.isBindingComplete)
            {
                textBox.SetupSelection();
            }
        }

        private void OnSelectionChanged(object sender, RoutedEventArgs e)
        {
            if (isBindingComplete)
            {
                this.BindableSelectionStart = this.SelectionStart;
                this.BindableSelectionLength = this.SelectionLength;
            }
        }

        private void OnTextChanged(object sender, RoutedEventArgs e)
        {
            if (!isBindingComplete)
            {
                SetupSelection();
            }
            isBindingComplete = true;
        }

        private void SetupSelection()
        {
           // this.Focus();
            this.SelectionLength = this.BindableSelectionLength;
            this.SelectionStart = this.BindableSelectionStart;
        }
    }
}

答案 2 :(得分:0)

绑定的更新取决于WPF或Silverlight Engine将评估的顺序,看起来您的SelectionStart和SelectionEnd绑定在Text之前更新,因此当Text更改时,SelectionStart和SelectionEnd都会更改回零。

唯一的方法是挂钩TextChanged事件并刷新SelectionStart和SelectionEnd的绑定,或者在WPF中你可以扩展文本框,如下所示

public class MyTextBox : TextBox{

    protected override OnTextChanged(TextChangedEventArgs e){
        BindingExpression be = this.GetBindingExpression(SelectionStartProperty);
        if(be!=null){
             be.UpdateTarget();
        }
        be = this.GetBindingExpression(SelectionEndProperty);
        if(be!=null){
             be.UpdateTarget();
        }
        be = this.GetBindingExpression(VerticalOffsetProperty);
        if(be!=null){
             be.UpdateTarget();
        }
    }

}

这里有一个技巧,你仍然需要更改上面的逻辑以适应你的逻辑,因为每次文本更新这将更新绑定,所以你必须找出何时刷新这些绑定。因为这将无法在运行时更改文本框的值,因为文本将修改,选择将仅转到以前的选择。