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