【问题标题】:ListBox Not Selecting the Selected Item列表框未选择所选项目
【发布时间】:2013-01-23 05:36:26
【问题描述】:

见下面的代码
视觉工作室 2010
ListBox 上方有一个 TextBox。
通过绑定文本框可以在选择项目时变大或变小。
这会导致 ListBox 移动。
当 ListBox 移动时,选中的项目不是被点击的项目。
选中的项目是移动的 ListBox 上鼠标下的项目。
有时它甚至根本不会选择(尝试从 9 到 10 或从 10 到 9)。
在此代码中重现问题的偶数和奇数产生不同的长度。
因此,如果您从奇数变为奇数,甚至从偶数变为偶数,那么没问题。
如果您从顶部的奇数到底部的偶数(不滚动),则有时会选择一个不在视图中的项目。
在实际代码中,TextBox 是对项目的描述,描述的长度不同。
有趣的是在调试中,在 get { return boundText; 上有一个断点; } 然后它会选择正确的项目。
我认为它会处理选择,然后测量 UI,然后在新 UI 上再次处理选择。
由于它在调试中的行为不同,因此很难弄清楚。

<Window x:Class="ListBoxMissClick.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"
        DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <TextBox Grid.Row="0" Grid.Column="0" Text="{Binding Path=BoundText}" TextWrapping="Wrap" />
        <ListBox Grid.Row="1" Grid.Column="0" ItemsSource="{Binding Path=BoundList}" SelectedItem="{Binding Path=BoundListSelected, Mode=TwoWay}"/>
    </Grid>
</Window>

using System.ComponentModel;

namespace ListBoxMissClick
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        private string boundListSelected;
        private string boundText = string.Empty;
        private List<string> boundList = new List<string>();
        private bool shortLong = true;
        public event PropertyChangedEventHandler PropertyChanged;

        protected void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }

        public MainWindow()
        {
            for (int i = 0; i < 1000; i++)
            {
                boundList.Add(i.ToString());
            }

            InitializeComponent();

        }
        public string BoundText 
        { 
            get { return boundText; }
            set 
            { 
                if (boundText != value)
                {
                    boundText = value;
                    NotifyPropertyChanged("BoundText");
                }
            }
        }
        public List<string> BoundList { get { return boundList; } }
        public string BoundListSelected
        {
            get { return boundListSelected; }
            set
            {
                boundListSelected = value;
                if (Int32.Parse(value) % 2 == 0)
                {
                    BoundText = value.ToString() + " something very long something very long something very long something very long something very long something very long something very long something very long something very long something very longsomething very long something very long something very long something very long something very longsomething very long something very long something very long something very long something very longsomething very long something very long something very long something very long something very longsomething very long something very long something very long something very long something very long";
                }
                else
                {
                    BoundText = value.ToString() + " something short "; 
                }
             }
        }

        private void ListBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            BoundText = " something very long something very long something very long something very long something very long something very long something very long something very long something very long something very longsomething very long something very long something very long something very long something very longsomething very long something very long something very long something very long something very longsomething very long something very long something very long something very long something very longsomething very long something very long something very long something very long something very long";
        }
    }
}

除了接受的答案 Mouse.Capture 和 ReleaseMouseCapture 工作。

set
{
    Mouse.Capture(this);
    {
        boundListSelected = value;
        if (Int32.Parse(value) % 2 == 0)
        {
            BoundText = value.ToString() + " something very long something very long something very long something very long something very long something very long something very long something very long something very long something very longsomething very long something very long something very long something very long something very longsomething very long something very long something very long something very long something very longsomething very long something very long something very long something very long something very longsomething very long something very long something very long something very long something very long";
        }
        else
        {
            BoundText = value.ToString() + " something short ";
        }
    }
    ReleaseMouseCapture();
}

【问题讨论】:

  • 呵呵,是的,如果你只是按住鼠标真的很有趣。因此,布局已更新,鼠标下方的 ListBoxItem 现在显示“耶!鼠标已按下,我被选中了!”当有断点时它起作用的唯一原因是它让您有机会在循环继续之前让鼠标按钮向上。不幸的是,我不知道如何解决它 - 我尝试捕获鼠标事件并设置一个标志以强制停止进一步的选择,直到鼠标上升,但我无法让它工作。祝你好运!

标签: .net wpf listbox


【解决方案1】:

我已经稍微改写了您的代码。诀窍是使用 MouseCapture 来避免处理多个事件(使用您的原始代码,由于在按下鼠标按钮时布局发生了变化,因此 listBox 一次单击最多可以选择三个)

代码如下:

MainWindow.xaml

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        x:Class="TextEditor.MainWindow"
        Title="MainWindow" Height="350" Width="525">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <TextBox Grid.Row="0" Text="{Binding Path=BoundText}" TextWrapping="Wrap" />
        <ListBox Grid.Row="1" 
                 ItemsSource="{Binding Path=BoundList}" 
                 SelectedItem="{Binding Path=BoundListSelected, Mode=TwoWay}"/>
    </Grid>

