2

For my WPF TreeView user control, I would like to display a progress ring around a ToggleButton to indicate that the node's children are populating, like so:

Image

(Full disclosure, I saw this type of behavior in some open source software, I just can't remember where). I still want the toggle button to be interactive (i.e., get mouse events, like hover and click) so the user can collapse the node if they no longer wish to wait for the update to complete.

My first thought was to add the progress ring as an adorner layer to the ToggleButton. However, since the WPF adorner layer lies on top of the UI element in Z-order, I cannot seem to interact with the toggle button below the progress ring. Is there a way to draw the toggle button on top of the adorner layer? Or does an adorner layer not seem like the right approach in this case? Would it be better to just trigger off a property to control the template of the toggle button and draw both the progress ring and the toggle button when updating the children? Thanks for any thoughts!

Relevant tutorials I am leveraging off:

Working with Checkboxes in the WPF TreeView

WPF Loading Wait Adorner (which has links to progress ring and adorner sources)

4

2 回答 2

2

您可以使用网格在扩展器(切换按钮)后面放置动画。Grid 将按 z 顺序将元素堆叠在同一单元格中。动画可能始终存在,只是被隐藏,由您定义的某些触发器显示。

于 2015-09-01T07:43:03.210 回答
1

我相信你不需要装饰者来完成你的愿望。我已经通过 Blend 编辑了树视图模板,将带有繁忙动画的用户控件添加到树视图项目样式的扩展器按钮。有很多工作要做,但它可以向您展示方法。也可能有更好的方法来更改指示器的可见性,而不是在可视化树中搜索它。

我看到它的方式是为 treeview 和 treeviewitem 创建自定义控件。覆盖它的样式并为 treeviewitem 添加一个“IsLoading”属性。切换此属性应更改用户控件的可见性。

在此处输入图像描述

主窗口.xaml

<Window x:Class="WpfApplication17.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525" xmlns:local="clr-namespace:WpfApplication17">
    <Window.Resources>

        <Style x:Key="TreeViewItemFocusVisual">
            <Setter Property="Control.Template">
                <Setter.Value>
                    <ControlTemplate>
                        <Rectangle/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Checked.Fill" Color="#FF595959"/>
        <SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Checked.Stroke" Color="#FF262626"/>
        <SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Stroke" Color="#FF1BBBFA"/>
        <SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Fill" Color="Transparent"/>
        <SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Checked.Stroke" Color="#FF262626"/>
        <SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Checked.Fill" Color="#FF595959"/>
        <PathGeometry x:Key="TreeArrow" Figures="M0,0 L0,6 L6,0 z"/>
        <SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Fill" Color="Transparent"/>
        <SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Stroke" Color="#FF989898"/>
        <Style x:Key="ExpandCollapseToggleStyle" TargetType="{x:Type ToggleButton}">
            <Setter Property="Focusable" Value="False"/>
            <Setter Property="Width" Value="16"/>
            <Setter Property="Height" Value="16"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ToggleButton}">
                        <Grid HorizontalAlignment="Left" Height="16" VerticalAlignment="Top" Width="16">
                            <Border Background="Transparent" Height="16" Padding="5,5,5,5" Width="16">
                                <Path x:Name="ExpandPath" Data="{StaticResource TreeArrow}" Fill="{StaticResource TreeViewItem.TreeArrow.Static.Fill}" Stroke="{StaticResource TreeViewItem.TreeArrow.Static.Stroke}">
                                    <Path.RenderTransform>
                                        <RotateTransform Angle="135" CenterY="3" CenterX="3"/>
                                    </Path.RenderTransform>
                                </Path>
                            </Border>
                            <local:UserControl1 Name="busyInd"/>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsChecked" Value="True">
                                <Setter Property="RenderTransform" TargetName="ExpandPath">
                                    <Setter.Value>
                                        <RotateTransform Angle="180" CenterY="3" CenterX="3"/>
                                    </Setter.Value>
                                </Setter>
                                <Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.Static.Checked.Fill}"/>
                                <Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.Static.Checked.Stroke}"/>
                            </Trigger>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Stroke}"/>
                                <Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Fill}"/>
                            </Trigger>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="IsMouseOver" Value="True"/>
                                    <Condition Property="IsChecked" Value="True"/>
                                </MultiTrigger.Conditions>
                                <Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Checked.Stroke}"/>
                                <Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Checked.Fill}"/>
                            </MultiTrigger>
                        </ControlTemplate.Triggers>

                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <Style x:Key="TreeViewItemStyle1" TargetType="{x:Type TreeViewItem}">
            <Setter Property="Background" Value="Transparent"/>
            <Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
            <Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
            <Setter Property="Padding" Value="1,0,0,0"/>
            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
            <Setter Property="FocusVisualStyle" Value="{StaticResource TreeViewItemFocusVisual}"/>
            <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">
                                <ContentPresenter x:Name="PART_Header" ContentSource="Header" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                            </Border>
                            <ItemsPresenter x:Name="ItemsHost" Grid.ColumnSpan="2" Grid.Column="1" Grid.Row="1"/>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsExpanded" Value="false">
                                <Setter Property="Visibility" TargetName="ItemsHost" Value="Collapsed"/>
                            </Trigger>
                            <Trigger Property="HasItems" Value="false">
                                <Setter Property="Visibility" TargetName="Expander" Value="Hidden"/>
                            </Trigger>
                            <Trigger Property="IsSelected" Value="true">
                                <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
                                <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
                            </Trigger>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="IsSelected" Value="true"/>
                                    <Condition Property="IsSelectionActive" Value="false"/>
                                </MultiTrigger.Conditions>
                                <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.InactiveSelectionHighlightBrushKey}}"/>
                                <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}}"/>
                            </MultiTrigger>
                            <Trigger Property="IsEnabled" Value="false">
                                <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Style.Triggers>
                <Trigger Property="VirtualizingPanel.IsVirtualizing" Value="true">
                    <Setter Property="ItemsPanel">
                        <Setter.Value>
                            <ItemsPanelTemplate>
                                <VirtualizingStackPanel/>
                            </ItemsPanelTemplate>
                        </Setter.Value>
                    </Setter>
                </Trigger>
            </Style.Triggers>
        </Style>
        <Style x:Key="BusyInd" TargetType="{x:Type Grid}"/>
    </Window.Resources>

    <Grid>
        <TreeView Name="treeView" SelectedItemChanged="treeView_SelectedItemChanged" HorizontalAlignment="Left" Height="300" Margin="10,10,0,0" VerticalAlignment="Top" Width="497" ItemContainerStyle="{DynamicResource TreeViewItemStyle1}">

            <TreeViewItem Header="TreeViewItem" IsExpanded="True">
                <TreeViewItem Header="TreeViewItem" HorizontalAlignment="Left" Width="474"/>
                <TreeViewItem Header="TreeViewItem" HorizontalAlignment="Left" Width="474"/>
            </TreeViewItem>
            <TreeViewItem Header="TreeViewItem" IsExpanded="True">
                <TreeViewItem Header="TreeViewItem" HorizontalAlignment="Left" Width="474"/>
                <TreeViewItem Header="TreeViewItem" HorizontalAlignment="Left" Width="474"/>
            </TreeViewItem>
            <TreeViewItem Header="TreeViewItem" IsExpanded="True">
                <TreeViewItem Header="TreeViewItem" HorizontalAlignment="Left" Width="474"/>
            </TreeViewItem>
        </TreeView>

    </Grid>
