开发者

TreeView inside AutoCompleteBox's popup

Inspired by this post, I'm trying to use a TreeView inside the AutoCompleteBox's popup in order to show hierarchical data.

Here's what i did:

  1. I Made my own TreeView that implemen开发者_如何学运维ts ISelectionAdapter (not sure it's perfect)

  2. Edit AutoCompleteBox template and change the selector to my treeview

  3. Try Bind my ViewModel to it

Unfortunately this is not working. i checked my new TreeView with the same binding to the same object and it works prefectly when it's outside of a AutoCompleteBox template.

Any idea? any special way to implement ISelectionAdapter for treeview? did i missed something?

couldn't find any example for it on the web...

Thanks.


I managed to write it eventually.

Made a custom control for it. it still need to be refined, but it works good:

Generic.xaml :

<ControlTemplate x:Key="CommonValidationToolTipTemplate" TargetType="ToolTip">
    <Grid x:Name="Root" Margin="5,0" Opacity="0" RenderTransformOrigin="0,0">
        <Grid.RenderTransform>
            <TranslateTransform x:Name="Translation" X="-25"/>
        </Grid.RenderTransform>
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="OpenStates">
                <VisualStateGroup.Transitions>
                    <VisualTransition GeneratedDuration="0"/>
                    <VisualTransition GeneratedDuration="0:0:0.2" To="Open">
                        <Storyboard>
                            <DoubleAnimation Duration="0:0:0.2" To="0" Storyboard.TargetProperty="X" Storyboard.TargetName="Translation">
                                <DoubleAnimation.EasingFunction>
                                    <BackEase Amplitude=".3" EasingMode="EaseOut"/>
                                </DoubleAnimation.EasingFunction>
                            </DoubleAnimation>
                            <DoubleAnimation Duration="0:0:0.2" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="Root"/>
                        </Storyboard>
                    </VisualTransition>
                </VisualStateGroup.Transitions>
                <VisualState x:Name="Closed">
                    <Storyboard>
                        <DoubleAnimation Duration="0" To="0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="Root"/>
                    </Storyboard>
                </VisualState>
                <VisualState x:Name="Open">
                    <Storyboard>
                        <DoubleAnimation Duration="0" To="0" Storyboard.TargetProperty="X" Storyboard.TargetName="Translation"/>
                        <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="Root"/>
                    </Storyboard>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
        <Border Background="#052A2E31" CornerRadius="5" Margin="4,4,-4,-4"/>
        <Border Background="#152A2E31" CornerRadius="4" Margin="3,3,-3,-3"/>
        <Border Background="#252A2E31" CornerRadius="3" Margin="2,2,-2,-2"/>
        <Border Background="#352A2E31" CornerRadius="2" Margin="1,1,-1,-1"/>
        <Border Background="#FFDC000C" CornerRadius="2">
            <TextBlock Foreground="White" MaxWidth="250" Margin="8,4,8,4" TextWrapping="Wrap" Text="{Binding (Validation.Errors)[0].ErrorContent}" UseLayoutRounding="false"/>
        </Border>
    </Grid>
</ControlTemplate>
<Style TargetType="local:AutoCompleteTreeView"
       xmlns:System="clr-namespace:System;assembly=mscorlib">
    <Setter Property="IsTabStop" Value="False"/>
    <Setter Property="Padding" Value="2"/>
    <Setter Property="BorderThickness" Value="1"/>
    <Setter Property="BorderBrush">
        <Setter.Value>
            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                <GradientStop Color="#FFA3AEB9" Offset="0"/>
                <GradientStop Color="#FF8399A9" Offset="0.375"/>
                <GradientStop Color="#FF718597" Offset="0.375"/>
                <GradientStop Color="#FF617584" Offset="1"/>
            </LinearGradientBrush>
        </Setter.Value>
    </Setter>
    <Setter Property="Background" Value="#FFFFFFFF"/>
    <Setter Property="Foreground" Value="#FF000000"/>
    <Setter Property="MinWidth" Value="45"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:AutoCompleteTreeView">
                <Grid Opacity="{TemplateBinding Opacity}">
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="PopupStates">
                            <VisualStateGroup.Transitions>
                                <VisualTransition GeneratedDuration="0:0:0.1" To="PopupOpened"/>
                                <VisualTransition GeneratedDuration="0:0:0.2" To="PopupClosed"/>
                            </VisualStateGroup.Transitions>
                            <VisualState x:Name="PopupOpened">
                                <Storyboard>
                                    <DoubleAnimation To="1.0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="PopupBorder"/>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="PopupClosed">
                                <Storyboard>
                                    <DoubleAnimation To="0.0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="PopupBorder"/>
                                </Storyboard>
                            </VisualState>
                        </VisualStateGroup>
                        <VisualStateGroup x:Name="ValidationStates">
                            <VisualState x:Name="Valid"/>
                            <VisualState x:Name="InvalidUnfocused">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="ValidationErrorElement">
                                        <DiscreteObjectKeyFrame KeyTime="0">
                                            <DiscreteObjectKeyFrame.Value>
                                                <Visibility>Visible</Visibility>
                                            </DiscreteObjectKeyFrame.Value>
                                        </DiscreteObjectKeyFrame>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="InvalidFocused">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="ValidationErrorElement">
                                        <DiscreteObjectKeyFrame KeyTime="0">
                                            <DiscreteObjectKeyFrame.Value>
                                                <Visibility>Visible</Visibility>
                                            </DiscreteObjectKeyFrame.Value>
                                        </DiscreteObjectKeyFrame>
                                    </ObjectAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="IsOpen" Storyboard.TargetName="validationTooltip">
                                        <DiscreteObjectKeyFrame KeyTime="0">
                                            <DiscreteObjectKeyFrame.Value>
                                                <System:Boolean>True</System:Boolean>
                                            </DiscreteObjectKeyFrame.Value>
                                        </DiscreteObjectKeyFrame>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                    <TextBox x:Name="Text" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Foreground="{TemplateBinding Foreground}" IsTabStop="True" Margin="0" Padding="{TemplateBinding Padding}" Style="{TemplateBinding TextBoxStyle}"/>
                    <Border x:Name="ValidationErrorElement" BorderBrush="#FFDB000C" BorderThickness="1" CornerRadius="1" Visibility="Collapsed">
                        <ToolTipService.ToolTip>
                            <ToolTip x:Name="validationTooltip" DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}" Placement="Right" PlacementTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}" Template="{StaticResource CommonValidationToolTipTemplate}">
                                <ToolTip.Triggers>
                                    <EventTrigger RoutedEvent="Canvas.Loaded">
                                        <BeginStoryboard>
                                            <Storyboard>
                                                <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="IsHitTestVisible" Storyboard.TargetName="validationTooltip">
                                                    <DiscreteObjectKeyFrame KeyTime="0">
                                                        <DiscreteObjectKeyFrame.Value>
                                                            <System:Boolean>true</System:Boolean>
                                                        </DiscreteObjectKeyFrame.Value>
                                                    </DiscreteObjectKeyFrame>
                                                </ObjectAnimationUsingKeyFrames>
                                            </Storyboard>
                                        </BeginStoryboard>
                                    </EventTrigger>
                                </ToolTip.Triggers>
                            </ToolTip>
                        </ToolTipService.ToolTip>
                        <Grid Background="Transparent" HorizontalAlignment="Right" Height="12" Margin="1,-4,-4,0" VerticalAlignment="Top" Width="12">
                            <Path Data="M 1,0 L6,0 A 2,2 90 0 1 8,2 L8,7 z" Fill="#FFDC000C" Margin="1,3,0,0"/>
                            <Path Data="M 0,0 L2,0 L 8,6 L8,8" Fill="#ffffff" Margin="1,3,0,0"/>
                        </Grid>
                    </Border>
                    <Popup x:Name="Popup">
                        <Grid Opacity="{TemplateBinding Opacity}">
                            <Border x:Name="PopupBorder" BorderThickness="0" Background="#11000000" HorizontalAlignment="Stretch" Opacity="0">
                                <Border.RenderTransform>
                                    <TranslateTransform X="1" Y="1"/>
                                </Border.RenderTransform>
                                <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="0" HorizontalAlignment="Stretch" Opacity="1.0" Padding="0">
                                    <Border.Background>
                                        <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                            <GradientStop Color="#FFDDDDDD" Offset="0"/>
                                            <GradientStop Color="#AADDDDDD" Offset="1"/>
                                        </LinearGradientBrush>
                                    </Border.Background>
                                    <Border.RenderTransform>
                                        <TransformGroup>
                                            <TranslateTransform X="-1" Y="-1"/>
                                        </TransformGroup>
                                    </Border.RenderTransform>
                                    <local:TreeViewSelectionAdapter x:Name="SelectionAdapter" BorderThickness="0" Background="{TemplateBinding Background}" Foreground="{TemplateBinding Foreground}" ScrollViewer.HorizontalScrollBarVisibility="Auto" ItemTemplate="{TemplateBinding ItemTemplate}" ItemContainerStyle="{TemplateBinding ItemContainerStyle}" ScrollViewer.VerticalScrollBarVisibility="Auto"/>
                                </Border>  
                            </Border>  
                        </Grid>
                    </Popup>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

