ListBox和Popup不能一起工作

时间:2016-03-15 20:42:20

标签: wpf listbox popup caliburn.micro

我使用Caliburn.Micro作为WPF应用程序。

我正在使用的一项功能包含弹出控件中的视图,让我选择设置。

但我得出的结论是,这是一种我无法解决的行为。

在我点击按钮后面的代码中出现一个弹出窗口,当我点击屏幕上的其他地方时,它会按预期消失。

当我点击列表中的某个项目时,弹出窗口会出现,但如果我点击窗口上的其他位置,则不会消失。 (但如果我点击其他地方,就像其他应用程序一样)

基本上,在处理选定的项目事件时,我无法创建具有StaysOpen = false行为的PopUp。我该怎么做才能解决这个问题?

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication1"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <ListBox Name="ListBox" Width="250" HorizontalAlignment="Left" SelectionChanged="ListBox_SelectionChanged">

        </ListBox>

        <Button Name="Button" HorizontalAlignment="Right" Content="ShowPopUp" Click="Button_Click" />
    </Grid>
</Window>

和背后的代码

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            this.ListBox.ItemsSource = new System.Collections.ObjectModel.ObservableCollection<MainWindowItem>()
            {
                new MainWindowItem {Name = "Test 001" },
                new MainWindowItem {Name = "Test 002" },
                new MainWindowItem {Name = "Test 003" },

            };
        }

        private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            var selection = this.ListBox.SelectedItem as MainWindowItem;
            if (selection == null)
            {
                return;
            }

            var popUp = new System.Windows.Controls.Primitives.Popup();
            popUp.Child = new TextBox { Text = selection.Name , MinWidth = 200 , MinHeight = 32};
            popUp.StaysOpen = false;
            popUp.AllowsTransparency = true;
            popUp.PlacementTarget = sender as FrameworkElement;
            popUp.IsOpen = true;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {

            var popUp = new System.Windows.Controls.Primitives.Popup();
            popUp.Child = new TextBox { Text = "This is button Clicked" , MinWidth = 200, MinHeight = 32 };
            popUp.StaysOpen = false;
            popUp.AllowsTransparency = true;
            popUp.PlacementTarget = sender as FrameworkElement;
            popUp.IsOpen = true;
        }
    }

    public class MainWindowItem
    {
        public string Name { get; set; }

        public string Value { get; set; }
    }
}

1 个答案:

答案 0 :(得分:1)

嗨,请尝试下一个解决方案。它基于鼠标按下事件挂钩(from here),每次发生时都会折叠弹出窗口(并重置列表项目选择)。

代码

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow
{
    public static readonly DependencyProperty SelectedProperty = DependencyProperty.Register(
        "Selected", typeof (object), typeof (MainWindow), new PropertyMetadata(default(object)));
    //property to monitor selection
    public object Selected
    {
        get { return (object) GetValue(SelectedProperty); }
        set { SetValue(SelectedProperty, value); }
    }

    private Popup _popUp;

    public MainWindow()
    {
        InitializeComponent();
        _popUp = new Popup {IsOpen = false};

        ListBox.ItemsSource = new System.Collections.ObjectModel.ObservableCollection<MainWindowItem>()
        {
            new MainWindowItem {Name = "Test 001" },
            new MainWindowItem {Name = "Test 002" },
            new MainWindowItem {Name = "Test 003" },
        };

        Loaded += OnLoaded;
        Unloaded += OnUnloaded;
    }

    //will stop hookig here
    private void OnUnloaded(object sender, RoutedEventArgs routedEventArgs)
    {
        Unloaded -= OnUnloaded;
        Loaded -= OnLoaded;
        MouseHook.MouseAction -= MouseHookOnMouseAction;
        MouseHook.Stop();
    }

    //will collapse the popup and reset selection
    private void MouseHookOnMouseAction(object sender, EventArgs eventArgs)
    {
        Selected = null;
        _popUp.IsOpen = false;
    }

    //will start hookig here
    private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
    {
        MouseHook.MouseAction += MouseHookOnMouseAction;
        MouseHook.Start();
    }

    //here is your logic without any changes
    private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        var selection = ListBox.SelectedItem as MainWindowItem;
        if (selection == null)
        {
            return;
        }



        _popUp.Child = new TextBox { Text = selection.Name, MinWidth = 200, MinHeight = 32 };
        _popUp.StaysOpen = false;
        _popUp.AllowsTransparency = true;
        _popUp.PlacementTarget = sender as FrameworkElement;
        _popUp.IsOpen = true;
    }

    //here is your logic without any changes
    private void Button_Click(object sender, RoutedEventArgs e)
    {
        _popUp.IsOpen = false;
        _popUp.Child = new TextBox { Text = "This is button Clicked", MinWidth = 200, MinHeight = 32 };
        _popUp.StaysOpen = false;
        _popUp.AllowsTransparency = true;
        _popUp.PlacementTarget = sender as FrameworkElement;
        _popUp.IsOpen = true;
    }
}

