WPF GetTemplateChild 获取 Nullrefrence 异常
WPF GetTemplateChild get Nullrefrence exception
您好,我正在构建一个控件,我需要在后面的代码中访问复选框,但是我收到空错误
这是我的控件
<Style TargetType="TreeViewItem" x:Key="CheckTreeViewItem" >
<Setter Property="IsExpanded" Value="{Binding Path=IsExpanded,Mode=TwoWay}"/>
</Style>
<Style TargetType="local:CheckTreeView">
<Setter Property="ItemContainerStyle" Value="{StaticResource CheckTreeViewItem}"/>
<Setter Property="ItemTemplate">
<Setter.Value>
<HierarchicalDataTemplate DataType="{x:Type local:CheckTreeSource}" ItemsSource="{Binding Children}">
<CheckBox x:Name="PART_CheckBox" Margin="1" IsChecked="{Binding IsChecked}">
<TextBlock Text="{Binding Text}"/>
</CheckBox>
</HierarchicalDataTemplate>
</Setter.Value>
</Setter>
</Style>
这就是我访问控制的方式
[TemplatePart(Name = CheckBox_Key, Type = typeof(CheckBox))]
public partial class CheckTreeView : TreeView
{
private const string CheckBox_Key = "PART_CheckBox";
CheckBox checkBox;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
checkBox = GetTemplateChild(CheckBox_Key) as CheckBox;
checkBox.Click += CheckBox_Click;
}
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
}
}
当我使用以下代码时,我没有收到空错误,但在运行时没有控制
static CheckTreeView()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CheckTreeView),
new FrameworkPropertyMetadata(typeof(CheckTreeView)));
}
CheckBox_Key
元素不是 CheckTreeView.ControlTemplate
的一部分。它将多次成为树视图节点的一部分。 GetTemplateChild
找不到它
我想说的是 CheckTreeView 的 ItemTemplate 中的 CheckBox intead,您需要更改 TreeViewItem.Template
并在那里添加 CheckBox。它对 GetTemplateChild 没有帮助,但它是使用复选框构建可重用 TreeView 的更合乎逻辑的方法。
这是一个例子。 Check/Uncheck 操作可以通过 CheckTreeView 中的特殊命令处理 class:
public class CheckTreeView: TreeView
{
public CheckTreeView()
{
CheckCommand = new RelayCommand<object>(o => MessageBox.Show(o?.ToString()));
}
public ICommand CheckCommand
{
get { return (ICommand)GetValue(CheckCommandProperty); }
set { SetValue(CheckCommandProperty, value); }
}
public static readonly DependencyProperty CheckCommandProperty =
DependencyProperty.Register("CheckCommand", typeof(ICommand), typeof(CheckTreeView), new PropertyMetadata(null));
}
TreeViewItem 模板的相关部分(使用 WPF 设计器中的 Edit template
功能获取完整模板):
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TreeViewItem}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="19" Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ToggleButton x:Name="Expander" ClickMode="Press" IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource ExpandCollapseToggleStyle}"/>
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.Column="1" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
<StackPanel Orientation="Horizontal">
<CheckBox Command="{Binding CheckCommand, RelativeSource={RelativeSource AncestorType={x:Type local:CheckTreeView}}}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}"
Margin="0,0,4,0"/>
<ContentPresenter x:Name="PART_Header" ContentSource="Header" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</StackPanel>
</Border>
<ItemsPresenter x:Name="ItemsHost" Grid.ColumnSpan="2" Grid.Column="1" Grid.Row="1"/>
</Grid>
</ControlTemplate/>
</Setter.Value>
</Setter>
</Style>
订阅各个CheckBox的点击事件:
public partial class CheckTreeView : TreeView
{
public CheckTreeView()
{
InitializeComponent();
processNode(this);
}
void processNode(ItemsControl node)
{
node.ItemContainerGenerator.StatusChanged += (sender, args) =>
{
ItemContainerGenerator itemContainerGenerator = ((ItemContainerGenerator)sender);
if (itemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
for (int i = 0; i < itemContainerGenerator.Items.Count; i++)
{
TreeViewItem treeViewItem =
(TreeViewItem) itemContainerGenerator.ContainerFromIndex(i);
treeViewItem.Loaded += (o, eventArgs) =>
{
CheckBox checkBox = FindVisualChild<CheckBox>(treeViewItem);
checkBox.Click += CheckBox_Click;
};
processNode(treeViewItem);
}
}
};
}
public static T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
{
if (obj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
var child = VisualTreeHelper.GetChild(obj, i);
if (child is T)
{
return (T)child;
}
T childItem = FindVisualChild<T>(child);
if (childItem != null) return childItem;
}
}
return null;
}
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
}
}
请注意,如果您使用 ObservableCollection 作为项目源,您应该处理该集合中的更改:订阅添加项目的事件,取消订阅删除项目的事件。
您好,我正在构建一个控件,我需要在后面的代码中访问复选框,但是我收到空错误
这是我的控件
<Style TargetType="TreeViewItem" x:Key="CheckTreeViewItem" >
<Setter Property="IsExpanded" Value="{Binding Path=IsExpanded,Mode=TwoWay}"/>
</Style>
<Style TargetType="local:CheckTreeView">
<Setter Property="ItemContainerStyle" Value="{StaticResource CheckTreeViewItem}"/>
<Setter Property="ItemTemplate">
<Setter.Value>
<HierarchicalDataTemplate DataType="{x:Type local:CheckTreeSource}" ItemsSource="{Binding Children}">
<CheckBox x:Name="PART_CheckBox" Margin="1" IsChecked="{Binding IsChecked}">
<TextBlock Text="{Binding Text}"/>
</CheckBox>
</HierarchicalDataTemplate>
</Setter.Value>
</Setter>
</Style>
这就是我访问控制的方式
[TemplatePart(Name = CheckBox_Key, Type = typeof(CheckBox))]
public partial class CheckTreeView : TreeView
{
private const string CheckBox_Key = "PART_CheckBox";
CheckBox checkBox;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
checkBox = GetTemplateChild(CheckBox_Key) as CheckBox;
checkBox.Click += CheckBox_Click;
}
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
}
}
当我使用以下代码时,我没有收到空错误,但在运行时没有控制
static CheckTreeView()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CheckTreeView),
new FrameworkPropertyMetadata(typeof(CheckTreeView)));
}
CheckBox_Key
元素不是 CheckTreeView.ControlTemplate
的一部分。它将多次成为树视图节点的一部分。 GetTemplateChild
找不到它
我想说的是 CheckTreeView 的 ItemTemplate 中的 CheckBox intead,您需要更改 TreeViewItem.Template
并在那里添加 CheckBox。它对 GetTemplateChild 没有帮助,但它是使用复选框构建可重用 TreeView 的更合乎逻辑的方法。
这是一个例子。 Check/Uncheck 操作可以通过 CheckTreeView 中的特殊命令处理 class:
public class CheckTreeView: TreeView
{
public CheckTreeView()
{
CheckCommand = new RelayCommand<object>(o => MessageBox.Show(o?.ToString()));
}
public ICommand CheckCommand
{
get { return (ICommand)GetValue(CheckCommandProperty); }
set { SetValue(CheckCommandProperty, value); }
}
public static readonly DependencyProperty CheckCommandProperty =
DependencyProperty.Register("CheckCommand", typeof(ICommand), typeof(CheckTreeView), new PropertyMetadata(null));
}
TreeViewItem 模板的相关部分(使用 WPF 设计器中的 Edit template
功能获取完整模板):
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TreeViewItem}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="19" Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ToggleButton x:Name="Expander" ClickMode="Press" IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource ExpandCollapseToggleStyle}"/>
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.Column="1" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
<StackPanel Orientation="Horizontal">
<CheckBox Command="{Binding CheckCommand, RelativeSource={RelativeSource AncestorType={x:Type local:CheckTreeView}}}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}"
Margin="0,0,4,0"/>
<ContentPresenter x:Name="PART_Header" ContentSource="Header" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</StackPanel>
</Border>
<ItemsPresenter x:Name="ItemsHost" Grid.ColumnSpan="2" Grid.Column="1" Grid.Row="1"/>
</Grid>
</ControlTemplate/>
</Setter.Value>
</Setter>
</Style>
订阅各个CheckBox的点击事件:
public partial class CheckTreeView : TreeView
{
public CheckTreeView()
{
InitializeComponent();
processNode(this);
}
void processNode(ItemsControl node)
{
node.ItemContainerGenerator.StatusChanged += (sender, args) =>
{
ItemContainerGenerator itemContainerGenerator = ((ItemContainerGenerator)sender);
if (itemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
for (int i = 0; i < itemContainerGenerator.Items.Count; i++)
{
TreeViewItem treeViewItem =
(TreeViewItem) itemContainerGenerator.ContainerFromIndex(i);
treeViewItem.Loaded += (o, eventArgs) =>
{
CheckBox checkBox = FindVisualChild<CheckBox>(treeViewItem);
checkBox.Click += CheckBox_Click;
};
processNode(treeViewItem);
}
}
};
}
public static T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
{
if (obj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
var child = VisualTreeHelper.GetChild(obj, i);
if (child is T)
{
return (T)child;
}
T childItem = FindVisualChild<T>(child);
if (childItem != null) return childItem;
}
}
return null;
}
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
}
}
请注意,如果您使用 ObservableCollection 作为项目源,您应该处理该集合中的更改:订阅添加项目的事件,取消订阅删除项目的事件。