Espeon
Espeon

Reputation: 206

WPF binded property does not updates source value

I have DependencyProperty in my custom control. I bound it to some property in some object. When my control appears, it takes value from source and applies it to itself. But when I type something in the input field, source value doesn't change. What am I doing wrong?

Control declaration:

public class ScriptComponentField : Control
{
    public enum Datatype
    {
        String,
        Integer,
        Real,
        Boolean,
        Enumerable,
        Code
    }

    public string Header
    {
        get { return (String)GetValue(HeaderProperty); }
        set { SetValue(HeaderProperty, value); }
    }
    public static readonly DependencyProperty HeaderProperty = DependencyProperty.RegisterAttached(nameof(Header), typeof(string), typeof(ScriptComponentField), new PropertyMetadata(""));

    public object Value
    {
        get { return GetValue(ValueProperty); }
        set { SetValue(ValueProperty, value); }
    }
    public static readonly DependencyProperty ValueProperty = DependencyProperty.RegisterAttached(nameof(Value), typeof(object), typeof(ScriptComponentField), new PropertyMetadata(null));

    public Datatype ValueType
    {
        get { return (Datatype)GetValue(ValueTypeProperty); }
        set { SetValue(ValueTypeProperty, Value); }
    }
    public static readonly DependencyProperty ValueTypeProperty = DependencyProperty.RegisterAttached(nameof(ValueType), typeof(Datatype), typeof(ScriptComponentField), new PropertyMetadata(Datatype.String));

    public List<string> EnumerableItems
    {
        get { return (List<string>)GetValue(EnumerableItemsProperty); }
        set { SetValue(EnumerableItemsProperty, Value); }
    }
    public static readonly DependencyProperty EnumerableItemsProperty = DependencyProperty.RegisterAttached(nameof(EnumerableItems), typeof(List<string>), typeof(ScriptComponentField), new PropertyMetadata(null));

    static ScriptComponentField()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(ScriptComponentField), new FrameworkPropertyMetadata(typeof(ScriptComponentField)));
    }
}

Property Value has object type just because it can contain any value like string, bool, int and etc.

Control template:

<Style TargetType="{x:Type Controls:ScriptComponentField}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Controls:ScriptComponentField}">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="150" SharedSizeGroup="name"/>
                        <ColumnDefinition SharedSizeGroup="value"/>
                    </Grid.ColumnDefinitions>
                    <Label Content="{TemplateBinding Header}" HorizontalAlignment="Right" VerticalAlignment="Center"/>
                    <TextBox x:Name="InputString" Visibility="Collapsed" Grid.Column="1" Text="{TemplateBinding Value}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" VerticalContentAlignment="Center" Margin="0,2"/>
                    <Advanced:IntegerUpDown x:Name="InputInteger" Visibility="Collapsed" Grid.Column="1" Value="{TemplateBinding Value}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" VerticalContentAlignment="Center" Margin="0,2"/>
                    <CheckBox x:Name="InputBoolean" Visibility="Collapsed" Grid.Column="1" IsChecked="{TemplateBinding Value}" VerticalAlignment="Center"/>
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="ValueType" Value="String">
                        <Setter TargetName="InputString" Property="Visibility" Value="Visible"/>
                    </Trigger>
                    <Trigger Property="ValueType" Value="Integer">
                        <Setter TargetName="InputInteger" Property="Visibility" Value="Visible"/>
                    </Trigger>
                    <Trigger Property="ValueType" Value="Boolean">
                        <Setter TargetName="InputBoolean" Property="Visibility" Value="Visible"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Upvotes: 1

Views: 80

Answers (2)

MaxB
MaxB

Reputation: 438

You might have missed the "Mode=TwoWay" property, as follows:

<Button x:Name="btnMy" Content="{Binding MyText, Mode=TwoWay}" />

Default control binding is "OneWay" (See EDIT below), which "Updates the binding target (target) property when the binding source (source) changes." © MSDN link below

TwoWay Binding: "Causes changes to either the source property or the target property to automatically update the other" © MSDN link below

MSDN BindingMode

EDIT:

As noted by Clemens, regarding the default binding mode: "The default mode is set during registration of a property, by FrameworkPropertyMetadataOptions." © Clemens
Is short, it isn't always OneWay, but most of the times.

Upvotes: 0

Rekshino
Rekshino

Reputation: 7325

The problem is that you use TemplateBinding. TemplateBinding is always OneWay. If you change your TemplateBinding to the TwoWay Binding with RelativeSource or with ElementName it will work.

<TextBox x:Name="InputString" Visibility="Collapsed" Grid.Column="1" Text="{Binding Value, RelativeSource={RelativeSource AncestorType=Controls:ScriptComponentField}, Mode=TwoWay}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" VerticalContentAlignment="Center" Margin="0,2"/>

Upvotes: 1

Related Questions