访问自定义控件 wpf c# 的子级
Access children of Custom Control wpf c#
我对 WPF 很有经验 UserControl
,我想尝试自定义控件。这是我目前所拥有的:
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
的文本发生变化,这意味着文本块是 found/accessible...
但是当我从我的 window 中做同样的事情时,如下所示:
private void test()
{
testcontrol tt = new testcontrol();
tt.tb.Text = "abc";
}
a NullReferenceException
被抛出。我想知道为什么找不到 TextBlock
?任何解释将不胜感激。
最后一件事,我刚开始使用自定义控件,所以请指出我的方法是否正确:)
您可以将此扩展方法放在辅助程序中 class
public static IEnumerable<T> GetVisualChildren<T>(this DependencyObject parent) where T : DependencyObject
{
if (parent == null)
{
yield return null;
}
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(parent, i);
if (child is T t)
{
yield return t;
}
foreach (T childrensChild in child.FindVisualChildren<T>())
{
yield return childrensChild;
}
}
}
然后你可以像这样使用它:
testcontrol tt = new testcontrol();
IEnumerable<TextBlock> allTextBlocks = tt.GetVisualChildren<TextBlock>();
在你的 testcontrol
中获取 所有 TextBlock
。
特别是对于你的情况,你可以使用
testcontroll tt = new testcontrol();
IEnumerable<TextBlock> allTextBlocks = tt.GetVisualChildren<TextBlock>();
TextBlock tb = allTextBlocks.Single(t => t.Name = "tb");
if(tb != null)
{
tb.Text = "abc";
}
我不确定 FindTemplateChild<T>
在这里是否有用;至少它已经内置了,所以你可以试试看。
如评论所述,您应该使用依赖关系 属性 或路由事件来更新 child 模板化 parent 而不是直接访问。
下面是自定义控件的简单示例,它前面有 Header (TextBlock)。
[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);
}
}
XAML 这个 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中定义:
<local:ExtendedTextBox x:Name="extendedTextBox" Grid.Row="1" Style="{StaticResource ExtendedTextBoxStyle}" Text="Some Text!!" PreText="Pre Text :" />
现在你应该如何更新模板 child 值(我给的名字是 extendedTextBox
):
this.extendedTextBox.PreText = "I'm clicked";
我对 WPF 很有经验 UserControl
,我想尝试自定义控件。这是我目前所拥有的:
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
的文本发生变化,这意味着文本块是 found/accessible...
但是当我从我的 window 中做同样的事情时,如下所示:
private void test()
{
testcontrol tt = new testcontrol();
tt.tb.Text = "abc";
}
a NullReferenceException
被抛出。我想知道为什么找不到 TextBlock
?任何解释将不胜感激。
最后一件事,我刚开始使用自定义控件,所以请指出我的方法是否正确:)
您可以将此扩展方法放在辅助程序中 class
public static IEnumerable<T> GetVisualChildren<T>(this DependencyObject parent) where T : DependencyObject
{
if (parent == null)
{
yield return null;
}
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(parent, i);
if (child is T t)
{
yield return t;
}
foreach (T childrensChild in child.FindVisualChildren<T>())
{
yield return childrensChild;
}
}
}
然后你可以像这样使用它:
testcontrol tt = new testcontrol();
IEnumerable<TextBlock> allTextBlocks = tt.GetVisualChildren<TextBlock>();
在你的 testcontrol
中获取 所有 TextBlock
。
特别是对于你的情况,你可以使用
testcontroll tt = new testcontrol();
IEnumerable<TextBlock> allTextBlocks = tt.GetVisualChildren<TextBlock>();
TextBlock tb = allTextBlocks.Single(t => t.Name = "tb");
if(tb != null)
{
tb.Text = "abc";
}
我不确定 FindTemplateChild<T>
在这里是否有用;至少它已经内置了,所以你可以试试看。
如评论所述,您应该使用依赖关系 属性 或路由事件来更新 child 模板化 parent 而不是直接访问。
下面是自定义控件的简单示例,它前面有 Header (TextBlock)。
[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);
}
}
XAML 这个 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中定义:
<local:ExtendedTextBox x:Name="extendedTextBox" Grid.Row="1" Style="{StaticResource ExtendedTextBoxStyle}" Text="Some Text!!" PreText="Pre Text :" />
现在你应该如何更新模板 child 值(我给的名字是 extendedTextBox
):
this.extendedTextBox.PreText = "I'm clicked";