Wpf 如何处理附加属性事件?
我创建了一个扩展样式,它的标题中包含一个复选框。复选框状态绑定到附加的属性:Wpf 如何处理附加属性事件?,wpf,events,mvvm,attached-properties,Wpf,Events,Mvvm,Attached Properties,我创建了一个扩展样式,它的标题中包含一个复选框。复选框状态绑定到附加的属性: <Style TargetType="{x:Type Expander}" x:Key="MyCheckboxExpander"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type Expander}">
<Style TargetType="{x:Type Expander}" x:Key="MyCheckboxExpander">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Expander}">
(...)
<CheckBox x:Name="ExpanderHeaderChk" VerticalAlignment="Center" Margin="4,0,0,2"
IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(my:AP.IsChecked)}" />
(...)
(...)
(...)
在我的视图中,扩展器内有一个带有组合框的stackpanel
每当用户选中expander的复选框时,我不希望combobox选中第一个项目;另一方面,每当用户取消选中它时,我不希望combobox的selecteditem为空
我怎样才能做到这一点?我遵循MVVM模式,但由于这更多是一个视图问题,我愿意接受代码隐藏建议。好吧,我认为您的设计不是最优的。你看,你正试图改变
扩展器的语义。真正的扩展器没有附加复选框的语义,因此您正在创建的控件不再是扩展器
我建议您切换到用户控件(或者自定义控件,看看您的语义),并在控件的类中公开所需的事件。用户控件的XAML可能应该是带有复选框的扩展器
编辑:带有UserControl的示例(未测试)
(XAML)
...
...
(代码隐藏)
公共类MyCheckboxExpander:UserControl
{
MyCheckboxExpander()
{
初始化组件();
cb.Check+=OnCheck;
}
void OnCheck(对象发送方,whatever2参数)
{
如果(CheckboxTriggered!=null)
CheckboxTriggered(新事件参数);
}
公共事件事件args CheckboxTriggered;
}
嗯,我认为你的设计不是最优的。你看,你正试图改变扩展器的语义。真正的扩展器没有附加复选框的语义,因此您正在创建的控件不再是扩展器
我建议您切换到用户控件(或者自定义控件,看看您的语义),并在控件的类中公开所需的事件。用户控件的XAML可能应该是带有复选框的扩展器
编辑:带有UserControl的示例(未测试)
(XAML)
...
...
(代码隐藏)
公共类MyCheckboxExpander:UserControl
{
MyCheckboxExpander()
{
初始化组件();
cb.Check+=OnCheck;
}
void OnCheck(对象发送方,whatever2参数)
{
如果(CheckboxTriggered!=null)
CheckboxTriggered(新事件参数);
}
公共事件事件args CheckboxTriggered;
}
WPF是一个功能强大的框架,您只需使用下一种样式的Expander即可解决问题:
<Style x:Key="myExpanderStyle" TargetType="{x:Type Expander}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Expander}">
<StackPanel>
<CheckBox x:Name="PART_CheckBox" IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}" />
<ComboBox x:Name="PART_ComboBox" ItemsSource="{TemplateBinding Content}" />
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="True">
<Setter TargetName="PART_ComboBox" Property="SelectedIndex" Value="0"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
样本:
<Expander Style="{StaticResource myExpanderStyle}">
<x:Array Type="sys:String">
<sys:String>1</sys:String>
<sys:String>2</sys:String>
<sys:String>3</sys:String>
</x:Array>
</Expander>
1.
2.
3.
只是XAML!我喜欢XAML声明性
但是从MVVM的角度来看,这种方法有一个缺点——我不能用单元测试来描述这种情况。因此,我宁愿:
创建具有以下属性的视图模型:IsChecked(绑定到复选框),
选择EdItem(绑定到ComboBox)和Source(ComboBox的ItemsSource)-
在没有任何控件参考的情况下对我的真实视图进行抽象李>
在视图模型中编写一个逻辑,根据需要设置或取消设置SelectedItem
关于被检查的财产李>
用单元测试覆盖这个逻辑(是的,你可以
如果您喜欢测试优先的方法,甚至可以从这一点开始)
WPF是一个功能强大的框架,您只需使用下一种Expander样式即可解决您的问题:
<Style x:Key="myExpanderStyle" TargetType="{x:Type Expander}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Expander}">
<StackPanel>
<CheckBox x:Name="PART_CheckBox" IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}" />
<ComboBox x:Name="PART_ComboBox" ItemsSource="{TemplateBinding Content}" />
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="True">
<Setter TargetName="PART_ComboBox" Property="SelectedIndex" Value="0"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
样本:
<Expander Style="{StaticResource myExpanderStyle}">
<x:Array Type="sys:String">
<sys:String>1</sys:String>
<sys:String>2</sys:String>
<sys:String>3</sys:String>
</x:Array>
</Expander>
1.
2.
3.
只是XAML!我喜欢XAML声明性
但是从MVVM的角度来看,这种方法有一个缺点——我不能用单元测试来描述这种情况。因此,我宁愿:
创建具有以下属性的视图模型:IsChecked(绑定到复选框),
选择EdItem(绑定到ComboBox)和Source(ComboBox的ItemsSource)-
在没有任何控件参考的情况下对我的真实视图进行抽象李>
在视图模型中编写一个逻辑,根据需要设置或取消设置SelectedItem
关于被检查的财产李>
用单元测试覆盖这个逻辑(是的,你可以
如果您喜欢测试优先的方法,甚至可以从这一点开始)
我遵循@Baboon提供的建议,创建了一个自定义控件,其中包含一个名为CheckedChanged的路由事件,这样我就可以通过视图的xaml和隐藏代码访问它:
[TemplatePart(Name = "PART_Expander", Type = typeof(Expander))]
[TemplatePart(Name = "PART_CheckBox", Type = typeof(CheckBox))]
public class MyCustomExpander : Expander
{
static MyCustomExpander()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomExpander), new FrameworkPropertyMetadata(typeof(MyCustomExpander)));
}
public bool IsChecked
{
get { return (bool)GetValue(IsCheckedProperty); }
set { SetValue(IsCheckedProperty, value); }
}
public static readonly DependencyProperty IsCheckedProperty =
DependencyProperty.Register("IsChecked", typeof(bool), typeof(MyCustomExpander),
new UIPropertyMetadata(false));
#region Events
private CheckBox chkExpander = new CheckBox();
public CheckBox ChkExpander { get { return chkExpander; } private set { chkExpander = value; } }
public static readonly RoutedEvent CheckedChangedEvent = EventManager.RegisterRoutedEvent("ExtraButtonClick",
RoutingStrategy.Bubble,
typeof(RoutedEventHandler),
typeof(MyCustomExpander));
public event RoutedEventHandler CheckedChanged
{
add { AddHandler(CheckedChangedEvent, value); }
remove { RemoveHandler(CheckedChangedEvent, value); }
}
void OnCheckedChanged(object sender, RoutedEventArgs e)
{
RaiseEvent(new RoutedEventArgs(CheckedChangedEvent, this));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
CheckBox chk = base.GetTemplateChild("PART_CheckBox") as CheckBox;
if (chk != null)
{
chk.Checked += new RoutedEventHandler(OnCheckedChanged);
chk.Unchecked += new RoutedEventHandler(OnCheckedChanged);
}
}
#endregion
}
我要感谢@Baboon和@Vlad的帮助。我遵循了@Baboon提供的建议,创建了一个自定义控件,其中包含一个名为CheckedChanged的路由事件,这样我就可以通过视图的xaml和代码来访问它:
[TemplatePart(Name = "PART_Expander", Type = typeof(Expander))]
[TemplatePart(Name = "PART_CheckBox", Type = typeof(CheckBox))]
public class MyCustomExpander : Expander
{
static MyCustomExpander()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomExpander), new FrameworkPropertyMetadata(typeof(MyCustomExpander)));
}
public bool IsChecked
{
get { return (bool)GetValue(IsCheckedProperty); }
set { SetValue(IsCheckedProperty, value); }
}
public static readonly DependencyProperty IsCheckedProperty =
DependencyProperty.Register("IsChecked", typeof(bool), typeof(MyCustomExpander),
new UIPropertyMetadata(false));
#region Events
private CheckBox chkExpander = new CheckBox();
public CheckBox ChkExpander { get { return chkExpander; } private set { chkExpander = value; } }
public static readonly RoutedEvent CheckedChangedEvent = EventManager.RegisterRoutedEvent("ExtraButtonClick",
RoutingStrategy.Bubble,
typeof(RoutedEventHandler),
typeof(MyCustomExpander));
public event RoutedEventHandler CheckedChanged
{
add { AddHandler(CheckedChangedEvent, value); }
remove { RemoveHandler(CheckedChangedEvent, value); }
}
void OnCheckedChanged(object sender, RoutedEventArgs e)
{
RaiseEvent(new RoutedEventArgs(CheckedChangedEvent, this));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
CheckBox chk = base.GetTemplateChild("PART_CheckBox") as CheckBox;
if (chk != null)
{
chk.Checked += new RoutedEventHandler(OnCheckedChanged);
chk.Unchecked += new RoutedEventHandler(OnCheckedChanged);
}
}
#endregion
}
我要感谢@Baboon和@Vlad的帮助。看起来他在使用自定义控件(!=UserControl)好的,这很有意义。我将在谷歌上搜索“自定义控件公开事件”,因为我对自定义控件不太了解……它应该是一个自定义控件,他不是在对功能进行分组,而是在重新部署框架的控件。@Baboon:我的回答是,我不认为重新部署