Non-notified parentcontrol of UserControl changes DependencyProperty

I created a base ClassAthat contains DependencyProperty MyText. I received from him ClassB. This one ClassBis built in MyCustomButton. In MouseHover / MousePressed, I change the MyText value of the built-in ClassB class. But, while the parent ClassA fires the PropertyChanged-Event, the derived class B never changes the Property.

What would be the right way to notify the derived class B of a changing DependencyProperty? I already tried the solution mentioned in changing the DependencyProperty value of the base class in WPF ", but could not get this to work.

My code is:

The main window consists of only one MyCustomButton containing the ClassB-Instance:

<Window.Resources>
    <cc:ClassB x:Key="MyClassB" MyText="MyClassBText"/>
</Window.Resources>

<Grid>
    <StackPanel>
        <cc:MyCustomButton MyClassA="{StaticResource MyClassB}" Width="100" Height="100"/>
    </StackPanel>
</Grid> 

MyCustomButton ClassA (, B):

class MyCustomButton : Button
{
    public ClassA MyClassA
    {
        get { return (ClassA)GetValue(MyClassAProperty); }
        set { SetValue(MyClassAProperty, value); }
    }
    public static readonly DependencyProperty MyClassAProperty =
        DependencyProperty.Register("MyClassA", typeof(ClassA), typeof(MyCustomButton));
}

<Style TargetType="{x:Type cc:MyCustomButton}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type cc:MyCustomButton}">

                <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
                    <StackPanel Background="Transparent">
                        <cc:ClassA x:Name="ButtonClassA" Content="{Binding MyClassA, RelativeSource={RelativeSource TemplatedParent}}" />
                    </StackPanel>
                </Border>

                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="true">
                        <Setter TargetName="ButtonClassA" Property="MyText" Value="HOVER"/>
                    </Trigger>
                    <Trigger Property="IsPressed" Value="true">
                        <Setter TargetName="ButtonClassA" Property="MyText" Value="PRESSED"/>
                    </Trigger>
                </ControlTemplate.Triggers>

            </ControlTemplate>

        </Setter.Value>
    </Setter>
</Style>

ClassA DependencyProperty:

public class ClassA : UserControl
{
    public string MyText
    {
        get { return (string)GetValue(MyTextProperty); }
        set { SetValue(MyTextProperty, value); }
    }
    public static readonly DependencyProperty MyTextProperty =
        DependencyProperty.Register("MyText", typeof(string), typeof(ClassA), new PropertyMetadata("ClassA Default Text", MyTextPropertyChanged));

    private static void MyTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        Console.WriteLine("ClassA.MyTextPropertyChanged: " + e.NewValue);
    }

    public ClassA()
    {
    }
}

ClassB ClassA

static ClassB()
{
    MyTextProperty.AddOwner(typeof(ClassB), new PropertyMetadata(MyTextPropertyChanged));
}

private static void MyTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    Console.WriteLine("ClassB.MyTextPropertyChanged: " + e.NewValue); // This is just reached once at startup!
}

public ClassB()
{
    InitializeComponent();            
}

<local:ClassA 
         xmlns:local="clr-namespace:CustomControlPlayground.CustomControls"
         x:Class="CustomControlPlayground.CustomControls.ClassB"
         x:Name="ClassBXAML">

    <Grid>
        <TextBlock Text="{Binding MyText, ElementName=ClassBXAML}"/>
    </Grid>
</local:ClassA> 

Update:

, , , . , , . , ClassB MyClassB ClassA?

MainWindow.xaml:

<Window.Resources>
    <cc:ClassB x:Key="MyClassB" MyText="MyClassBText"/>
</Window.Resources>
<Grid>
    <cc:MyCustomButton MyClassA="{StaticResource MyClassB}" Width="100" Height="100"/>
</Grid> 

MyButton-Style:

<StackPanel Background="Transparent">
    <cc:ClassA x:Name="ButtonClassA" Content="{Binding MyClassA, RelativeSource={RelativeSource TemplatedParent}}" />
</StackPanel>
+4
3

, , , , BindsTwoWayByDefault=false. . Dependency, , :

public static readonly DependencyProperty MyTextPropertyChanged =
    DependencyProperty.Register
    (
            "MyTextProperty",
            typeof(ClassA), 
            typeof(string),
            new FrameworkPropertyMetadata
            (
                    default(string), 
                    //the next line enables the binding back to source:
                    FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
                    MyTextPropertyChanged
            )
    );

public static void MyTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs)
{
    //handling changed event
}

Project , style ClassA / ClassB. XAML ClassB # App.xaml, :

<Application.Resources>
     <Style TargetType="{x:Type customControls:ClassA}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type customControls:ClassA}">
                    <TextBlock Text="{Binding MyText, 
                               RelativeSource={RelativeSource TemplatedParent}}"/>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Application.Resources>

XAML ClassB #.

, ClassA, ClassB, :

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

//and in ClassB

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

MyText ClassB; ClassA. :

<ControlTemplate TargetType="{x:Type l:MyCustomButton}">
  <Border BorderBrush="{TemplateBinding BorderBrush}"
          BorderThickness="{TemplateBinding BorderThickness}">
    <StackPanel Background="Transparent">
      <l:ClassA x:Name="ButtonClassA"
                Content="{Binding MyClassA,
                                  RelativeSource={RelativeSource TemplatedParent}}" />
    </StackPanel>
  </Border>

  <ControlTemplate.Triggers>
    <Trigger Property="IsMouseOver"
              Value="true">
      <Setter TargetName="ButtonClassA"
              Property="MyText"
              Value="HOVER" />
    </Trigger>
    <Trigger Property="IsPressed"
              Value="true">
      <Setter TargetName="ButtonClassA"
              Property="MyText"
              Value="PRESSED" />
    </Trigger>
  </ControlTemplate.Triggers>
</ControlTemplate>

MyText ButtonClassA, ClassA. ClassB - Content of ButtonClassA, . MyText ClassB, : MyClassB.

, UIElement , UIElement ; . , , ClassA StackPanel MyClassB.


nitpick OverrideMetadata AddOwner ClassB. OverrideMetadata, , . AddOwner, , . , Panel Control , Background, Control , Panel:

public static readonly DependencyProperty BackgroundProperty =
        Panel.BackgroundProperty.AddOwner(
            typeof(Control),
            new FrameworkPropertyMetadata(
                Panel.BackgroundProperty.DefaultMetadata.DefaultValue,
                FrameworkPropertyMetadataOptions.None));

AddOwner, ; , OverrideMetadata.

0

, :

static ClassB()
{
    MyTextProperty.OverrideMetadata(
        typeof(ClassB),
        new PropertyMetadata("ClassB Default Text", MyTextPropertyChanged));
}

MyTextProperty ClassB ClassB.MyTextPropertyChanged . , (ClassA ClassB) , PropertyMetadata, FrameworkPropertyMetadata .

, ClassA.MyTextPropertyChanged , ClassB. , :

public ClassB()
{
    DependencyPropertyDescriptor.FromProperty(MyTextProperty, typeof(ClassB))
        .AddValueChanged(this, MyHandler);
}

private void MyHandler(object sender, EventArgs e)
{
    //your handler code
}
0

All Articles