</Window>

MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Input;

namespace TextEditor
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public string BoundText
        {
            get { return (string)GetValue(BoundTextProperty); }
            set { SetValue(BoundTextProperty, value); }
        }

        // Using a DependencyProperty as the backing store for BoundText.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty BoundTextProperty =
            DependencyProperty.Register("BoundText", typeof(string), typeof(MainWindow), new PropertyMetadata(string.Empty));

        public string BoundListSelected
        {
            get { return (string)GetValue(BoundListSelectedProperty); }
            set { SetValue(BoundListSelectedProperty, value); }
        }

        // Using a DependencyProperty as the backing store for BoundListSelected.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty BoundListSelectedProperty =
            DependencyProperty.Register("BoundListSelected", typeof(string), typeof(MainWindow), new PropertyMetadata(string.Empty, OnBoundListSelectedChanged));

        private static void OnBoundListSelectedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var mainWindow = d as MainWindow;
            var value = e.NewValue as string;

            Mouse.Capture(mainWindow);

            if (Int32.Parse(value) % 2 == 0)
            {
                mainWindow.BoundText = value.ToString() + " something very long something very long something very long something very long something very long something very long something very long something very long something very long something very longsomething very long something very long something very long something very long something very longsomething very long something very long something very long something very long something very longsomething very long something very long something very long something very long something very longsomething very long something very long something very long something very long something very long";
            }
            else
            {
                mainWindow.BoundText = value.ToString() + " something short ";
            }

            mainWindow.ReleaseMouseCapture();
        }

        public MainWindow()
        {
            for (int i = 0; i < 1000; i++)
            {
                boundList.Add(i.ToString());
            }

            InitializeComponent();
            DataContext = this;
        }

        public List<string> BoundList { get { return boundList; } }
        private List<string> boundList = new List<string>();
    }
}

编辑:我实际上改变了 MainWindow 的编码方式(没有必要在 DependencyObject 上实现 INotifyPropertyChanged,所以我只是删除了它并设置了两个依赖属性),但您可以尝试解决您的问题您的原始代码只需在设置 BoundText 之前捕获鼠标,然后释放它。

【讨论】:

  • +1 工作。如果在接下来的一个小时内没有更短的时间出现,会给你一个检查。我能够捕获鼠标,并且确实修复了第一个选择。但我不知道如何释放鼠标。
  • 很高兴我能帮上忙。释放鼠标是通过调用之前捕获它的 UIElement 上的 ReleaseMouseCapture() 方法完成的:)
  • Mouse.Capture 和 ReleaseMouseCapture 有效,是我采用的解决方案,因为代码更改较少,而且我不熟悉 DependencyProperty。您是否认为该解决方案存在任何风险。再次感谢。
  • 完全没有风险。 MouseCapture 只会重定向特定 UIElement 上的所有鼠标事件,直到您释放它。
  • 我知道这已经很长时间了,但是我遇到了集合和类来自单独的类文件的情况。该设备无权使用鼠标。你知道我怎样才能让这种情况发挥作用吗?
【解决方案2】:

您可以在BoundListSelected setter 中添加Thread.Sleep 来解决您的问题,但我认为在这种情况下更好的解决方案是使用网格列。当你使用列时,你不需要使用Thread.Sleep

public string BoundListSelected
{
    get { return boundListSelected; }
    set
    {
        Thread.Sleep(TimeSpan.FromSeconds(.2));
        boundListSelected = value;
        if (Int32.Parse(value) % 2 == 0)
        {
            BoundText = value.ToString() + " something very long something very long something very long something very long something very long something very long something very long something very long something very long something very longsomething very long something very long something very long something very long something very longsomething very long something very long something very long something very long something very longsomething very long something very long something very long something very long something very longsomething very long something very long something very long something very long something very long";
        }
        else
        {
            BoundText = value.ToString() + " something short ";
        }
    }
}

如果你不想使用Thread.Sleep,你可以使用带有列的网格:

<Grid>
    <Grid.ColumnDefinitions>            
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <TextBox Grid.Column="0" Text="{Binding Path=BoundText}" TextWrapping="Wrap" />
    <ListBox Grid.Column="1" ItemsSource="{Binding Path=BoundList}" SelectedItem="{Binding Path=BoundListSelected, Mode=TwoWay}"/>
</Grid>

【讨论】:

  • 我尝试了列,但这对我不起作用。查看更新问题。
  • 0.2 的睡眠仍然会失败,只是不会那么频繁。 0.1 的睡眠几乎总是失败。
  • @Blam 检查我的答案,我将您的示例代码添加到带有列的网格中。
  • @Blam 所以将此值从 .2 更改为 .5。
  • 谢谢,但为了管理屏幕空间真的很想从同一列的列表中借用描述。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-06-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-05
相关资源
最近更新 更多