public class MainWindowItem
{
    public string Name { get; set; }

    public string Value { get; set; }
}

/// <summary>
/// Mouse hooking helper
/// </summary>
public static class MouseHook
{
    public static event EventHandler MouseAction = delegate { };

    /// <summary>
    /// Starts hooking
    /// </summary>
    public static void Start()
    {
        _hookID = SetHook(_proc);
    }

    /// <summary>
    /// Stops hooking
    /// </summary>
    public static void Stop()
    {
        UnhookWindowsHookEx(_hookID);
    }

    private static LowLevelMouseProc _proc = HookCallback;
    private static IntPtr _hookID = IntPtr.Zero;

    //actually set a hook
    private static IntPtr SetHook(LowLevelMouseProc proc)
    {
        using (Process curProcess = Process.GetCurrentProcess())
        using (ProcessModule curModule = curProcess.MainModule)
        {
            IntPtr hook = SetWindowsHookEx(WH_MOUSE_LL, proc, GetModuleHandle("user32"), 0);
            if (hook == IntPtr.Zero) throw new System.ComponentModel.Win32Exception();
            return hook;
        }
    }

    private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);

    //is the mouse hook callback
    private static IntPtr HookCallback(
      int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode >= 0 && MouseMessages.WM_LBUTTONDOWN == (MouseMessages)wParam)
        {
            MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
            MouseAction(null, new EventArgs());
        }
        return CallNextHookEx(_hookID, nCode, wParam, lParam);
    }

    private const int WH_MOUSE_LL = 14;

    private enum MouseMessages
    {
        WM_LBUTTONDOWN = 0x0201,
        WM_LBUTTONUP = 0x0202,
        WM_MOUSEMOVE = 0x0200,
        WM_MOUSEWHEEL = 0x020A,
        WM_RBUTTONDOWN = 0x0204,
        WM_RBUTTONUP = 0x0205
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct POINT
    {
        public int x;
        public int y;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct MSLLHOOKSTRUCT
    {
        public POINT pt;
        public uint mouseData;
        public uint flags;
        public uint time;
        public IntPtr dwExtraInfo;
    }

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr SetWindowsHookEx(int idHook,
      LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool UnhookWindowsHookEx(IntPtr hhk);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
      IntPtr wParam, IntPtr lParam);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr GetModuleHandle(string lpModuleName);
}

XAML代码

<Window x:Class="WPFPopupStrangeIssueSOHelpAttempt.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" x:Name="This">
<Grid>
    <ListBox Name="ListBox" Width="250" HorizontalAlignment="Left" 
             SelectedItem="{Binding ElementName=This, Path=Selected, UpdateSourceTrigger=PropertyChanged}" 
             SelectionChanged="ListBox_SelectionChanged">
    </ListBox>

    <Button Name="Button" HorizontalAlignment="Right" Content="ShowPopUp" Click="Button_Click" />
</Grid></Window>

如果您需要任何解释,请告诉我。

问候。