覆盖的元数据仍在子自定义控件中执行 class
Overriden metadata are still executed in a children custom control class
我在我制作的 "button" 自定义控件中创建了一个 DP:
#region IsPressed
public bool IsPressed {
get { return (bool)GetValue(IsPressedProperty); }
private set { SetValue(IsPressedProperty, value); }
}
private static void IsPressedCallback(DependencyObject o, DependencyPropertyChangedEventArgs args) {
MyParentClass btn = (MyParentClass) o;
//change the background when the "button" is pressed, change it back afterward
if ((bool) args.NewValue) {
btn._backgroundBrushTemp = btn.Background;
btn.Background = btn._pressBrush;
} else {
btn.Background = btn._backgroundBrushTemp;
}
}
private readonly static PropertyMetadata IsPressedMetadata = new PropertyMetadata() {
DefaultValue = false,
PropertyChangedCallback = IsPressedCallback
};
public static readonly DependencyProperty IsPressedProperty =
DependencyProperty.Register("IsPressed", typeof(bool), typeof(MyParentClass), IsPressedMetadata);
#endregion
一切都按预期工作,但用户现在想要一种新行为,使我的自定义控件方式不那么通用,所以我创建了另一个自定义控件:继承 MyParentClass 的 MyChildrenClass。
为了实现新行为,我必须重写 IsPressed 的回调函数,所以我在 MyChildrenClass 的静态构造器中写了这个:
IsPressedProperty.OverrideMetadata(
typeof(MyChildrenClass),
new FrameworkPropertyMetadata(false, IsPressedCallback)
);
并编写了新的回调:
private static void IsPressedCallback(DependencyObject o, DependencyPropertyChangedEventArgs args) {
MyChildrenClass btn = (MyChildrenClass)o;
//change the background when the "button" is pressed, change it back after 1s
if ((bool) args.NewValue) {
btn._backgroundBrushTemp = btn.Background;
btn.Background = btn._pressBrush;
if (btn._threadIsPressed == null ||
!btn._threadIsPressed.IsAlive) {
btn._threadIsPressed = new Thread(() => {
Thread.Sleep(1000);
btn.Dispatcher.BeginInvoke(new Action(() => {
btn.Background = btn._backgroundBrushTemp;
}));
});
btn._threadIsPressed.Start();
}
}
}
现在的问题是:尽管我已经覆盖了元数据和回调函数,但两个回调都被调用了。我做错了什么?
设法完全在 XAML 完成:
<!--Standard brushes-->
<ImageBrush x:Key="BrushBtnIdle">
<ImageBrush.ImageSource>
<BitmapImage>
<BitmapImage.UriSource>
<system1:Uri>pack://application:,,,/AssemblyName;component/Resources/Images/Btn_Idle.png
</system1:Uri>
</BitmapImage.UriSource>
</BitmapImage>
</ImageBrush.ImageSource>
</ImageBrush>
<ImageBrush x:Key="BrushBtnPushed">
<ImageBrush.ImageSource>
<BitmapImage>
<BitmapImage.UriSource>
<system1:Uri>pack://application:,,,/AssemblyName;component/Resources/Images/Btn_Pushed.png
</system1:Uri>
</BitmapImage.UriSource>
</BitmapImage>
</ImageBrush.ImageSource>
</ImageBrush>
<Style TargetType="{x:Type local:MyParentClass}">
<Setter Property="Background" Value="White"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyParentClass}">
<Border Background="{TemplateBinding Background}"
Margin="{TemplateBinding Margin}"
BorderBrush="{TemplateBinding BorderBrush}"
SnapsToDevicePixels="true"
Opacity="{TemplateBinding Opacity}"
CornerRadius="{TemplateBinding Border.CornerRadius}"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="{StaticResource BrushBtnPushed}"/>
</Trigger>
<Trigger Property="IsPressed" Value="False">
<Setter Property="Background" Value="{StaticResource BrushBtnIdle}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value=".5"/>
</Trigger>
</Style.Triggers>
</Style>
<!--Highlighted button-->
<ImageBrush x:Key="BrushBtnHighlight">
<ImageBrush.ImageSource>
<BitmapImage>
<BitmapImage.UriSource>
<system1:Uri>pack://application:,,,/AssemblyName;component/Resources/Images/ActivatedBtn.png
</system1:Uri>
</BitmapImage.UriSource>
</BitmapImage>
</ImageBrush.ImageSource>
</ImageBrush>
<Style TargetType="{x:Type local:MyChildrenClass}" BasedOn="{StaticResource {x:Type local:MyParentClass}}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource self}, Path=IsPressed}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard TargetProperty="Background">
<ObjectAnimationUsingKeyFrames Duration="00:00:01" FillBehavior="Stop">
<DiscreteObjectKeyFrame KeyTime="00:00:00" Value="{StaticResource BrushBtnHighlight}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
在 DataTrigger 中使用 StoryBoard 而不是 Thread.Sleep。
我在我制作的 "button" 自定义控件中创建了一个 DP:
#region IsPressed
public bool IsPressed {
get { return (bool)GetValue(IsPressedProperty); }
private set { SetValue(IsPressedProperty, value); }
}
private static void IsPressedCallback(DependencyObject o, DependencyPropertyChangedEventArgs args) {
MyParentClass btn = (MyParentClass) o;
//change the background when the "button" is pressed, change it back afterward
if ((bool) args.NewValue) {
btn._backgroundBrushTemp = btn.Background;
btn.Background = btn._pressBrush;
} else {
btn.Background = btn._backgroundBrushTemp;
}
}
private readonly static PropertyMetadata IsPressedMetadata = new PropertyMetadata() {
DefaultValue = false,
PropertyChangedCallback = IsPressedCallback
};
public static readonly DependencyProperty IsPressedProperty =
DependencyProperty.Register("IsPressed", typeof(bool), typeof(MyParentClass), IsPressedMetadata);
#endregion
一切都按预期工作,但用户现在想要一种新行为,使我的自定义控件方式不那么通用,所以我创建了另一个自定义控件:继承 MyParentClass 的 MyChildrenClass。
为了实现新行为,我必须重写 IsPressed 的回调函数,所以我在 MyChildrenClass 的静态构造器中写了这个:
IsPressedProperty.OverrideMetadata(
typeof(MyChildrenClass),
new FrameworkPropertyMetadata(false, IsPressedCallback)
);
并编写了新的回调:
private static void IsPressedCallback(DependencyObject o, DependencyPropertyChangedEventArgs args) {
MyChildrenClass btn = (MyChildrenClass)o;
//change the background when the "button" is pressed, change it back after 1s
if ((bool) args.NewValue) {
btn._backgroundBrushTemp = btn.Background;
btn.Background = btn._pressBrush;
if (btn._threadIsPressed == null ||
!btn._threadIsPressed.IsAlive) {
btn._threadIsPressed = new Thread(() => {
Thread.Sleep(1000);
btn.Dispatcher.BeginInvoke(new Action(() => {
btn.Background = btn._backgroundBrushTemp;
}));
});
btn._threadIsPressed.Start();
}
}
}
现在的问题是:尽管我已经覆盖了元数据和回调函数,但两个回调都被调用了。我做错了什么?
设法完全在 XAML 完成:
<!--Standard brushes-->
<ImageBrush x:Key="BrushBtnIdle">
<ImageBrush.ImageSource>
<BitmapImage>
<BitmapImage.UriSource>
<system1:Uri>pack://application:,,,/AssemblyName;component/Resources/Images/Btn_Idle.png
</system1:Uri>
</BitmapImage.UriSource>
</BitmapImage>
</ImageBrush.ImageSource>
</ImageBrush>
<ImageBrush x:Key="BrushBtnPushed">
<ImageBrush.ImageSource>
<BitmapImage>
<BitmapImage.UriSource>
<system1:Uri>pack://application:,,,/AssemblyName;component/Resources/Images/Btn_Pushed.png
</system1:Uri>
</BitmapImage.UriSource>
</BitmapImage>
</ImageBrush.ImageSource>
</ImageBrush>
<Style TargetType="{x:Type local:MyParentClass}">
<Setter Property="Background" Value="White"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyParentClass}">
<Border Background="{TemplateBinding Background}"
Margin="{TemplateBinding Margin}"
BorderBrush="{TemplateBinding BorderBrush}"
SnapsToDevicePixels="true"
Opacity="{TemplateBinding Opacity}"
CornerRadius="{TemplateBinding Border.CornerRadius}"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="{StaticResource BrushBtnPushed}"/>
</Trigger>
<Trigger Property="IsPressed" Value="False">
<Setter Property="Background" Value="{StaticResource BrushBtnIdle}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value=".5"/>
</Trigger>
</Style.Triggers>
</Style>
<!--Highlighted button-->
<ImageBrush x:Key="BrushBtnHighlight">
<ImageBrush.ImageSource>
<BitmapImage>
<BitmapImage.UriSource>
<system1:Uri>pack://application:,,,/AssemblyName;component/Resources/Images/ActivatedBtn.png
</system1:Uri>
</BitmapImage.UriSource>
</BitmapImage>
</ImageBrush.ImageSource>
</ImageBrush>
<Style TargetType="{x:Type local:MyChildrenClass}" BasedOn="{StaticResource {x:Type local:MyParentClass}}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource self}, Path=IsPressed}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard TargetProperty="Background">
<ObjectAnimationUsingKeyFrames Duration="00:00:01" FillBehavior="Stop">
<DiscreteObjectKeyFrame KeyTime="00:00:00" Value="{StaticResource BrushBtnHighlight}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
在 DataTrigger 中使用 StoryBoard 而不是 Thread.Sleep。