首页 > 解决方案 > WPF PlacementTarget 用于用户控件文本框上的弹出窗口

问题描述

我想在我的窗口中每个文本框的底部有一个弹出显示,因为它们是集中的。用户将看到在该文本框中输入的最后几个条目。我希望放置在当前聚焦的文本框的底部。

这是我的文本框用户控件:

<UserControl x:Class="PopupPlacement.MyControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <StackPanel>
        <TextBox Name="TextBox_MyControl" Text="enter your text here" Height="25" Width="200"/>
    </StackPanel>
</UserControl>

这是我的窗口:

<Window x:Class="PopupPlacement.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:PopupPlacement"
        Title="MainWindow" Height="450" Width="800">
    <Canvas>
        <Grid ShowGridLines="False">
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>

            <Label Content="Domain" Margin="10"/>
            <local:MyControl Grid.Column="1" x:Name="Domain" Margin="10"/>
            <Label Grid.Row="1" Content="Username" Margin="10"/>
            <local:MyControl Grid.Row="1" Grid.Column="1" x:Name="Username" Margin="10"/>
            <Label Grid.Row="2" Content="Password" Margin="10"/>
            <local:MyControl Grid.Row="2" Grid.Column="1" x:Name="Password" Margin="10"/>
            <Button Grid.Row="3" Content="OK" Margin="10" Name="Button_OK"/>                                        
            <Button Grid.Row="3" Grid.Column="1" Content="Cancel" Margin="10"/>
            <Popup PlacementTarget="{Binding ElementName=TextBox_MyControl}" Placement="Bottom"
                   IsOpen="{Binding ElementName=TextBox_MyControl, Path=IsKeyboardFocused}">
                <ComboBox IsDropDownOpen="True">
                    <ComboBoxItem IsSelected="True">Item 1</ComboBoxItem>
                    <ComboBoxItem>Item 2</ComboBoxItem>
                </ComboBox>
            </Popup>
        </Grid>

    </Canvas>
</Window>

感谢任何指针。

标签: c#wpfxamlpopupplacement

解决方案


对我来说,类似要求的最佳解决方案是编写一个模仿 Intellisense的行为。

我手头没有任何简单的代码,但您可以创建并显示一个ListBox放置PopupAssociatedObject底部的内部。然后,您可以将TextBox-related 条目绑定到Behaviorvia a DependencyProperty

当然,它还有很多其他功能,例如关闭Popup、重新使用现有控件、处理按键以访问ListBox、将所选值插入TextBox等。

这是一个简单的(未经测试的)草图。

public class IntellisenseBehavior : Behavior<TextBox>
{
        public IEnumerable ItemsSource
        {
            get => (IEnumerable)GetValue(ItemsSourceProperty);
            set => SetValue(ItemsSourceProperty, value);
        }

        public static readonly DependencyProperty ItemsSourceProperty =
            DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(IntellisenseBehavior), new UIPropertyMetadata(null));

        protected override void OnAttached()
        {
            base.OnAttached();
            AssociatedObject.GotKeyboardFocus += AssociatedObjectOnGotKeyboardFocus;
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();
            AssociatedObject.GotKeyboardFocus -= AssociatedObjectOnGotKeyboardFocus;

            //cleanup
        }

        private void AssociatedObjectOnGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
        {
            var popup = new Popup
            {
                ClipToBounds = false,
                AllowsTransparency = true,
                PopupAnimation = PopupAnimation.Fade,
                HorizontalAlignment = HorizontalAlignment.Left
            };

            popup.SetValue(FocusManager.IsFocusScopeProperty, true);
            popup.Placement = PlacementMode.Bottom;
            popup.PlacementTarget = AssociatedObject;

            var shadow = new SystemDropShadowChrome { Color = Colors.Transparent, MaxHeight = 200, Margin = new Thickness(0, 0, 5, 5) };
            var listBox = new ListBox
            {
                ItemsSource = ItemsSource
            }

            ((IAddChild)shadow).AddChild(listBox);
            ((IAddChild)popup).AddChild(shadow);

            popup.IsOpen = true;
        }
}

将它附加到您需要具有此功能的所有TextBoxes,例如使用转换器来获取您需要的过滤条目。

<!-- Uses converter's public const string NameBox = "NameBox"; for filtering. -->
<TextBox>
   <i:Interaction.Behaviors>
      <IntellisenseBehavior ItemsSource="{Binding LastEntries, Converter={StaticResource FilterEntriesConverter}, ConverterParameter={x:Static FilterEntriesConverter.NameBox}}" />
   </i:Interaction.Behaviors>
</TextBox>

希望有帮助。


推荐阅读