You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何以最简洁方式基于现有控件组合创建WPF自定义控件?

实现带按钮的TextBox的轻量化方案

方案1:附加属性+行为(无需自定义控件)

完全复用原生TextBox,通过附加属性关联按钮的命令、内容,再通过回调动态将按钮插入TextBox的可视化树,无需修改模板,自动继承TextBox的所有样式与主题支持。

代码示例

附加属性定义

public static class TextBoxButtonAttached
{
    public static readonly DependencyProperty ButtonCommandProperty =
        DependencyProperty.RegisterAttached("ButtonCommand", typeof(ICommand), typeof(TextBoxButtonAttached),
            new PropertyMetadata(null, OnButtonCommandChanged));

    public static readonly DependencyProperty ButtonContentProperty =
        DependencyProperty.RegisterAttached("ButtonContent", typeof(object), typeof(TextBoxButtonAttached),
            new PropertyMetadata(null));

    public static void SetButtonCommand(DependencyObject obj, ICommand value)
    {
        obj.SetValue(ButtonCommandProperty, value);
    }

    public static ICommand GetButtonCommand(DependencyObject obj)
    {
        return (ICommand)obj.GetValue(ButtonCommandProperty);
    }

    public static void SetButtonContent(DependencyObject obj, object value)
    {
        obj.SetValue(ButtonContentProperty, value);
    }

    public static object GetButtonContent(DependencyObject obj)
    {
        return obj.GetValue(ButtonContentProperty);
    }

    private static void OnButtonCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is TextBox textBox)
        {
            if (textBox.Template.FindName("PART_Button", textBox) is Button existingButton)
            {
                existingButton.Command = e.NewValue as ICommand;
                return;
            }

            var button = new Button
            {
                Name = "PART_Button",
                Command = e.NewValue as ICommand,
                Content = GetButtonContent(textBox)
            };

            textBox.RegisterPropertyChangedCallback(ButtonContentProperty, (s, args) =>
            {
                button.Content = GetButtonContent(textBox);
            });

            if (textBox.Template.FindName("PART_ContentHost", textBox) is FrameworkElement contentHost)
            {
                if (contentHost.Parent is Panel parentPanel)
                {
                    parentPanel.Children.Add(button);
                    DockPanel.SetDock(button, Dock.Right);
                }
            }
        }
    }
}

XAML使用

<TextBox local:TextBoxButtonAttached.ButtonContent="清除"
         local:TextBoxButtonAttached.ButtonCommand="{Binding ClearCommand}" />

方案2:自定义控件复用原有模板结构

通过模板继承仅添加按钮元素,不复制完整TextBox模板,保留原生TextBox的样式继承能力,适配深色主题等场景。

代码示例

自定义控件类

public class TextBoxWithButton : TextBox
{
    static TextBoxWithButton()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(TextBoxWithButton), new FrameworkPropertyMetadata(typeof(TextBoxWithButton)));
    }

    public static readonly DependencyProperty ButtonCommandProperty =
        DependencyProperty.Register(nameof(ButtonCommand), typeof(ICommand), typeof(TextBoxWithButton), new PropertyMetadata(null));

    public ICommand ButtonCommand
    {
        get => (ICommand)GetValue(ButtonCommandProperty);
        set => SetValue(ButtonCommandProperty, value);
    }

    public static readonly DependencyProperty ButtonContentProperty =
        DependencyProperty.Register(nameof(ButtonContent), typeof(object), typeof(TextBoxWithButton), new PropertyMetadata(null));

    public object ButtonContent
    {
        get => GetValue(ButtonContentProperty);
        set => SetValue(ButtonContentProperty, value);
    }
}

控件模板

<Style TargetType="{x:Type local:TextBoxWithButton}" BasedOn="{StaticResource {x:Type TextBox}}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:TextBoxWithButton}">
                <DockPanel>
                    <Button DockPanel.Dock="Right"
                            Content="{TemplateBinding ButtonContent}"
                            Command="{TemplateBinding ButtonCommand}"
                            Margin="2" />
                    <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}">
                        <ScrollViewer x:Name="PART_ContentHost" Focusable="false" />
                    </Border>
                </DockPanel>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

方案3:UserControl简化依赖属性开发

利用Visual Studio的propdp代码片段快速生成依赖属性,通过直观的XAML组合TextBox与Button,适合简单组合场景。

代码示例

XAML部分

<UserControl x:Class="WpfApp.TextBoxWithButtonUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <DockPanel>
        <Button DockPanel.Dock="Right"
                Content="{Binding ButtonContent, RelativeSource={RelativeSource AncestorType=UserControl}}"
                Command="{Binding ButtonCommand, RelativeSource={RelativeSource AncestorType=UserControl}}" />
        <TextBox Text="{Binding Text, RelativeSource={RelativeSource AncestorType=UserControl}, Mode=TwoWay}" />
    </DockPanel>
</UserControl>

C#部分

public partial class TextBoxWithButtonUserControl : UserControl
{
    public TextBoxWithButtonUserControl()
    {
        InitializeComponent();
    }

    public static readonly DependencyProperty TextProperty =
        DependencyProperty.Register(nameof(Text), typeof(string), typeof(TextBoxWithButtonUserControl), new PropertyMetadata(string.Empty));

    public string Text
    {
        get => (string)GetValue(TextProperty);
        set => SetValue(TextProperty, value);
    }

    public static readonly DependencyProperty ButtonCommandProperty =
        DependencyProperty.Register(nameof(ButtonCommand), typeof(ICommand), typeof(TextBoxWithButtonUserControl), new PropertyMetadata(null));

    public ICommand ButtonCommand
    {
        get => (ICommand)GetValue(ButtonCommandProperty);
        set => SetValue(ButtonCommandProperty, value);
    }

    public static readonly DependencyProperty ButtonContentProperty =
        DependencyProperty.Register(nameof(ButtonContent), typeof(object), typeof(TextBoxWithButtonUserControl), new PropertyMetadata(null));

    public object ButtonContent
    {
        get => GetValue(ButtonContentProperty);
        set => SetValue(ButtonContentProperty, value);
    }
}

内容的提问来源于stack exchange,提问作者ekt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 19:02:07