WPF文本框:如何将绑定模式默认更改为单向?

WPF文本框:如何将绑定模式默认更改为单向?,wpf,binding,textbox,binding-mode,Wpf,Binding,Textbox,Binding Mode,最初,我有以下代码: <TextBox Text="{Binding LengthUnit, Mode=OneWay}" IsReadOnly="True" Background="{x:Static SystemColors.ControlBrush}" /> 因为这个文本框是只读的,所以绑定模式不能是双向的 为什么不呢?IsReadOnly将阻止用户修改文本,从而修改属性。只需确保不要修改代码中的Text属性 如果将TextBox子类化,则可以防止绑定属性更新。如果这样做,则可

最初,我有以下代码:

<TextBox Text="{Binding LengthUnit, Mode=OneWay}" IsReadOnly="True" Background="{x:Static SystemColors.ControlBrush}" />
因为这个文本框是只读的,所以绑定模式不能是双向的

为什么不呢?IsReadOnly将阻止用户修改文本,从而修改属性。只需确保不要修改代码中的Text属性

如果将TextBox子类化,则可以防止绑定属性更新。如果这样做,则可以覆盖TextBox.Text依赖项属性元数据

public class TextBoxEx : TextBox
{
    public TextBoxEx() : base() { }

    static TextBoxEx()
    {
        TextBox.TextProperty.OverrideMetadata(typeof(TextBoxEx), 
            new FrameworkPropertyMetadata() { BindsTwoWayByDefault = false, Journal = true,
                DefaultUpdateSourceTrigger = System.Windows.Data.UpdateSourceTrigger.Explicit });
    }

}

出于某种原因,将BindsTwoWayByDefault更改为false对我不起作用,但您可以将DefaultUpdateSourceTrigger设置为Explicit,这意味着绑定属性不会更新,除非通过代码进行更新,有效地使绑定单向。

样式是一种将同一组自定义应用于UI对象的一个或多个属性的方法,例如后台、IsReadOnly等,它们通常是依赖性属性

模式是绑定对象的属性,它不是UI对象

可以在任何元素上设置样式 从FrameworkElement或 框架内容元素。-

因此,这通常不是通过XAML/样式实现的。。我猜你必须为它编写代码。尽管XAML允许您设置嵌套属性Text.Mode=“value”,但它很容易出错(因为它假定文本已经设置为绑定对象)。如果Text属性返回的对象上没有Mode属性,则会导致绑定异常,例如,如果Text=“纯字符串”


如果您必须拥有这个,那么您需要以编程方式创建绑定。例如,您可以使用命名约定来查看backing属性是否有setter,如果没有,则添加单向绑定。

我知道这个问题很老,但我最近自己也遇到了这个问题,所以也许我可以帮助其他人

我想创建一个文本框,它的文本属性上有一个单向绑定。 我发现这并没有像问题中所示那样起作用,因为WPF基本上通过对标志进行ORing将现有元数据和覆盖元数据结合在一起。 由于BindsTwoWayByDefault是这些标志之一,只要其中一个元数据对象具有BindsTwoWayByDefault=true,则保持为true

唯一的解决方法是在OverrideMetadata中发生WPF合并过程后更改元数据。 但是,元数据对象在方法中标记为密封

作为一个优秀的开发人员,我停在这里重新考虑。。。 Naaa,我使用反射来“解封”元数据对象,并将bindstwaybydefault设置回false

如果有人知道更好的方法,请告诉我

这是我的代码:

public partial class SelectableTextBlock : TextBox
{
    static SelectableTextBlock()
    {
        var defaultMetadata = (FrameworkPropertyMetadata)TextProperty.GetMetadata(typeof(TextBox));

        var newMetadata = new FrameworkPropertyMetadata(
            defaultMetadata.DefaultValue,
            FrameworkPropertyMetadataOptions.Journal,
            defaultMetadata.PropertyChangedCallback,
            defaultMetadata.CoerceValueCallback,
            defaultMetadata.IsAnimationProhibited,
            defaultMetadata.DefaultUpdateSourceTrigger);

        TextProperty.OverrideMetadata(typeof(SelectableTextBlock), newMetadata);

        //Workaround for a bug in WPF were the Metadata is merged wrongly and BindsTwoWayByDefault is always true
        var sealedProperty = typeof(PropertyMetadata).GetProperty("Sealed", BindingFlags.Instance | BindingFlags.NonPublic);
        sealedProperty.SetValue(newMetadata, false);
        newMetadata.BindsTwoWayByDefault = false;
        sealedProperty.SetValue(newMetadata, true);
    }

