0

我创建了一个扩展器样式,在其标题中包含一个复选框。复选框状态绑定到附加属性:

<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)}" />
                (...)

我认为,在扩展器内部,我有一个带有 ComboBox 的堆栈面板。

每当用户检查扩展器的复选框时,我不希望组合框选择第一个项目,另一方面,每当用户取消选中它时,我不希望组合框的选定项为空。

我怎样才能做到这一点?我遵循 MVVM 模式,但由于这更多是一个观点问题,我愿意接受代码隐藏建议。

4

3 回答 3

1

好吧,我认为您的设计不是最佳的。你看,你正试图改变Expander. 真正的扩展器没有附加复选框的语义,因此您创建的控件不再是扩展器。

我建议您切换到用户控件(或者可能是自定义控件,查看您的语义),并在控件的类中公开所需的事件。用户控件的 XAML 应该是带有复选框的扩展器。


编辑:带有 UserControl 的示例(未测试)

(XAML)

<UserControl x:Class="namespace:MyCheckboxExpander">
    <Expander>
        ...
        <Checkbox x:Name="cb"/>
        ...
    </Expander>
</UserControl>

(代码隐藏)

public class MyCheckboxExpander : UserControl
{
    MyCheckboxExpander()
    {
        InitializeComponent();
        cb.Check += OnCheck;
    }

    void OnCheck(object sender, whatever2 args)
    {
        if (CheckboxTriggered != null)
            CheckboxTriggered(new EventArgs<whatever>);
    }

    public event EventArgs<whatever> CheckboxTriggered;
}
于 2012-07-18T11:11:49.110 回答
1

WPF 是一个非常强大的框架,你可以使用扩展器的下一个样式来解决你的问题:

 <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>

只是 XAML!我喜欢 XAML 声明性。

但是从 MVVM 的角度来看,这种方法有一个缺点——我不能用单元测试来覆盖这种情况。所以,我更喜欢:

  1. 创建具有以下属性的视图模型:IsChecked(绑定到 CheckBox)、SelectedItem(绑定到 ComboBox)和 Source(ComboBox 的 ItemsSource)——我的真实视图的抽象,没有任何控件引用;
  2. 在视图模型中编写一个逻辑,根据 IsChecked 属性设置或取消设置 SelectedItem;
  3. 用单元测试覆盖该逻辑(是的,如果您喜欢测试优先方法,您甚至可以从这一点开始)。
于 2012-07-18T19:02:13.527 回答
1

我遵循了@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 的帮助。

于 2012-07-19T13:26:11.073 回答