【问题标题】:WPF custom Textbox control isn't properly binding textWPF 自定义文本框控件未正确绑定文本
【发布时间】:2011-08-11 16:59:48
【问题描述】:

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

【问题讨论】:

    标签: c# wpf xaml data-binding


    【解决方案1】:

    好的...您的代码有一些问题。 让我们从自定义控件的样式开始。您需要添加一个允许重新设置新控件样式的静态构造函数。像这样

        static TextBoxWithDescription ()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(TextBoxWithDescription ), new FrameworkPropertyMetadata(typeof(TextBoxWithDescription )));
        }
    

    这告诉 WPF“嘿,请为此控件查找样式”。

    现在您可以删除x:Key="{x:Type TaskDash:TextBoxWithDescription}",因为这将是您的默认样式。

    接下来就是。在 WPF 中要理解的一件事是,每个控件都绝对没有 UI 内容,除非它获得一个模板。您的样式中已经有一个模板,但它获得的唯一视觉内容是一个空的文本框。这很奇怪,因为您从 TextBox 派生了 TextBoxWithDescription。所以你创建的是一个从文本框派生的控件,包含一个文本框。 请参阅 this 以了解 TextBox 模板在 WPF 4.0 中的外观。

    返回到 ControlTemplate 中的空文本框。记住我说过,没有样式的控件是完全不可见的,它是唯一的逻辑。唯一可见的是模板中的文本框。要使其以某种方式工作,您需要将一些属性传递给此 TextBox。控件说明模板如何获取属性并将其投入使用。 您可以通过TemplateBinding 执行此操作 例如。如果您的控件具有属性背景,则此属性不会执行任何操作,您可以根据需要设置它,但 ControlTemplate 可以赋予它一些意义。例如,在您的ControlTemplate 中添加一个Rectangle,并将Fill 属性设置为{TemplateBinding Background}。这基本上告诉 Rectangle “您的属性 Fill 将成为我们当前正在模板化的控件的 Background 的值”。我希望这可以说清楚。

    接下来:您覆盖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 中有了文本块。

    我能看到的最后一个错误是。 您将OnApplyTemplatethe TextBox Text 设置为您的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 背景,就很难理解所有不同的新设计理念和不同的做事方式。

    希望对你有所帮助

    【讨论】:

    • 感谢您的详细反馈。今天晚些时候我会尝试更改模板。你所说的模板 TextBox 是有道理的......我以前有 Rectangle 或 Border 之类的东西,而不是 TextBox。我认为该控件当时实际上正在工作,但我遇到了其他问题并决定更改为 TextBox。
    【解决方案2】:

    @dowhilefor - 很好的答案。我写这个作为答案只是因为评论太长了。

    @Shawn - 您似乎正在尝试制作标签字段控件。如果是这种情况,试试这个模板:

    <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(不必是 Grid控制板)。这会将文本框对齐到一条统一的垂直线。

    【讨论】:

      猜你喜欢
      • 2012-11-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-14
      • 1970-01-01
      • 2014-01-26
      • 2012-11-24
      相关资源
      最近更新 更多