WPF自定义文本框控件未正确绑定文本

时间:2011-08-11 16:59:49

标签: c# wpf xaml data-binding

我有一个自定义的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;
    }
}

2 个答案:

答案 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}}小组)。这会将文本框与统一的垂直线对齐。