AutoCompleteTreeView.cs:

public class AutoCompleteTreeView : AutoCompleteBox
{
    public AutoCompleteTreeView()
    {
        DefaultStyleKey = typeof(AutoCompleteTreeView);
    }
}

TreeViewSelectionAdapter.cs:

public class TreeViewSelectionAdapter : TreeView, ISelectionAdapter
{
    private object lastSelectedItem = null;

    /// <summary>
    ///This prevents the text box of the AutoCompleteBox control from being updated continuously.
    /// </summary>
    private bool IgnoreAnySelection { get; set; }

    /// <summary>
    /// An event that indicates that a selection is complete and has been
    /// made, effectively a commit action.
    /// </summary>
    public event RoutedEventHandler Commit;

    /// <summary>
    /// An event that indicates that the selection operation has been
    /// canceled.
    /// </summary>
    public event RoutedEventHandler Cancel;

    /// <summary>
    /// Initializes a new instance of the SelectorSelectionAdapter class.
    /// </summary>
    public TreeViewSelectionAdapter()
    {
        base.SelectedItemChanged += new RoutedPropertyChangedEventHandler<object>(OnSelectionChanged);
        MouseLeftButtonUp += OnSelectorMouseLeftButtonUp;

    }

    void OnSelectionChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
    {

        if (IgnoreAnySelection)
        {
            return;
        }

        SelectionChangedEventHandler handler = this.SelectionChanged;
        if (handler != null)
        {
            IList oldSelectedItem = new List<object>();
            if (lastSelectedItem != null)
                oldSelectedItem.Add(lastSelectedItem);

            if (SelectionChanged != null)
                handler(this, new SelectionChangedEventArgs(oldSelectedItem, new List<object> { this.SelectedItem }));

            lastSelectedItem = this.SelectedItem;

        }
    }

