1

Создал новый проект, в нём определил свою кнопку с новым свойством "TextField_1":

public partial class MyButton : Button
    {
        public string TextField_1
        {
            get; set;
        }
    [Bindable(true, BindingDirection.TwoWay)]
    [Category("TextField_1")]
    public static readonly DependencyProperty TextField_1Property = DependencyProperty.RegisterAttached(
        "TextField_1",
        typeof(string),
        typeof(MyButton),
        new FrameworkPropertyMetadata("", new PropertyChangedCallback(OnTextField_1Changed))
        );

    public static void SetTextField_1(UIElement obj, string value)
    {
        obj.SetValue(TextField_1Property, value);
    }

    public static string GetTextField_1(UIElement obj)
    {
        return (string)obj.GetValue(TextField_1Property);
    }

    private static readonly DependencyPropertyKey TextField_1PropertyKey = DependencyProperty.RegisterReadOnly(
        "TextField_1PropertyKey", 
        typeof(bool), 
        typeof(MyButton), 
        new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.None));

    private static void OnTextField_1Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        MyButton MycontentControl = d as MyButton;
        MycontentControl.SetValue(TextField_1PropertyKey, e.NewValue != null);
        MycontentControl.OnContentChanged((string)e.OldValue, (string)e.NewValue);
    }

}

По сути, "TextField_1" добавляет второе свойство "Content". Потом создал кнопку в XAML:

<local:MyButton x:Name="button" Content="Button" HorizontalAlignment="Left" Margin="80,65,0,0" VerticalAlignment="Top" Width="230" Height="300" Style="{DynamicResource MyButtonStyle1}"/>

и стиль для неё:

<Style x:Key="MyButtonStyle1" TargetType="{x:Type local:MyButton}">
            <Setter Property="Background" Value="#FFDDDDDD"/>
            <Setter Property="BorderBrush" Value="#FF707070"/>
            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="HorizontalContentAlignment" Value="Center"/>
            <Setter Property="VerticalContentAlignment" Value="Center"/>
            <Setter Property="Padding" Value="1"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type local:MyButton}">
                        <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}">
                            <Grid x:Name="grid" HorizontalAlignment="Center" Margin="0" VerticalAlignment="Center" Width="{Binding ActualWidth, ElementName=border}" Height="{Binding ActualHeight, ElementName=border}">
                                <Label x:Name="label" Content="TextField_1" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top"/>
                                <TextBox x:Name="textBox1" HorizontalAlignment="Left" Height="23" Margin="10,40.96,0,0" TextWrapping="Wrap" Text="{TemplateBinding TextField_1}" VerticalAlignment="Top" Width="120"/>
                                <Label x:Name="label_Copy1" Content="Content" HorizontalAlignment="Left" Margin="10,86.04,0,0" VerticalAlignment="Top"/>
                                <TextBox x:Name="textBox2" HorizontalAlignment="Left" Height="23" Margin="10,117,0,0" TextWrapping="Wrap" Text="{TemplateBinding Content}" VerticalAlignment="Top" Width="120"/>
                            </Grid>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

В стиле поле "Text" у "textBox1" связал со своим новым свойством "{TemplateBinding TextField_1}".

В самом дизайнере, если зайти в свойства кнопки и вручную изменить поле "TextField_1", то установленное значение сразу же отображается в "textBox1". Но после запуска программы, если программно установить значение для "TextField_1", то само значение меняется, а вот "Text" у "textBox1" нет. Я так понимаю, я где-то не правильно создал привязку.

Помоги, пожалуйста, я уже всё перепробовал, даже метод "тыка" не помогает...

*П.С. Если использовать родное свойство "Content", то всё прекрасно.

П.П.С. Создание "UserControl" с "InitializeComponent" не помогает. "ContentPresenter" так же ни на что не влияет.*

1 Answers1

2

Ваше присоединенное свойство просто не поддерживает по умолчанию двухстороннюю привязку. Но это можно исправить.

Аттрибут

[Bindable(true, BindingDirection.TwoWay)]

Ни на что не влияет в поведении свойства, это всего-лишь метаданные для визуального редактора WPF.

Переключить поведение по умолчанию можно вот так:

public static readonly DependencyProperty TextField_1Property = DependencyProperty.RegisterAttached(
    "TextField_1",
    typeof(string),
    typeof(MyButton),
    new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnTextField_1Changed))
);

Так же изучите другие флаги FrameworkPropertyMetadataOptions и на что они влияют (ссылка).

Второй нюанс - это то что TemplateBinding всегда OneWay (ссылка).

Нужно вместо

<TextBox Text="{TemplateBinding TextField_1}"/>

Использовать

<TextBox Text="{Binding TextField_1, RelativeSource={RelativeSource TemplatedParent}}"/>

В сумме с FrameworkPropertyMetadataOptions.BindsTwoWayByDefault это даст нужный вам эффект.

И последнее: свойство есть, а к зависимости не подключено.

public string TextField_1
{
    get; set;
}

Надо вот так

public string TextField_1
{ 
    get => (string)GetValue(TextField_1Property);
    set => SetValue(TextField_1Property, value);
}  

P. S. Style="{DynamicResource MyButtonStyle1}" - используйте StaticResource.

aepot
  • 49,560
  • Спасибо за развёрнутый ответ, сделал как вы написали - результата нет. Видимо, дело в другом. К слову, изучал как привязан "Content", так там вообще строки "FrameworkPropertyMetadataOptions.BindsTwoWayByDefault" нету. Может дело в событии "OnTextField_1Changed"? – AlexSkornyakov May 11 '21 at 06:43
  • @AlexSkornyakov нет, всё проще, я пропустил один баг. Обновил ответ. BindsTwoWayByDefault отвечает за то, надо вам Mode=TwoWay в XAML по умолчанию прописывать или нет. – aepot May 11 '21 at 07:17
  • 1
    большое спасибо, теперь всё работает! П.С. Этого "бага" изначально не было, т.е. у меня так и было написано, но не работало и я переделал... Ирония) – AlexSkornyakov May 11 '21 at 07:23
  • к слову: Text="{TemplateBinding TextField_1}" тоже работает). А "OnTextField_1Changed" вообще не нужен – AlexSkornyakov May 11 '21 at 07:26
  • @AlexSkornyakov возможно оно и так, но я даже ссылку на документацию дал, где написано, что TemplateBinding - односторонняя привязка, хотя быть может опция BindsTwoWayByDefault дала эффект, не знаю. – aepot May 11 '21 at 07:28
  • 1
    @ aepot, да, я прочитал ту статью, просто ради любопытства решил проверить другие варианты). Забавно ещё другое: у меня в другом проекте сделано так же, как и в этом (только без вывода данных на экран) и всё работает. В любом случае, ещё раз большое спасибо за помощь – AlexSkornyakov May 11 '21 at 07:29