c# - WPF TextAlignmentProperty.OverrideMetadata 在 TextBox 的继承者中不起作用
问题描述
我创建了一个自定义控件,它继承了 TextBox。它基本上有一个额外的属性'Seconds'并在'Text'上设置绑定,以显示'Seconds'格式化,例如。2m 5s,使用转换器。
我现在想默认右对齐文本。从其他自定义控件我知道我们有时会想要使用样式设置/覆盖值。如果我直接在构造函数中设置值,我将无法做到这一点。
我通常会这样:
TextAlignmentProperty.OverrideMetadata(typeof(DurationTextBox), new FrameworkPropertyMetadata(TextAlignment.Right));
但这似乎不起作用:
前两个具有默认对齐方式,然后它们在控件上直接左对齐、居中对齐和右对齐。秒行有一个样式设置对齐到中心
我已经将一个 TextBlock 绑定到第一个 DurationTextBox 的 TextAlignment,这表明对齐是“右”,但这不是它的显示方式!
谁能解释一下:
A. 为什么这不起作用?
B.如何正确地做到这一点,或者具有相同的最终效果?(默认右对齐,但可以从样式中覆盖)
C#类:
请注意,这是一个简化版本。完整的有Min,Max,确认值更改的选项和超出范围操作的选项,这是类结构的原因。请继续关注 TextAlignment 问题!(可以去掉 SecondsToDurationStringConverter 和 DurationStringValidator 使示例编译效果相同)
public class DurationTextBox : TextBox
{
#region Dependency properties
/// <summary>
/// Property for <see cref="Seconds"/>
/// </summary>
[NotNull] public static readonly DependencyProperty SecondsProperty = DependencyProperty.Register(nameof(Seconds), typeof(double), typeof(Demo.DurationTextBox), new FrameworkPropertyMetadata(default(double), SecondsChangedCallback) { BindsTwoWayByDefault = true });
/// <summary>
/// Seconds to show as duration string
/// </summary>
public double Seconds
{
// ReSharper disable once PossibleNullReferenceException
get { return (double)GetValue(SecondsProperty); }
set { SetValue(SecondsProperty, value); }
}
/// <summary>
/// Property for <see cref="EditValue"/>
/// </summary>
[NotNull] public static readonly DependencyProperty EditValueProperty = DependencyProperty.Register(nameof(EditValue), typeof(double), typeof(Demo.DurationTextBox), new FrameworkPropertyMetadata(default(double), EditValueChangedCallback) { BindsTwoWayByDefault = true });
/// <summary>
/// Number being edited by the actual text box. Transferred to <see cref="Seconds"/>.
/// <para>Do NOT bind to this property from outside this control</para>
/// </summary>
public double EditValue
{
// ReSharper disable once PossibleNullReferenceException
get { return (double)GetValue(EditValueProperty); }
set { SetValue(EditValueProperty, value); }
}
#endregion Dependency properties
private bool _isLocked;
static DurationTextBox()
{
// TextAlignment
TextAlignmentProperty.OverrideMetadata(typeof(Demo.DurationTextBox), new FrameworkPropertyMetadata(TextAlignment.Right));
}
/// <inheritdoc />
public DurationTextBox()
{
SecondsToDurationStringConverter secondsToDurationStringConverter = new SecondsToDurationStringConverter();
// Text
Binding binding = new Binding(nameof(EditValue)) { Source = this, Converter = secondsToDurationStringConverter, NotifyOnValidationError = true };
binding.ValidationRules.Add(new DurationStringValidation());
SetBinding(TextProperty, binding);
}
private static void SecondsChangedCallback([CanBeNull] DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Demo.DurationTextBox durationTextBox = d as Demo.DurationTextBox;
if (durationTextBox == null) return;
if (!durationTextBox._isLocked)
{
durationTextBox._isLocked = true;
durationTextBox.SetCurrentValue(EditValueProperty, durationTextBox.Seconds);
durationTextBox._isLocked = false;
}
}
private static void EditValueChangedCallback([CanBeNull] DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Demo.DurationTextBox durationTextBox = d as Demo.DurationTextBox;
if (durationTextBox == null) return;
if (!durationTextBox._isLocked)
{
durationTextBox._isLocked = true;
durationTextBox.SetCurrentValue(SecondsProperty, durationTextBox.EditValue);
durationTextBox._isLocked = false;
}
}
}
XAML 代码:
<Label Content="Demo.DurationTextBox" FontWeight="Bold"/>
<WrapPanel>
<demo:DurationTextBox MinWidth="150" Seconds="{Binding ElementName=Duration1, Path=Text}" x:Name="DemoDurationTextBox"/>
<TextBlock Text="{Binding ElementName=DemoDurationTextBox, Path=TextAlignment}"/>
<demo:DurationTextBox MinWidth="150" Seconds="{Binding ElementName=Duration1, Path=Text}" />
<demo:DurationTextBox MinWidth="150" Seconds="{Binding ElementName=Duration1, Path=Text}" TextAlignment="Left"/>
<demo:DurationTextBox MinWidth="150" Seconds="{Binding ElementName=Duration1, Path=Text}" TextAlignment="Center"/>
<demo:DurationTextBox MinWidth="150" Seconds="{Binding ElementName=Duration1, Path=Text}" TextAlignment="Right"/>
</WrapPanel>
<WrapPanel>
<WrapPanel.Resources>
<Style TargetType="demo:DurationTextBox">
<Setter Property="TextAlignment" Value="Center"/>
</Style>
</WrapPanel.Resources>
<demo:DurationTextBox MinWidth="150" Seconds="{Binding ElementName=Duration1, Path=Text}"/>
<demo:DurationTextBox MinWidth="150" Seconds="{Binding ElementName=Duration1, Path=Text}" />
<demo:DurationTextBox MinWidth="150" Seconds="{Binding ElementName=Duration1, Path=Text}" TextAlignment="Left" />
<demo:DurationTextBox MinWidth="150" Seconds="{Binding ElementName=Duration1, Path=Text}" TextAlignment="Center" />
<demo:DurationTextBox MinWidth="150" Seconds="{Binding ElementName=Duration1, Path=Text}" TextAlignment="Right" />
</WrapPanel>
解决方案
A:这不起作用,因为TextBox.TextAlignment
是继承的,所以默认情况下,活动值由父控件在运行时确定。DependencyProperty
类覆盖静态对象本身的默认值是没有意义的。
您添加的调试绑定显示“正确”的事实很奇怪,并且可能是内部 WPF 优化故障。
B:正确的解决方案是在父控件上设置TextBox.TextAlignment="Right"
(注意类型名称限定符),例如您的包装面板。然后该值将自动应用于所有子文本块,除非它们或中间父级进一步覆盖它,包括通过样式。
C:我要补充一点,您发布的代码似乎是一种重新发明的尝试DataGrid
。该控件支持开箱即用的事务编辑,如果您切换到它,可能会为您节省大量时间!
推荐阅读
- angular7 - 垫分页器问题
- python - Django:在模型的字段上设置 unique_together 以及字段条件
- oracle - 获取总数
- swiftui - 如何在swiftUI中登录页面后隐藏导航栏
- laravel - 插入数据时尝试获取非对象错误的属性“stock_name”
- javascript - 选择选项时调用 javascript 函数但在选项本身内部需要它
- javascript - 将 SVG 图标用作手风琴菜单中的单击图标时如何转换/反转 SVG 图标
- automation - BrowserStack-是否可以自动化测试`
- android - 删除android中折叠工具栏布局内进度条中的滚动效果
- php - 如何在单个 foreach 中使用两个数组