</Window>

主窗口.cs

public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

        }

        private void treeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            TreeViewItem item_container = (TreeViewItem)treeView.ItemContainerGenerator.ContainerFromItem(treeView.SelectedItem);
            var indicator = FindChild<UserControl1>(item_container, "busyInd");
            indicator.Visibility = Visibility.Visible;
            Task.Factory.StartNew(() =>
            {
                Thread.Sleep(3000);
                Dispatcher.Invoke(() => { indicator.Visibility = Visibility.Collapsed; });
            });
        }

        public static T FindChild<T>(DependencyObject parent, string childName)
           where T : DependencyObject
        {
            if (parent == null) return null;

            T foundChild = null;

            int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
            for (int i = 0; i < childrenCount; i++)
            {
                var child = VisualTreeHelper.GetChild(parent, i);
                // If the child is not of the request child type child
                T childType = child as T;
                if (childType == null)
                {
                    // recursively drill down the tree
                    foundChild = FindChild<T>(child, childName);

                    // If the child is found, break so we do not overwrite the found child. 
                    if (foundChild != null) break;
                }
                else if (!string.IsNullOrEmpty(childName))
                {
                    var frameworkElement = child as FrameworkElement;
                    // If the child's name is set for search
                    if (frameworkElement != null && frameworkElement.Name == childName)
                    {
                        // if the child's name is of the request name
                        foundChild = (T)child;
                        break;
                    }
                }
                else
                {
                    // child element found.
                    foundChild = (T)child;
                    break;
                }
            }

            return foundChild;
        }
    }

