模板部件不可作为视觉子项使用。当自定义控件可见性默认折叠时

Template Part Not Available As Visual Child. When Custom Control Visibility is Collpased By Default

我在默认情况下折叠的用户控件中使用 sdl/Multiselect-ComboBox。按下按钮时,UserControl Visibility 设置为可见,因此所有内容都应加载到 Visual Tree 中。如果可见性设置为可见,则按此顺序调用存储库控件。

1) 静态 MultiSelectComboBox()

2) public 覆盖 void OnApplyTemplate()

3) 依赖属性回调。

当事件按此顺序进行时。一切都按预期工作。

但是当可见性设置为默认折叠时。 Dependency 属性 在 OnApplyTemplate 之前调用,这是可以理解的。问题是在回调加载所有属性并绑定所需的所有事件之后。

private static void ItemsPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
    LoadComponents(dependencyObject, dependencyPropertyChangedEventArgs);
}

private static void LoadComponents(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
    var control = dependencyObject as MultiSelectComboBox;

    if (control?.MultiSelectComboBoxGrid != null && control?.ItemsSource != null)
    {
        control.ItemsCollectionViewSource = new CollectionViewSource
        {
            Source = control.ItemsSource
        };

        if (dependencyPropertyChangedEventArgs.NewValue is IList newItems && newItems.Count > 0)
        {
            control.UpdateSelectedItemsContainer(newItems);
        }

        if (control.SelectedItemsControl == null)
        {
            control.SelectedItemsControl = VisualTreeService.FindVisualChild<ItemsControl>(control.MultiSelectComboBoxGrid, PART_MultiSelectComboBox_SelectedItemsPanel_ItemsControl);
        }



        if (control.DropdownListBox == null)
        {
            if (control.DropdownMenu == null)
            {
                control.DropdownMenu = VisualTreeService.FindVisualChild<Popup>(control.MultiSelectComboBoxGrid, PART_MultiSelectComboBox_Dropdown);
            }

            if (control.DropdownMenu != null)
            {
                control.DetachedFilterPanel = VisualTreeService.FindVisualChild<StackPanel>(control.DropdownMenu.Child, PART_MultiSelectComboBox_Detached_FilterPanel);
                if (control.DetachedFilterPanel != null)
                {
                    control.DetachedFilterTextBox = VisualTreeService.FindVisualChild<TextBox>(control.DetachedFilterPanel, PART_MultiSelectComboBox_Detached_Filter_TextBox);
                    control.DetachedFilterAutoCompleteTextBox = VisualTreeService.FindVisualChild<TextBox>(control.DetachedFilterPanel, PART_MultiSelectComboBox_Detached_Filter_AutoComplete_TextBox);
                }
                control.DropdownListBox = VisualTreeService.FindVisualChild<ListBox>(control.DropdownMenu.Child, PART_MultiSelectComboBox_Dropdown_ListBox);
            }
        }
    }
}

在我的场景中,这是在 OnApplyTemplate 之前调用的,所以我创建了一个名为 LoadComponent Function 的函数,并从 OnApplyTemplate 调用它。一切正常,除了以下总是给出 null 并且当我检查 Visual 树时它甚至不存在

if (control.SelectedItemsControl == null)
{
    control.SelectedItemsControl = VisualTreeService.FindVisualChild<ItemsControl>(control.MultiSelectComboBoxGrid, PART_MultiSelectComboBox_SelectedItemsPanel_ItemsControl);
}

我无法理解这背后的原因。这是这部分的Xaml

