我正在开发一个应用程序,其中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
时; SelectionStart
,SelectionLength
和VerticalOffset
似乎都设置为0,即使Repository
对象的属性不是0。
我认为,当SelectionStart
,SelectionLength
和VerticalOffset
不能超过0时,会在文本显示之前发生这种情况。这不仅会设置TextBox
的实际属性为零,但也更新绑定并将Repository
对象的属性设置为零。
有什么方法可以防止这种情况发生吗?
- 编辑 -
我不知道发布dl链接到项目是否是禁止或不是SO,但这里是我创建的项目的链接,以证明我遇到的问题:modified
当您运行演示应用程序时,可以单击“切换存储库”按钮以更改文本框中显示的存储库。如果查看文本框的右侧,当切换到另一个时,当前存储库的属性都将设置为零。
此演示与我的实际应用程序之间的区别在于,我的应用程序存储库将通过热键而不是按钮进行切换。
答案 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();
}
}
}
这里有一个技巧,你仍然需要更改上面的逻辑以适应你的逻辑,因为每次文本更新这将更新绑定,所以你必须找出何时刷新这些绑定。因为这将无法在运行时更改文本框的值,因为文本将修改,选择将仅转到以前的选择。