我有一个自定义的TextBox控件。我试图将它绑定到对象的简单描述字符串属性。如何让绑定工作?如果我将其更改为TextBox,则相同的绑定可以正常工作。
<Style x:Key="{x:Type TaskDash:TextBoxWithDescription}" TargetType="{x:Type TaskDash:TextBoxWithDescription}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TaskDash:TextBoxWithDescription}">
<TextBox>
</TextBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
public class TextBoxWithDescription : TextBox
{
public TextBoxWithDescription()
{
LabelText = String.Empty;
}
public string LabelText { get; set; }
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var textBlock = (TextBlock)this.Template.FindName("LabelText", this);
if (textBlock != null) textBlock.Text = this.LabelText;
}
}
<TaskDash:TextBoxWithDescription Grid.Row="0" Grid.Column="0"
x:Name="textBoxDescription"
Text="{Binding Description, BindsDirectlyToSource=True}" LabelText="Description">
</TaskDash:TextBoxWithDescription>
public partial class EditTaskItem : Window
{
private TaskItem _taskItem;
public EditTaskItem(TaskItem taskItem)
{
InitializeComponent();
this.DataContext = taskItem;
textBoxDescription.DataContext = taskItem;
_taskItem = taskItem;
}
}
答案 0 :(得分:6)
好的......你的代码有几个问题。 让我们从您的自定义控件的样式开始。您需要添加一个静态构造函数,以允许重新设置新控件。喜欢这个
static TextBoxWithDescription ()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(TextBoxWithDescription ), new FrameworkPropertyMetadata(typeof(TextBoxWithDescription )));
}
这告诉WPF“嘿,请为此控件寻找样式”。
现在您可以删除x:Key="{x:Type TaskDash:TextBoxWithDescription}"
,因为这将是您的默认样式。
接下来是。在WPF中,要理解的是,每个控件都绝对没有UI内容,除非它获得模板。您的Style中已经有一个模板,但它获得的唯一可视内容是一个空的TextBox。这很奇怪,因为您从TextBox派生了TextBoxWithDescription。所以你创建的是一个从文本框派生的控件,包含一个文本框。 请参阅this以了解TextBox模板在WPF 4.0中的外观。
返回ControlTemplate中的空TextBox。请记住,我说过,没有风格的控件是完全不可见的,它是唯一的逻辑。唯一可见的是模板中的TextBox。为了使它以某种方式工作,您需要将一些属性传递给此TextBox。控件说明了如何以及模板获取属性并将其置于使用中。
您可以通过TemplateBinding
执行此操作
例如。如果您的控件具有属性背景,则只要您愿意,此属性不会执行任何操作,但ControlTemplate可以为其赋予一些含义。例如,在Rectangle
中添加ControlTemplate
并将填充属性设置为{TemplateBinding Background}
。哪个基本告诉Rectangle“你的属性填充将是我们目前模板控件的背景值”。我希望这说清楚。
接下来的事情:你覆盖OnApplyTemplate
通常这样做是为了在你的ControlTemplate中按名称查找一个控件。您似乎将此与您的某个属性混合在一起。要通过FindName在模板中查找控件,您需要为其命名
喜欢这个
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TaskDash:TextBoxWithDescription}">
<TextBox x:Name="PART_MyTextBox">
</TextBox>
</ControlTemplate>
</Setter.Value>
</Setter>
并将OnApplyTemplate修改为
var textBlock = (TextBlock)this.Template.FindName("PART_MyTextBox", this);
现在您在当前的ControlTemplate中有了文本块。
我能看到的最后一个错误是。
您将TextBox文本OnApplyTemplate
设置为LabelText。虽然这有效,但有一次,它不是很好,通常不是WPF方式。此外,如果您修改LabelText属性,它将不会显示,因为您必须再次设置它。
将LabelText更改为dependency property现在,您可以使用已提及的TemplateBinding
直接在控件模板TextBox上设置此文本。
<TextBox x:Name="PART_MyTextBox" Text="{TemplateBinding LabelText}>
</TextBox>
这也将确保对Control属性的更改也将更新此TextBox上的Text。
最后一件事
this.DataContext = taskItem;
// textBoxDescription.DataContext = taskItem;
_taskItem = taskItem;
如果您的textboxdescription将成为窗口的父级,则不需要显式设置DataContext
,因为它将在层次结构中继承。只要Element不更改DataContext,它就始终是父元素的DataContext。
我建议你阅读更多关于WPF的基础知识,我知道它有一个陡峭的学习曲线,但它值得努力。如果有人来自WinForms背景,很难让所有不同的新设计理念和不同的做事方式得到满足。
希望有所帮助
答案 1 :(得分:0)
@dowhilefor - 很棒的答案。我把它写成答案只是因为评论时间太长了。
@Shawn - 看起来你正在尝试进行Label-Field控制。如果是这种情况,请尝试以下模板:
<ControlTemplate TargetType="{x:Type TaskDash:TextBoxWithDescription}">
<Grid>
<Grid.ColumnDefinitions>
<!--The SharedSizeGroup property will allow us to align all text boxes to the same vertical line-->
<ColumnDefinition Width="Auto"
SharedSizeGroup="LabelColumn"/>
<!--This column acts as a margin between the label and the text box-->
<ColumnDefinition Width="5"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Text="{TemplateBinding LabelText}"
VerticalAlignment="Center"/>
<Border Grid.Column="2"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer x:Name="PART_ContentHost"
Padding="{TemplateBinding Padding}"/>
</Border>
</Grid>
</ControlTemplate>
并删除OnApplyTemplate的覆盖。
如果控件是(通常称为)“PropertyGrid”的一部分,并且您在同一面板中有多个TextBoxWithDescription控件实例,请在该面板上定义Grid.IsSharedSizeScope
(它不必是{ {1}}小组)。这会将文本框与统一的垂直线对齐。