    public SelectableTextBlock()
    {
        InitializeComponent();
    }
}

@福森:非常感谢你的回答。这种方法应该可以,因为我实际上正在考虑创建一个派生的ReadOnlyTextBox类。至于绑定模式,你是对的。当我从xaml中删除Mode=OneWay时,我得到了一个异常:“TwoWay或OneWayToSource绑定不能在类型为'xxx'的只读属性'LengthUnit'上工作。”我没有更仔细地看这条消息。问题是我的绑定资源属性只有一个getter。因此,它与文本框的IsReadOnly无关。再次感谢@我刚刚测试了你建议的代码,但它不起作用。我仍然得到一个例外:“双向或单向源绑定不能在类型为“xxx”的只读属性“LengthUnit”上工作。”。我会把我的代码贴在“答案”部分,让它更具可读性。@Prince:对你有用吗?这对我不合适。我还尝试使用TextProperty.AddOwner(…),但出现了相同的错误。@miliu:是否要将此标记为正确并开始一个新问题,或者编辑问题以包含属性为get only?@foson:正如我所说,您建议的解决方案不起作用(即,它不会将TextBox的默认绑定模式更改为单向)。这与我的属性是只读的这一事实无关。非常感谢您的解释,这非常有帮助。@milliu,如果它将是只读的,为什么您希望它是单向的?imho为单个属性创建单独的控件不是一个健康的想法。@Prince,正如我的更新所指出的,单向是由于我的get-only属性。
public class ReadOnlyTextBox : TextBox
{
    static ReadOnlyTextBox()
    {
        TextBox.TextProperty.OverrideMetadata(typeof(ReadOnlyTextBox), 
            new FrameworkPropertyMetadata() { BindsTwoWayByDefault = false, Journal = true, DefaultUpdateSourceTrigger = UpdateSourceTrigger.Explicit }); 
    }
    public ReadOnlyTextBox()
    {
        base.Background = SystemColors.ControlBrush;
        base.IsReadOnly = true;            
    }
}
public class TextBoxEx : TextBox
{
    public TextBoxEx() : base() { }

    static TextBoxEx()
    {
        TextBox.TextProperty.OverrideMetadata(typeof(TextBoxEx), 
            new FrameworkPropertyMetadata() { BindsTwoWayByDefault = false, Journal = true,
                DefaultUpdateSourceTrigger = System.Windows.Data.UpdateSourceTrigger.Explicit });
    }

}
public partial class SelectableTextBlock : TextBox
{
    static SelectableTextBlock()
    {
        var defaultMetadata = (FrameworkPropertyMetadata)TextProperty.GetMetadata(typeof(TextBox));

        var newMetadata = new FrameworkPropertyMetadata(
            defaultMetadata.DefaultValue,
            FrameworkPropertyMetadataOptions.Journal,
            defaultMetadata.PropertyChangedCallback,
            defaultMetadata.CoerceValueCallback,
            defaultMetadata.IsAnimationProhibited,
            defaultMetadata.DefaultUpdateSourceTrigger);

        TextProperty.OverrideMetadata(typeof(SelectableTextBlock), newMetadata);

        //Workaround for a bug in WPF were the Metadata is merged wrongly and BindsTwoWayByDefault is always true
        var sealedProperty = typeof(PropertyMetadata).GetProperty("Sealed", BindingFlags.Instance | BindingFlags.NonPublic);
        sealedProperty.SetValue(newMetadata, false);
        newMetadata.BindsTwoWayByDefault = false;
        sealedProperty.SetValue(newMetadata, true);
    }

    public SelectableTextBlock()
    {
        InitializeComponent();
    }
}