I wanted to create a readonly TextBox that has a default OneWay Binding on its Text property. Like in a very similar question my first try was this:
public partial class SelectableTextBlock : TextBox
{
static SelectableTextBlock ()
{
TextBox.TextProperty.OverrideMetadata(typeof(ReadOnlyTextBox),
new FrameworkPropertyMetadata() { BindsTwoWayByDefault = false, Journal = true, DefaultUpdateSourceTrigger = UpdateSourceTrigger.Explicit });
}
public SelectableTextBlock()
{
InitializeComponent();
}
}
I discovered that this is does not work because WPF combines the existing metadata and the overriding metadata by basically ORing all flags together. Since BindsTwoWayByDefault is one of those flags, as long as one of the Metadata objects has BindsTwoWayByDefault=true is stays true.
The only way around that is to change the Metadata after the WPF merging process takes places in OverrideMetadata. However the Metadata object is marked as Sealed in the method.
As any good developer would I stopped here and reconsidered... Naaa, I used reflection to "unseal" the metadata object and set the BindsTwoWayByDefault back to true.
Please tell me that I am stupid and did not see the obvious and correct way of doing this in WPF.
Here my code:
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();
}
}