UserControl1.xaml

<UserControl x:Class="WpfApplication17.UserControl1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Height="Auto" Width="Auto" Background="Transparent" Visibility="Collapsed">
    <Viewbox Width="{Binding Width, ElementName=BusyIndicator}" Height="{Binding Height, ElementName=BusyIndicator}"      
        HorizontalAlignment="Center" VerticalAlignment="Center">
        <Grid Background="Transparent" ToolTip="Searching...." HorizontalAlignment="Center" VerticalAlignment="Center">
            <Canvas Name="Canvas1"  
         RenderTransformOrigin="0.5,0.5"  
         HorizontalAlignment="Center"         
         VerticalAlignment="Center" Width="120" Height="120">
                <Canvas.RenderTransform>
                    <RotateTransform Angle="0" />
                </Canvas.RenderTransform>
                <Canvas.Style>
                    <Style TargetType="Canvas">
                        <Style.Triggers>
                            <Trigger Property="IsVisible" Value="True">
                                <Trigger.EnterActions>
                                    <BeginStoryboard Name="Storyboard_Rotate">
                                        <Storyboard RepeatBehavior="Forever">
                                            <DoubleAnimation Storyboard.TargetProperty="RenderTransform.Angle"   
                          From="0" To="360" Duration="0:0:2"/>
                                        </Storyboard>
                                    </BeginStoryboard>
                                </Trigger.EnterActions>
                                <Trigger.ExitActions>
                                    <StopStoryboard BeginStoryboardName="Storyboard_Rotate" />
                                </Trigger.ExitActions>
                            </Trigger>

                        </Style.Triggers>
                    </Style>
                </Canvas.Style>
                <Ellipse Width="20" Height="20" Stretch="Fill" Fill="Black" Opacity="1.0" Canvas.Left="50" Canvas.Top="0" />
                <Ellipse Width="20" Height="20" Stretch="Fill" Fill="Black" Opacity="0.9" Canvas.Left="20.6107373853764" Canvas.Top="9.54915028125262" />
                <Ellipse Width="20" Height="20" Stretch="Fill" Fill="Black" Opacity="0.8" Canvas.Left="2.44717418524233" Canvas.Top="34.5491502812526" />
                <Ellipse Width="20" Height="20" Stretch="Fill" Fill="Black" Opacity="0.7" Canvas.Left="2.44717418524232" Canvas.Top="65.4508497187474" />
                <Ellipse Width="20" Height="20" Stretch="Fill" Fill="Black" Opacity="0.6" Canvas.Left="20.6107373853763" Canvas.Top="90.4508497187474" />
                <Ellipse Width="20" Height="20" Stretch="Fill" Fill="Black" Opacity="0.5" Canvas.Left="50" Canvas.Top="100" />
                <Ellipse Width="20" Height="20" Stretch="Fill" Fill="Black" Opacity="0.4" Canvas.Left="79.3892626146236" Canvas.Top="90.4508497187474" />
                <Ellipse Width="20" Height="20" Stretch="Fill" Fill="Black" Opacity="0.3" Canvas.Left="97.5528258147577" Canvas.Top="65.4508497187474" />
                <Ellipse Width="20" Height="20" Stretch="Fill" Fill="Black" Opacity="0.2" Canvas.Left="97.5528258147577" Canvas.Top="34.5491502812526" />
            </Canvas>
        </Grid>
    </Viewbox>
</UserControl>
于 2015-09-01T09:41:28.963 回答