本文介绍了访问自定义控件WPF C#的子级的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对WPF UserControl 很有经验,我想到了尝试自定义控件。这是我到目前为止的内容:

Being quite experienced with WPF UserControl, I thought of trying out custom controls. Here's what I have so far :

Generic.XAML

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:testapp">
    <Style TargetType="{x:Type local:testcontrol}">
        <Setter Property="Height" Value="45"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:testcontrol}">
                    <TextBlock x:Name="tb" Text="test"/>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

CustomControl.Cs

using System.Windows.Controls.Primitives;

public class testcontrol : System.Windows.Controls.Control
{
    public TextBlock tb;

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        tb = this.GetTemplateChild("tb");
    }

    public static test()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof (testcontrol), new FrameworkPropertyMetadata(typeof (testcontrol)));
    }

    private void testcontrol_MouseDown(object sender, MouseButtonEventArgs e)
    {
        tb.Text = "Abc";
    }
}

现在当 MouseDown 事件被触发, TextBlock 的文本更改,这意味着该文本块已找到/可访问...

Now when the MouseDown event is fired, the TextBlock's text changes which means the textblock is found/accessible...

但是当我从窗口执行如下操作时:

But when i do the same thing from my window as follows :

 private void test()
 {
     testcontrol tt = new testcontrol();
     tt.tb.Text = "abc";
 }

a NullReferenceException 是抛出。我想知道为什么找不到 TextBlock 吗?

a NullReferenceException is thrown. I wonder why it fails to find the TextBlock? Any explanation will be highly appreciated.

最后一件事,我刚开始使用自定义控件,因此请亲切指出我的方法是否正确:)

One last thing, I just got started with custom controls so please be kind to indicate if my approach is correct :)

推荐答案

如注释所讨论,您应该使用Dependency属性或Routed Events更新子模板化的父级,而不是直接访问。

As discussed on comments, you should use the Dependency Property or Routed Events to update the child templated parent instead of direct accessing.

下面是自定义控件的简单示例,它前面带有标题(TextBlock)。

Below is simple example of Custom Control which has Header (TextBlock) before it.

[TemplatePart(Name = "PreText", Type = typeof(TextBlock))]
public class ExtendedTextBox : TextBox
{
    public static readonly DependencyProperty PreTextDependency = DependencyProperty.Register("PreText", typeof(string), typeof(ExtendedTextBox));

    public string PreText
    {
        get
        {
            return (string)GetValue(PreTextDependency);
        }

        set
        {
            SetValue(PreTextDependency, value);
        }
    }

    private TextBlock preTextBlock;

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        preTextBlock = GetTemplateChild("PreText") as TextBlock;
        Binding preTextBinding = new Binding("PreText");
        preTextBinding.Source = this;
        preTextBinding.Mode = BindingMode.TwoWay;
        preTextBlock.SetBinding(TextBlock.TextProperty, preTextBinding);
    }
}

ExtendedTextBox :

    <SolidColorBrush x:Key="TextBox.Static.Border" Color="#FFABAdB3"/>
    <SolidColorBrush x:Key="TextBox.MouseOver.Border" Color="#FF7EB4EA"/>
    <SolidColorBrush x:Key="TextBox.Focus.Border" Color="#FF569DE5"/>
    <Style x:Key="ExtendedTextBoxStyle" TargetType="{x:Type local:ExtendedTextBox}">
        <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
        <Setter Property="BorderBrush" Value="{StaticResource TextBox.Static.Border}"/>
        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
        <Setter Property="HorizontalContentAlignment" Value="Left"/>
        <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
        <Setter Property="AllowDrop" Value="true"/>
        <Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
        <Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:ExtendedTextBox}">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition Width="*" />
                        </Grid.ColumnDefinitions>
                        <TextBlock Grid.Column="0" Text="{Binding PreText}" Name="PreText" IsHitTestVisible="False" />
                        <Border Grid.Column="1" x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
                            <ScrollViewer x:Name="PART_ContentHost" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
                        </Border>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsEnabled" Value="false">
                            <Setter Property="Opacity" TargetName="border" Value="0.56"/>
                        </Trigger>
                        <Trigger Property="IsMouseOver" Value="true">
                            <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource TextBox.MouseOver.Border}"/>
                        </Trigger>
                        <Trigger Property="IsKeyboardFocused" Value="true">
                            <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource TextBox.Focus.Border}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Style.Triggers>
            <MultiTrigger>
                <MultiTrigger.Conditions>
                    <Condition Property="IsInactiveSelectionHighlightEnabled" Value="true"/>
                    <Condition Property="IsSelectionActive" Value="false"/>
                </MultiTrigger.Conditions>
                <Setter Property="SelectionBrush" Value="{DynamicResource {x:Static SystemColors.InactiveSelectionHighlightBrushKey}}"/>
            </MultiTrigger>
        </Style.Triggers>
    </Style>

如何在XAML中定义:

How to define in XAML :

    <local:ExtendedTextBox x:Name="extendedTextBox" Grid.Row="1" Style="{StaticResource ExtendedTextBoxStyle}" Text="Some Text!!" PreText="Pre Text :" />

现在应该如何更新子模板Value了(我给了一个 extendedTextBox ):

Now how you should update the template child Value (I've give a name of extendedTextBox):

this.extendedTextBox.PreText = "I'm clicked";

这篇关于访问自定义控件WPF C#的子级的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-20 21:25