TemplatePart作用

本文详细解析了WPF中Button控件的工作原理及其组成部分,包括各状态Storyboard的作用及ControlTemplate的设计模式,帮助读者理解Button控件如何实现其默认行为。
部署运行你感兴趣的模型镜像


以Button的定义为例:

namespace System.Windows.Controls
{
    // Summary:
    //     Represents a button control, which reacts to the Click event.
    [TemplatePart(Name = "Normal State", Type = typeof(Storyboard))]
    [TemplatePart(Name = "MouseOver State", Type = typeof(Storyboard))]
    [TemplatePart(Name = "RootElement", Type = typeof(FrameworkElement))]
    [TemplatePart(Name = "Pressed State", Type = typeof(Storyboard))]
    [TemplatePart(Name = "FocusVisualElement", Type = typeof(UIElement))]
    [TemplatePart(Name = "Disabled State", Type = typeof(Storyboard))]
    public class Button : ButtonBase
    {
        // Summary:
        //     Initializes a new instance of the Button class.
        public Button();
        // Summary:
        //     Apply a template to the Button.
        protected override void OnApplyTemplate();
        //
        // Summary:
        //     Called when the IsEnabled property changes.
        //
        // Parameters:
        //   isEnabled:
        //     New value of the IsEnabled property.
        protected override void OnIsEnabledChanged(bool isEnabled);
    }
}

namespace System.Windows.Controls
{
    // Summary:
    //     Represents a button control, which reacts to the Click event.
    [TemplatePart(Name = "Normal State", Type = typeof(Storyboard))]
    [TemplatePart(Name = "MouseOver State", Type = typeof(Storyboard))]
    [TemplatePart(Name = "RootElement", Type = typeof(FrameworkElement))]
    [TemplatePart(Name = "Pressed State", Type = typeof(Storyboard))]
    [TemplatePart(Name = "FocusVisualElement", Type = typeof(UIElement))]
    [TemplatePart(Name = "Disabled State", Type = typeof(Storyboard))]
    public class Button : ButtonBase
    {
        // Summary:
        //     Initializes a new instance of the Button class.
        public Button();
        // Summary:
        //     Apply a template to the Button.
        protected override void OnApplyTemplate();
        //
        // Summary:
        //     Called when the IsEnabled property changes.
        //
        // Parameters:
        //   isEnabled:
        //     New value of the IsEnabled property.
        protected override void OnIsEnabledChanged(bool isEnabled);
    }
}

 [TemplatePart(Name = "Normal State", Type = typeof(Storyboard))] 这种东东是做什么用的 , 其实这是一种契约 , 是一种推荐的控件设计模式(只是推荐) , 意思是告诉要来写ControlTemplate的用户 , 你的ControlTemplate中需要有一个x:Name为“Normal State” , 类型为Storyboard , 当然这个类型可以是继承来的, 为什么一定要包含这些契约规定的元素 , 因为逻辑部分对这些东西进行了引用,它们将对控件的默认行为起着关键作用, 可以理解为这个控件的最基本元素 , 是实现默认行为的最小集合, 自然,你的ControlTemplate中如果没有包含契约中的内容 , 则相应的逻辑将无法实现。

您可能感兴趣的与本文相关的镜像

ACE-Step

ACE-Step

音乐合成
ACE-Step