<DataTemplate x:Key="MultiSelectComboBox.SelectedItemsPanel.Template">
    <Border Background="White" BorderBrush="{StaticResource MultiSelectComboBox.SelectedItemsPanel.Border}" BorderThickness="1">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="20"/>
            </Grid.ColumnDefinitions>

            <Border Grid.Column="0" Margin="1" Background="White" BorderThickness="0">
                <sdl:ContentItemsControl x:Name="PART_MultiSelectComboBox_SelectedItemsPanel_ItemsControl"                            
                        ItemContainerStyle="{StaticResource MultiSelectComboBox.SelectedItems.ItemContainer.Style}"                           
                        Style="{StaticResource MultiSelectComboBox.SelectedItemsPanel.WrapableItemsSource.Style}">
                    <sdl:ContentItemsControl.Resources>
                        <DataTemplate x:Key="MultiSelectComboBox.SelectedItems.ItemTemplate">
                            <TextBlock Text="{Binding}" Style="{DynamicResource MultiSelectComboBox.DefaultTextBlock.Style}" Margin="2,0" />
                        </DataTemplate>
                        <DataTemplate x:Key="MultiSelectComboBox.SelectedItems.Searchable.ItemTemplate">
                            <StackPanel Orientation="Horizontal">
                                <TextBox x:Name="PART_MultiSelectComboBox_SelectedItemsPanel_Filter_TextBox" IsEnabled="{Binding IsEditMode, RelativeSource={RelativeSource TemplatedParent}}" 
                                     Focusable="True" IsTabStop="False" Margin="0" ForceCursor="True" BorderThickness="0" BorderBrush="Transparent" Padding="1" TextWrapping="NoWrap"/>
                                <TextBox x:Name="PART_MultiSelectComboBox_SelectedItemsPanel_Filter_AutoComplete_TextBox" IsReadOnly="True"
                                     Focusable="False" IsTabStop="False" Margin="-2,0,0,0" BorderThickness="0" BorderBrush="Transparent" Padding="-3,1,0,1" TextWrapping="NoWrap"/>
                            </StackPanel>
                        </DataTemplate>
                    </sdl:ContentItemsControl.Resources>
                </sdl:ContentItemsControl>
            </Border>

            <Button x:Name="PART_MultiSelectComboBox_Dropdown_Button" Grid.Column="1" Style="{StaticResource MultiSelectComboBox.DropDown.Button.Style}" >
                <ContentControl>
                    <ContentControl.Style>
                        <Style TargetType="ContentControl">
                            <Setter Property="IsTabStop" Value="False"/>
                            <Setter Property="ContentTemplate" Value="{StaticResource MultiSelectComboBox.SelectedItemsPanel.Readonly.Glyph.Template}"/>
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type sdl:MultiSelectComboBox}}, Path=IsEditMode}" Value="True">
                                    <Setter Property="ContentTemplate" Value="{StaticResource MultiSelectComboBox.SelectedItemsPanel.EditMode.Glyph.Template}"/>
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </ContentControl.Style>
                </ContentControl>

            </Button>
        </Grid>
    </Border>
</DataTemplate>

