Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
WPF-数据模板中的文本框存在问题_Wpf_Textbox_Datatemplate - Fatal编程技术网

WPF-数据模板中的文本框存在问题

WPF-数据模板中的文本框存在问题,wpf,textbox,datatemplate,Wpf,Textbox,Datatemplate,我正在开发一个应用程序,存储库对象通过一个数据模板显示,该数据模板包含一个版本的文本框,该文本框支持绑定到选择开始、选择长度和垂直偏移 DataTemplate如下所示: <DataTemplate DataType="{x:Type m:Repository}"> <controls:ModdedTextBox x:Name="textBox" Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}" Bind

我正在开发一个应用程序,
存储库
对象通过一个数据模板显示,该数据模板包含一个版本的
文本框
,该文本框支持绑定到
选择开始
选择长度
垂直偏移

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>

问题是,当我更改当前显示的
存储库
时;
SelectionStart
SelectionLength
VerticalOffset
似乎都设置为0,即使
存储库
对象的那些属性不是0

我认为这是在显示文本之前的瞬间发生的,此时
SelectionStart
SelectionLength
VerticalOffset
不能大于0。这不仅将
文本框
的实际属性设置为零,还将更新绑定并将
存储库
对象的属性设置为零

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

--编辑--

我不知道向项目发布dl链接是否为否,但这里有一个指向我创建的项目的链接,用于说明我遇到的问题:

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


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

这里是另一个解决方案的重写。这一项考虑了未在其他属性之前绑定的文本属性


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;
        }
    }
}


问题是由于绑定是串行计算的,当
Text
属性更改时,会导致删除所有选择信息(您可以通过在
ModdedTextBox
事件处理程序上放置断点来查看)。作为BindableSelection。。。此时绑定仍处于活动状态,这会导致重置选择信息

根据您想要的确切行为,可能有一种解决方法,但您需要了解更多细节

根据评论进行编辑: 这个解决方案并没有完全回答您最初的问题,它可能不是一个很好的实践,但它至少起到了作用

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

<local:ModdedTextBox
               x:Name="textBox" 
                Repository="{Binding CurrentRepository}"
               TextWrapping="Wrap"
               />
这从本质上消除了绑定求值的串行性


注意:您也可以使用附加属性实现同样的效果,这比子类化TextBox要好,但这更接近您最初的尝试,因此我认为这更容易解释

绑定的更新取决于WPF或Silverlight引擎的计算顺序,看起来您的SelectionStart和SelectionEnd绑定是在文本之前更新的,因此当文本发生更改时,SelectionStart和SelectionEnd都会更改回零

唯一的方法是挂接TextChanged事件并刷新SelectionStart和SelectionEnd的绑定,或者在WPF中,您可以如下扩展textbox

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();
        }
    }

}

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

文本框始终处于焦点位置。您能否发布有关如何创建可绑定***依赖项属性/将其绑定到基础选择属性的信息?您的绑定是否需要双向和双向是在PropertyChanged上触发的,还是你能逃脱单向或LostFocus的惩罚?我链接到这个问题:可绑定依赖属性就是这样创建的。@Steve,是的,它们必须是在PropertyChanged上触发的双向绑定。Repository对象的属性需要与ui同步,并且当前Repository对象的更改不会使文本框失去焦点。我不知道如何修复此问题。你能说得更详细些吗?你需要我提供更多的信息吗?我不确定它是否能像你期望的那样被“修复”。我的意思是,可能有另一种方法可以实现您想要的结果-您想对选择信息做什么?我希望它能够在我在存储库之间切换时恢复选择信息。
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();
        }
    }

}