【问题标题】:WPF TextBox: How to change binding mode default to OneWay?WPF TextBox:如何将绑定模式默认更改为 OneWay?
【发布时间】:2011-07-21 19:23:10
【问题描述】:

最初,我有以下代码:

<TextBox Text="{Binding LengthUnit, Mode=OneWay}" IsReadOnly="True" Background="{x:Static SystemColors.ControlBrush}" />

我知道我可以定义这样的样式:

<Style TargetType="{x:Type TextBox}" x:Key="readOnlyTextBox">
    <Setter Property="Background" Value="{x:Static SystemColors.ControlBrush}"></Setter>
    <Setter Property="IsReadOnly" Value="True"></Setter>
</Style>

这样我就可以写了:

<TextBox Text="{Binding LengthUnit, Mode=OneWay}" Style="{StaticResource readOnlyTextBox}" />

因为这个文本框是只读的,所以绑定模式不能是双向的。那么,是否可以将 OneWay 绑定作为我的 TextBox 的默认绑定?

编辑:我需要将绑定模式更改为 OneWay,因为我的属性是 get-only,而不是因为我将 TextBox 标记为只读。但是,如果可能,我仍然想将文本框的默认绑定模式更改为 OneWay。


这是我按照您的建议编写的代码,但它不起作用。我错过了什么吗?

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

【问题讨论】:

  • @milliu,如果它是 ReadOnly 那么你为什么要让它成为 OneWay?恕我直言,为单个属性创建单独的控件不是一个健康的想法。
  • @Prince,正如我的更新所示,OneWay 是由于我的 get-only 属性。

标签: wpf binding textbox binding-mode


【解决方案1】:

因为这个文本框是只读的,所以绑定模式不能是双向的。

为什么不呢? 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,这意味着除非通过代码更新绑定的属性,否则不会更新绑定属性,从而有效地进行绑定 OneWay。

【讨论】:

  • @foson:非常感谢您的回答。这种方法应该没问题,因为我实际上一直在考虑创建一个派生的 ReadOnlyTextBox 类。至于绑定模式,你是对的。当我从 xaml 中删除 Mode=OneWay 时,我收到此异常:“TwoWay 或 OneWayToSource 绑定无法对 'xxx' 类型的只读属性 'LengthUnit' 起作用。”我没有更仔细地查看此消息。问题是我的绑定源属性只有一个吸气剂。因此,它与文本框的 IsReadOnly 无关。再次感谢!
  • @foson:我刚刚测试了您建议的代码,但它不起作用。我仍然收到异常:“TwoWay 或 OneWayToSource 绑定无法对 'xxx' 类型的只读属性 'LengthUnit' 起作用。”。我将在答案部分发布我的代码以使其更具可读性。
  • @Prince:它对你有用吗?它不适合我。我也尝试使用 TextProperty.AddOwner(...),但得到了同样的错误。
  • @miliu:您是要将此标记为正确并开始一个新问题,还是编辑问题以包含该属性是仅获取的?
  • @foson:正如我所说,您建议的解决方案不起作用(即它不会将 TextBox 的默认绑定模式更改为 OneWay)。这与我的属性是 get-only 的事实无关。
【解决方案2】:

样式是一种将同一组自定义设置应用于 UI 对象的一个​​或多个属性的方法,例如背景、IsReadOnly 等通常是依赖属性。

Mode 是 Binding 对象的属性,它不是 UI 对象。

您可以在任何元素上设置样式 派生自 FrameworkElement 或 框架内容元素。 -- Source (MSDN)

所以这通常不是通过 XAML/Styling 完成的。我猜你必须为它编写代码。尽管 XAML 允许您设置嵌套属性 Text.Mode="value",但它很容易出错(因为它假定 Text 已经设置为绑定对象)。如果 Text 属性返回一个没有 Mode 属性的对象,它将导致绑定异常 - 例如if Text="a 普通字符串"。

如果您绝对必须拥有它,那么您需要以编程方式创建绑定。例如,您可以使用命名约定来查看支持属性是否具有 setter,如果没有,则添加 OneWay 绑定。

【讨论】:

  • 非常感谢您的解释非常有帮助。
【解决方案3】:

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

我想创建一个在其 Text 属性上有 OneWayBinding 的 TextBox。 我发现这不像问题中所示的那样工作,因为 WPF 通过基本上将标志 ORing 将现有元数据和覆盖元数据组合在一起。 由于 BindsTwoWayByDefault 是这些标志之一,只要元数据对象之一具有 BindsTwoWayByDefault=true 就保持为真。

解决此问题的唯一方法是在 WPF 合并过程发生在 OverrideMetadata 中之后更改元数据。 但是 Metadata 对象在方法中被标记为 Sealed。

作为任何优秀的开发人员,我会在这里停下来重新考虑...... 不,我使用反射“解封”元数据对象并将 BindsTwoWayByDefault 设置回 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();
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-10-26
    • 2015-08-14
    • 2015-09-05
    • 1970-01-01
    • 2017-05-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多