    public new object SelectedItem
    {
        get
        {
            return base.SelectedItem;
        }

        set
        {
            this.SelectItem(value);
        }
    }

    /// <summary>
    /// Handles the mouse left button up event on the selector control.
    /// </summary>
    private void OnSelectorMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        IgnoreAnySelection = false;

        OnSelectionChanged(this, null);
        OnCommit(this, new RoutedEventArgs());
    }


    public event SelectionChangedEventHandler SelectionChanged;



    /// <summary>
    /// Gets or sets the items source.
    /// </summary>
    public new IEnumerable ItemsSource
    {
        get { return base.ItemsSource; }

        set
        {
            if (base.ItemsSource != null)
            {
                INotifyCollectionChanged notify = base.ItemsSource as INotifyCollectionChanged;
                if (notify != null)
                {
                    notify.CollectionChanged -= OnCollectionChanged;
                }
            }

            base.ItemsSource = value;

            if (base.ItemsSource != null)
            {
                INotifyCollectionChanged notify = base.ItemsSource as INotifyCollectionChanged;
                if (notify != null)
                {
                    notify.CollectionChanged += OnCollectionChanged;
                }
            }
        }
    }

    /// <summary>
    /// Handles the CollectionChanged event, resetting the selection
    /// ignore flag.
    /// </summary>
    private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        IgnoreAnySelection = true;
    }


    /// <summary>
    /// Process a key down event.
    /// </summary>
    public void HandleKeyDown(KeyEventArgs e)
    {
        switch (e.Key)
        {
            case Key.Enter:
                IgnoreAnySelection = false;
                OnCommit(this, e);
                e.Handled = true;
                break;


            case Key.Down:
                if ((ModifierKeys.Alt & Keyboard.Modifiers) == ModifierKeys.None)
                {
                    IgnoreAnySelection = true;
                    //SelectedIndexIncrement();
                    TreeView tv = this.FindName("SelectionAdapter") as TreeView;

                    tv.KeyDown += new KeyEventHandler(tv_KeyDown);
                    tv.Focus();

                    List<TreeViewItem> ls = new List<TreeViewItem>(tv.GetContainers());
                    ls[0].Focus();
                    e.Handled = true;

                }
                break;

            case Key.Escape:
                OnCancel(this, e);
                e.Handled = true;
                break;

            default:
                break;
        }
    }

    void tv_KeyDown(object sender, KeyEventArgs e)
    {
        switch (e.Key)
        {
            case Key.Enter:

                IgnoreAnySelection = false;

                OnSelectionChanged(this, null);
                OnCommit(this, new RoutedEventArgs());
                break;
        }
    }



    /// <summary>
    /// Fires the Commit event.
    /// </summary>
    private void OnCommit(object sender, RoutedEventArgs e)
    {
        RoutedEventHandler handler = Commit;
        if (handler != null)
        {
            handler(sender, e);
        }

        AfterAdapterAction();
    }

    /// <summary>
    /// Fires the Cancel event.
    /// </summary>
    private void OnCancel(object sender, RoutedEventArgs e)
    {
        RoutedEventHandler handler = Cancel;
        if (handler != null)
        {
            handler(sender, e);
        }

        AfterAdapterAction();
    }

    /// <summary>
    /// Change the selection after the actions are complete.
    /// </summary>
    private void AfterAdapterAction()
    {
        this.SetSelectedContainer(null);
    }

    /// <summary>
    /// Initializes a new instance of a DataGridAutomationPeer.
    /// </summary>
    public AutomationPeer CreateAutomationPeer()
    {
        return new TreeViewAutomationPeer(this);
    }
}

Using the control:

<ccontrols:AutoCompleteTreeView  x:Name="textbox" MinimumPrefixLength="1" IsEnabled="True" ItemsSource="{Binding MyNodes}"  >
        <ccontrols:AutoCompleteTreeView.ItemTemplate>
            <hirarch:HierarchicalDataTemplate ItemsSource="{Binding MyNodes}" >
                <TextBlock Text="{Binding Name}" />
            </hirarch:HierarchicalDataTemplate>
        </ccontrols:AutoCompleteTreeView.ItemTemplate>
    </ccontrols:AutoCompleteTreeView>

The hierarchy objects:

public class Node
{
    public string Name { get; set; }
    public List<Node> MyNodes { get; set; }

    public override string ToString()
    {
        return Name;
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