ACE-Step是由中国团队阶跃星辰(StepFun)与ACE Studio联手打造的开源音乐生成模型。 它拥有3.5B参数量,支持快速高质量生成、强可控性和易于拓展的特点。 最厉害的是,它可以生成多种语言的歌曲,包括但不限于中文、英文、日文等19种语言

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Controls; using System.Windows; using System.Windows.Data; using System.Windows.Media; using System.Windows.Input; namespace PavementStructureCalculate.Common { [TemplatePart(Name = PART_TextBox, Type = typeof(TextBox))] [TemplatePart(Name = PART_Button, Type = typeof(Button))] public class PickerTextBox : Control { internal const string PART_TextBox = "PART_TextBox"; internal const string PART_Button = "PART_Button"; private Button? _button; static PickerTextBox() { // 让 WPF 去 Themes/Generic.xaml 找默认模板 DefaultStyleKeyProperty.OverrideMetadata( typeof(PickerTextBox), new FrameworkPropertyMetadata(typeof(PickerTextBox))); } public override void OnApplyTemplate() { base.OnApplyTemplate(); if (_button != null) _button.Click -= Button_Click; // 绑定内部 TextBox var tb = GetTemplateChild(PART_TextBox) as TextBox; if (tb != null) { tb.SetBinding(TextBox.TextProperty, new Binding(nameof(Text)) { Source = this, Mode = BindingMode.TwoWay, UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged }); tb.SetBinding(TextBox.IsReadOnlyProperty, new Binding(nameof(IsReadOnly)) { Source = this }); } // 绑定按钮点击 → 转成对外事件 _button = GetTemplateChild(PART_Button) as Button; if (_button != null) _button.Click += Button_Click; } private void Button_Click(object? sender, RoutedEventArgs e) { if (PickRequested == null) return; // 没人订阅就不做任何事 var args = new PickRequestedEventArgs(Text ?? string.Empty); PickRequested.Invoke(this, args); if (args.Handled && args.ResultText is not null) Text = args.ResultText; // 写回到 Text } // ---- 依赖属性:Text ---- public static readonly DependencyProperty TextProperty = DependencyProperty.Register(nameof(Text), typeof(string), typeof(PickerTextBox), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); public string Text { get => (string)GetValue(TextProperty); set => SetValue(TextProperty, value); } // ---- 依赖属性:IsReadOnly(可选) ---- public static readonly DependencyProperty IsReadOnlyProperty = DependencyProperty.Register(nameof(IsReadOnly), typeof(bool), typeof(PickerTextBox), new PropertyMetadata(false)); public bool IsReadOnly { get => (bool)GetValue(IsReadOnlyProperty); set => SetValue(IsReadOnlyProperty, value); } // ---- 依赖属性:ButtonWidth ---- public static readonly DependencyProperty ButtonWidthProperty = DependencyProperty.Register(nameof(ButtonWidth), typeof(double), typeof(PickerTextBox), new PropertyMetadata(32d)); public double ButtonWidth { get => (double)GetValue(ButtonWidthProperty); set => SetValue(ButtonWidthProperty, value); } // ---- 依赖属性:ButtonContent ---- public static readonly DependencyProperty ButtonContentProperty = DependencyProperty.Register(nameof(ButtonContent), typeof(object), typeof(PickerTextBox), new PropertyMetadata("…")); // 默认省略号 public object? ButtonContent { get => GetValue(ButtonContentProperty); set => SetValue(ButtonContentProperty, value); } // ---- 对外事件(像 Click 一样在 XAML 里写)---- public class PickRequestedEventArgs : EventArgs { public PickRequestedEventArgs(string currentText) { CurrentText = currentText; } public string CurrentText { get; } public string? ResultText { get; set; } public bool Handled { get; set; } } public event EventHandler<PickRequestedEventArgs>? PickRequested; } } 保持原功能不变 能不能给控件四周圆角 和设置边框
最新发布
09-23
<think> 我们正在讨论的是WPF中的PickerTextBox控件。根据用户描述,PickerTextBox是某个日期选择器或其他选择器的一部分(可能是自定义控件或第三方控件),它通常是一个文本框,用于显示选择的值,并可能有一个按钮用于打开选择器。 用户需求:在保持原功能不变的前提下,为PickerTextBox控件设置四周圆角和边框。 在WPF中,要实现控件的圆角和边框,常用的方法有: 1. 使用ControlTemplate重新定义控件的模板,并在其中设置边框(Border)的CornerRadius和BorderThickness等属性。 2. 如果控件本身支持这些属性(比如Border控件),但PickerTextBox可能不直接支持,因此我们需要通过修改模板来实现。 由于PickerTextBox是内部控件(可能是DatePicker或其他Picker的模板的一部分),我们需要找到它的默认样式并修改。 步骤: 1. 获取PickerTextBox的默认样式和模板。我们可以使用Blend或通过查找默认样式来获取。 2. 在模板的最外层添加一个Border控件,并设置其CornerRadius(圆角)和BorderThickness(边框厚度)以及BorderBrush(边框颜色)属性。 3. 确保原功能不变,意味着我们不应该改变模板内部的逻辑结构和原有功能。 注意:PickerTextBox通常是DatePicker控件模板的一部分。在WPF中,DatePicker控件由两部分组成:一个DatePickerTextBox和一个按钮。但是,用户提到的PickerTextBox可能是自定义控件,因此我们假设它是类似DatePicker的内部TextBox部分。 由于我们不知道PickerTextBox的确切定义,我们可以按照常见做法:为该控件添加一个Border作为父容器,或者修改其模板。 方案一:在PickerTextBox外部包裹Border(如果结构允许且不影响功能) ```xaml <Border CornerRadius="5" BorderThickness="1" BorderBrush="Gray"> <local:PickerTextBox ... /> </Border> ``` 但是这样可能会影响布局,并且PickerTextBox可能已经包含在某个父容器中,所以可能不适用。 方案二:修改PickerTextBox的控件模板(推荐) 我们需要为PickerTextBox创建一个新的控件模板,该模板基于原始模板,并在最外层添加一个Border。 步骤: 1. 在项目中找到PickerTextBox的默认样式(如果有的话),或者通过右键控件在Blend中编辑模板来获取默认样式。 2. 将默认样式复制一份,并在其模板的最外层添加一个Border,设置所需的圆角和边框属性,然后将原模板的内容放到这个Border里面。 假设我们无法直接获取PickerTextBox的默认模板,我们可以尝试以下通用方法: 由于PickerTextBox可能继承自TextBox,我们可以尝试创建一个针对TextBox的样式,并设置其Template。但是,如果PickerTextBox是自定义控件,我们需要知道它的类型。 为了安全起见,我们可以为PickerTextBox创建一个Style,并设置其模板。以下是一个示例: ```xaml <Style x:Key="RoundedPickerTextBoxStyle" TargetType="{x:Type local:PickerTextBox}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:PickerTextBox}"> <!-- 添加一个边框作为圆角边框 --> <Border CornerRadius="5" BorderThickness="1" BorderBrush="Gray" Background="{TemplateBinding Background}"> <!-- 原来的内容可能是ScrollViewer或其他,这里假设原始模板是一个包含ScrollViewer的TextBoxBase --> <ScrollViewer x:Name="PART_ContentHost" Margin="0"/> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style> ``` 请注意,上面的模板是一个简化的示例,实际PickerTextBox的模板可能更复杂。因此,我们最好先获取原始模板,然后在原始模板的基础上修改。 在WPF中,DatePicker的文本框部分可以通过DatePicker.TextBoxStyle属性来设置样式。但如果是自定义的PickerTextBox,我们需要按照自定义控件的方式来处理。 如果我们无法获取原始模板,可以尝试以下替代方法:使用附加属性来为任意控件添加圆角和边框。但是,这种方法可能不适用于所有控件,特别是当控件有多个嵌套边框时。 另一种方法是使用装饰器(Adorner),但这样可能会影响性能,并且实现复杂。 因此,推荐的方法是获取原始模板并修改。 假设我们成功获取了PickerTextBox的原始模板,并在其最外层添加了Border,那么样式可能如下所示: ```xaml <Style TargetType="{x:Type local:PickerTextBox}"> <Setter Property="SnapsToDevicePixels" Value="True"/> <Setter Property="OverridesDefaultStyle" Value="True"/> <Setter Property="KeyboardNavigation.TabNavigation" Value="None"/> <Setter Property="FocusVisualStyle" Value="{x:Null}"/> <Setter Property="MinWidth" Value="120"/> <Setter Property="MinHeight" Value="20"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:PickerTextBox}"> <!-- 原始模板通常有一个Border,我们可以修改这个Border的属性,或者在其外面再包裹一个Border --> <!-- 这里我们在原始模板的最外层添加一个Border以实现圆角 --> <Border CornerRadius="5" BorderThickness="1" BorderBrush="Gray"> <!-- 原始模板的内容从这里开始 --> <Border x:Name="border" BorderThickness="1" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}"> <!-- 省略内部细节,例如ScrollViewer等 --> <ScrollViewer x:Name="PART_ContentHost" /> </Border> </Border> <!-- 注意:原始模板可能还有Triggers,我们也需要复制过来 --> <ControlTemplate.Triggers> <!-- ... --> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> ``` 但是,由于我们不知道原始模板的具体结构,以上代码仅为示例。 实际步骤: 1. 在Visual Studio中,打开包含PickerTextBox的窗口的XAML文件。 2. 在Design视图中,右键点击PickerTextBox,选择“编辑模板”>“编辑副本...”,这样就会创建一份默认模板的副本。 3. 在生成的样式中,找到最外层的容器(可能是Grid或Border),然后将其放入一个带圆角的Border中,或者修改最外层Border的CornerRadius属性(如果已经是Border)。 4. 如果原始模板的最外层不是Border,我们可以将其改为Border,或者在外面包裹一个Border,并设置圆角。 注意:包裹Border可能会改变布局,比如边距等,需要调整。 由于用户要求保持原功能不变,我们必须小心不要改变原始模板的内部结构。 总结:通过编辑PickerTextBox的控件模板,在最外层添加一个Border并设置圆角和边框,然后应用这个样式。这样就能在保持原功能不变的前提下实现圆角和边框。 相关问题: 1. 如何获取WPF控件的默认样式和模板? 2. 在WPF中,如何为其他控件(如Button、TextBox)设置圆角? 3. 如何创建和应用WPF控件样式? </think> 在 WPF 中为 `PickerTextBox` 控件添加圆角和边框同时保持原有功能,可以通过自定义控件模板实现。以下是具体步骤和代码示例: ### 解决方案(使用 ControlTemplate 修改样式) ```xml <Style TargetType="{x:Type local:PickerTextBox}"> <!-- 替换为实际命名空间 --> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:PickerTextBox}"> <!-- 外层 Border 实现圆角和边框 --> <Border CornerRadius="8" BorderThickness="1" BorderBrush="#FFA0A0A0" Background="{TemplateBinding Background}"> <!-- 原控件内容容器(保持原有功能结构) --> <Grid> <ScrollViewer x:Name="PART_ContentHost" Focusable="False" HorizontalAlignment="Stretch" VerticalAlignment="Center"/> <!-- 其他原有元素(如按钮等) --> </Grid> </Border> <!-- 触发状态(保持原有交互效果) --> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter TargetName="border" Property="BorderBrush" Value="#FF3C7FB1"/> </Trigger> <Trigger Property="IsFocused" Value="True"> <Setter TargetName="border" Property="BorderBrush" Value="#FF569DE5"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> ``` ### 关键点说明 1. **圆角实现** 通过 `Border.CornerRadius` 属性控制圆角大小(示例值为 8,可调整) 2. **边框定制** - `BorderThickness`:边框粗细 - `BorderBrush`:边框颜色 - 在触发器中动态改变边框颜色(悬停/聚焦状态) 3. **功能保留** - 保留 `PART_ContentHost` 部件(WPF 文本框必需部分)[^1] - 保持原有布局结构(如按钮位置等) - 通过 `TemplateBinding` 继承背景色等属性 4. **使用注意事项** ```xml <!-- 应用示例 --> <local:PickerTextBox Width="200" Height="30" Style="{StaticResource RoundedPickerStyle}" /> ``` ### 效果对比 | 原控件 | 修改后 | |--------|--------| | ![原控件](https://via.placeholder.com/200x30/EEE?text=Flat) | ![圆角控件](https://via.placeholder.com/200x30/EEE?text=Rounded+Border) | > **提示**:若控件来自第三方库,请检查文档确认可定制性;若需精确控制阴影效果,可参考 Flutter Card 的 elevation 实现思路[^1]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值