<Style x:Key="MultiSelectComboBox.Custom.Style" TargetType="{x:Type sdl:MultiSelectComboBox}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type sdl:MultiSelectComboBox}">

                <Grid x:Name="PART_MultiSelectComboBox" MaxHeight="{TemplateBinding ActualHeight}" MaxWidth="{TemplateBinding ActualWidth}" Style="{StaticResource MultiSelectComboBox.Style}">
                    <ToggleButton KeyboardNavigation.TabNavigation="None" IsChecked="{Binding Path=IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" IsTabStop="False">
                        <ToggleButton.Template>
                            <ControlTemplate TargetType="ToggleButton">
                                <ContentControl ContentTemplate="{StaticResource MultiSelectComboBox.SelectedItemsPanel.Template}"/>
                            </ControlTemplate>
                        </ToggleButton.Template>
                    </ToggleButton>

                    <Popup x:Name="PART_MultiSelectComboBox_Dropdown" Margin="2" Grid.ColumnSpan="2" MaxHeight="{Binding Path=MaxDropDownHeight, RelativeSource={RelativeSource TemplatedParent} }" PlacementTarget="{Binding ElementName=PART_MultiSelectComboBox}" IsOpen="{Binding Path=IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" MaxWidth="{TemplateBinding ActualWidth}" Placement="Bottom" AllowsTransparency="True" PopupAnimation="Slide">
                        <themes:SystemDropShadowChrome x:Name="shadow" Color="Transparent" MinWidth="{Binding ActualWidth, ElementName=PART_MultiSelectComboBox}">
                            <Border x:Name="dropDownBorder" Margin="0,1,0,0" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="0" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
                                <Grid>
                                    <Grid.RowDefinitions>
                                        <RowDefinition Height="Auto"/>
                                        <RowDefinition Height="*"/>
                                    </Grid.RowDefinitions>
                                    <Grid Grid.Row="0" Visibility="{Binding DetachAutoCompleteFilterBox, RelativeSource={RelativeSource TemplatedParent},Converter={StaticResource BoolToVis},FallbackValue=Collpased}">
                                        <Border BorderBrush="LightBlue" BorderThickness="1">
                                            <Border.Effect>
                                                <DropShadowEffect ShadowDepth="0" Color="LightBlue" Opacity="1" BlurRadius="10"/>
                                            </Border.Effect>
                                        </Border>
                                        <Border Padding="2">
                                            <StackPanel x:Name="PART_MultiSelectComboBox_Detached_FilterPanel"  Orientation="Horizontal" Height="30">
                                                <TextBox x:Name="PART_MultiSelectComboBox_Detached_Filter_TextBox" Margin="1,0,0,0" IsEnabled="{Binding IsEditMode, RelativeSource={RelativeSource TemplatedParent}}" 
                                     Focusable="True" IsTabStop="False"  ForceCursor="True" BorderThickness="0" BorderBrush="Transparent" Padding="1" TextWrapping="NoWrap"/>
                                                <TextBox x:Name="PART_MultiSelectComboBox_Detached_Filter_AutoComplete_TextBox" IsReadOnly="True"
                                     Focusable="False" IsTabStop="False" Margin="-2,0,0,0" BorderThickness="0" BorderBrush="Transparent" Padding="-3,1,0,1" TextWrapping="NoWrap"/>
                                            </StackPanel>
                                        </Border>
                                    </Grid>
                                    <Grid x:Name="grid" Grid.Row="1" RenderOptions.ClearTypeHint="Enabled" 
                                          Visibility="{Binding Path=IsLoadingSuggestions, 
                                          RelativeSource={RelativeSource Mode=TemplatedParent}, 
                                          Converter={StaticResource ResourceKey=InvBoolToVis}}"
                                          >
                                        <Grid  Visibility="{Binding Path=IsLoadingSuggestions, 
                                               RelativeSource={RelativeSource Mode=TemplatedParent}, 
                                               Converter={StaticResource ResourceKey=InvBoolToVis}}">
                                            <Canvas x:Name="canvas" HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
                                                <Rectangle x:Name="opaqueRect" Fill="{Binding Background, ElementName=dropDownBorder}" Height="{Binding ActualHeight, ElementName=dropDownBorder}" Width="{Binding ActualWidth, ElementName=dropDownBorder}"/>
                                            </Canvas>
                                            <sdl:ExtendedListBox x:Name="PART_MultiSelectComboBox_Dropdown_ListBox" Style="{StaticResource MultiSelectComboBox.Dropdown.ListBox.Style}">
                                                <sdl:ExtendedListBox.GroupStyle>
                                                    <GroupStyle HeaderTemplate="{StaticResource MultiSelectComboBox.Dropdown.ListBox.Header.Template}"/>
                                                </sdl:ExtendedListBox.GroupStyle>
                                                <sdl:ExtendedListBox.Resources>
                                                    <DataTemplate x:Key="MultiSelectComboBox.Dropdown.ListBox.ItemTemplate">
                                                        <TextBlock Text="{Binding}" Style="{StaticResource MultiSelectComboBox.DefaultTextBlock.Style}" Margin="2,2,2,3"/>
                                                    </DataTemplate>
                                                </sdl:ExtendedListBox.Resources>
                                            </sdl:ExtendedListBox>
                                        </Grid>
                                        <ContentPresenter ContentSource="LoadingSuggestionsContent" 
                                                          Visibility="{Binding Path=IsLoadingSuggestions, 
                                                          RelativeSource={RelativeSource Mode=TemplatedParent}, 
                                                          Converter={StaticResource ResourceKey=BoolToVis}}"/>
                                    </Grid>
                                </Grid>
                            </Border>
                        </themes:SystemDropShadowChrome>
                    </Popup>
                </Grid>

            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

我浪费了一天的时间试图寻找可能的原因,但无法使它正常工作。

我没有检查代码,但我在行为属性方面遇到了类似的问题。

也许你应该在 属性 回调中做类似的事情:

private static void ItemsPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
    var control = dependencyObject as MultiSelectComboBox;
    if(control != null && control.MultiSelectComboBoxGrid == null)
    {
        control.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, (Action)(() => LoadComponents(dependencyObject, dependencyPropertyChangedEventArgs)));
        return;
    }
    LoadComponents(dependencyObject, dependencyPropertyChangedEventArgs);
}

我不确定检查 MultiSelectComboBoxGrid 是否最好,但它应该有效。