WPF TextBox: Как изменить режим привязки по умолчанию на OneWay? - PullRequest
7 голосов
/ 24 марта 2011

Изначально у меня есть следующий код:

<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, потому что мое свойство только для получения, а не потому, что я отметил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;            
    }
}

Ответы [ 3 ]

4 голосов
/ 24 марта 2011

Поскольку это текстовое поле доступно только для чтения, режим привязки не может быть двухсторонним.

Почему бы и нет?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.

2 голосов
/ 13 декабря 2016

Я знаю, что этот вопрос действительно старый, но недавно я сам столкнулся с этой проблемой, поэтому, возможно, я смогу помочь кому-то еще.

Я хотел создать TextBox, у которого OneWayBinding имеет свойство Text. Я обнаружил, что это не работает, как показано в вопросе, потому что WPF объединяет существующие метаданные и переопределяющие метаданные вместе, в основном объединяя флаги в OR. Поскольку BindsTwoWayByDefault является одним из таких флагов, пока один из объектов метаданных имеет BindsTwoWayByDefault = true, он остается истинным.

Единственный способ обойти метаданные после того, как процесс слияния WPF происходит в OverrideMetadata. Однако объект метаданных помечен как закрытый в методе.

Как любой хороший разработчик, я бы остановился здесь и пересмотрел ... Надеюсь, я использовал отражение, чтобы "распечатать" объект метаданных и установить для BindsTwoWayByDefault значение true.

Если кто-нибудь знает лучший способ сделать это, пожалуйста, дайте мне знать.

Вот мой код:

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();
    }
}
2 голосов
/ 24 марта 2011

Стили - это способ применить один и тот же набор настроек к одному или нескольким свойствам объектов пользовательского интерфейса, например, Background, IsReadOnly и т. Д., Которые обычно являются свойствами зависимости.

Режим - это свойство объекта Binding, который не является объектом пользовательского интерфейса.

Вы можете установить стиль для любого элемента который происходит от FrameworkElement или FrameworkContentElement. - Источник (MSDN)

Так что обычно это не делается с помощью XAML / Styling ... я думаю, вам придется написать код для него. Хотя XAML позволяет вам устанавливать вложенные свойства Text.Mode = "value", он подвержен ошибкам (поскольку предполагает, что Text уже был установлен для объекта привязки). Это приведет к исключению привязки, если свойство Text возвращает объект, у которого нет свойства Mode - например, if Text = "простая строка".

Если вам это абсолютно необходимо, то вам необходимо создать привязку программно. Вы можете использовать соглашение об именовании, например, чтобы увидеть, есть ли у свойства поддержки задатчик, и добавить привязку OneWay, если это не так.

...