扩展器的 WPF 自定义控件

WPF Custom Control for Expander

我正在使用 WPF 并寻找一种最佳方法来创建自定义的 re-usable 扩展器。具体来说,Expander header 将保持不变,内容会有所不同。

Expander header 将有 6 个按钮全部连接到相同的方法,这将通过接口强制执行。

假设这是扩展器 1

这是另一个扩展器

实际内容将是文本、按钮等。它只有用于演示目的的文本。

扩展器是要在用户控件中使用的,我有 44 个,不想到处重复代码。

目前我在 Window XAML

中使用如下所示的用户控件
xmlns:customcontrols="clr-namespace:MyNamespace.Controls;assembly=MyAssembly"

以及实际使用情况:

<customcontrols:FlexExtend ..... />

在每个用户控件中我都使用这样的扩展器

<Expander Style="{StaticResource ModToolPanelStyle}"  Background="#403F3B"  Name="toolExpand" Header="{x:Static properties:Resources.AdductionAbduction_Label}" Collapsed="toolExpand_Collapsed" Expanded="toolExpand_Expanded">

....... all the inside body stuff

</expander>

现在我正在考虑必须将代码复制 44 次,每个扩展器对应 44 个包含扩展器的用户控件中的每个扩展器。 WPF 中有没有一种方法可以使它成为具有按钮和所有内容的自定义控件?我认为不可以,因为点击时它无法绑定到那里?

更新:

按照建议,我在单独的 XAML.

中创建了一个 DataTemplate
<DataTemplate x:Key="DesignExpanderHeaderTemplate">
        <DockPanel>
            <TextBlock Name="ModName"
                       Foreground="White"
                       Text="Balls">
            </TextBlock>
            <Button Name="MoveUpButton"
                    Content="MoveUp"
                    Width="80"
                    Height="25">
            </Button>
        </DockPanel>
    </DataTemplate>

但是现在我在绑定用户控件的按钮时遇到问题:

var button = toolExpand.HeaderTemplate.FindName("MoveUpButton", toolExpand) as Button;
        button.Click += delegate (object sender, RoutedEventArgs args)
        {
            MessageBox.Show("The button has been pressed");
        };

按钮始终为空,因此无法找到它。

这就是 XAML 的样子

<Expander Style="{StaticResource ModToolPanelStyle}"  
                  Background="#403F3B"  
                  x:Name="toolExpand" 
                  HeaderTemplate="{StaticResource DesignExpanderHeaderTemplate}" 
                  Collapsed="toolExpand_Collapsed" 
                  Expanded="toolExpand_Expanded">

在 user2455627 的指导下,我成功了。主要关键如下:

RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}

我的数据模板如下所示。

<DataTemplate x:Key="DesignExpanderHeaderTemplate">
    <DockPanel>
        <TextBlock Foreground="White"
                   Text="{Binding ModName, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
        </TextBlock>
        <Button Command="{Binding MoveUpCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
                Content="MoveUp"
                Width="80"
                Height="25">
        </Button>
        <Button Command="{Binding MoveDownCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
                Content="MoveUp"
                Width="80"
                Height="25">
        </Button>
        <Button Command="{Binding UndoCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
                Content="Undo"
                Width="80"
                Height="25"></Button>
    </DockPanel>
</DataTemplate>

以下是我在 XAML

中使用数据模板的方式
<Expander Style="{StaticResource ModToolPanelStyle}"  
              Background="#403F3B"  
              x:Name="toolExpand" 
              HeaderTemplate="{StaticResource DesignExpanderHeaderTemplate}" 
              Collapsed="toolExpand_Collapsed" 
              Expanded="toolExpand_Expanded">

后面的相关代码如下:

// Commands
    public RelayCommand MoveUpCommand { get; set; }
    public RelayCommand MoveDownCommand { get; set; }
    public RelayCommand UndoCommand { get; set; }
    public RelayCommand RedoCommand { get; set; }
    public RelayCommand ClearCommnand { get; set; }
    public RelayCommand RemoveCommand { get; set; }

    // Properties
    private string _modName;

// setup ModPanel
        ModName = "Something";
        MoveUpCommand = new RelayCommand(MoveUp);
        MoveDownCommand = new RelayCommand(MoveDown);
        UndoCommand = new RelayCommand(Undo);
        RedoCommand = new RelayCommand(Redo);
        ClearCommnand = new RelayCommand(Clear);
        RemoveCommand = new RelayCommand(Remove);

public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public string ModName
    {
        get { return _modName; }
        set
        {
            _modName = value;
            OnPropertyChanged();
        }
    }

    public void MoveUp(object obj)
    {
        throw new NotImplementedException();
    }

    public void MoveDown(object obj)
    {
        throw new NotImplementedException();
    }

    public void Undo(object obj)
    {
        throw new NotImplementedException();
    }

    public void Redo(object obj)
    {
        throw new NotImplementedException();
    }

    public void Remove(object obj)
    {
        throw new NotImplementedException();
    }

    public void Clear(object obj)
    {
        throw new NotImplementedException();
    }

    public void PreApply()
    {
        throw new NotImplementedException();
    }