我创建了一个基础ClassA
,其中包含DependencyProperty
MyText
。我从中派生了一个ClassB
。此ClassB
已嵌入MyCustomButton
中。在MouseHover / MousePressed上,我更改了嵌入式ClassB的MyText-Value。但是,当父ClassA触发PropertyChanged-Event时,派生的ClassB永远不会更改Property。
通知派生的ClassB更改DependencyProperty的正确方法是什么?我已经尝试了“Base class DependencyProperty value change in WPF”中提到的解决方案,但无法使其工作。
我的代码:
主窗口只包含一个包含ClassB-Instance的MyCustomButton:
<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的实例(实际上是派生ClassB的一个实例):
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>
更新
到目前为止我得到的所有答案似乎都是正确的,但对我的问题没有帮助。我认为问题可能在于我绑定派生类的方式。可能是这样,我的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风格中:
<StackPanel Background="Transparent">
<cc:ClassA x:Name="ButtonClassA" Content="{Binding MyClassA, RelativeSource={RelativeSource TemplatedParent}}" />
</StackPanel>
答案 0 :(得分:0)
我看到的第一件事是,如果没有指定其他内容,依赖属性将设置为BindsTwoWayByDefault=false
。因此,您只有从源到目标的绑定。要启用依赖项属性允许从目标到源的绑定,您必须在依赖项属性创建时声明:
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
}
检查您的示例项目后,发现您遗漏了style
和/或ClassA
的{{1}}。只需删除ClassB的ClassB
,只需使用C#代码并在XAML
中添加以下行即可:
App.xaml
从ClassB中删除<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>
- 文件,只使用C#代码。
最后,我建议你在ClassA中写下以下的ClassB:
XAML
答案 1 :(得分: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
的{{1}}属性,这是ButtonClassA
个实例。您的ClassA
实例是ClassB
的 Content
,但您永远不会更改其属性。由于您永远不会更改ButtonClassA
实例上的MyText
,因此只会调用一次更改处理程序:实例化ClassB
资源时。
请注意,您确实不应将MyClassB
用作共享资源,因为UIElement
只能加载到单个可视树中;它不能拥有两个所有者。要查看问题的实际效果,请向UIElement
个ClassA
实例添加第二个相同的StackPanel
实例。
作为挑剔者,当您在 您不需要使用MyClassB
中添加回调时,应使用OverrideMetadata
代替AddOwner
。如果要覆盖祖先类声明的属性的元数据,请使用ClassB
。如果要重新使用由基类层次结构之外的类声明的属性,请使用OverrideMetadata
。例如,AddOwner
和Panel
位于不同的层次结构中,但它们共享相同的Control
属性,因为Background
重新使用Control
声明的属性:< / p>
Panel
public static readonly DependencyProperty BackgroundProperty =
Panel.BackgroundProperty.AddOwner(
typeof(Control),
new FrameworkPropertyMetadata(
Panel.BackgroundProperty.DefaultMetadata.DefaultValue,
FrameworkPropertyMetadataOptions.None));
,因为您没有将该属性附加到新类;您已经从基类继承了该属性,因此您只需要使用AddOwner
。
答案 2 :(得分: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
}