确定Selector.SelectionChanged事件是否由用户启动

时间:2011-09-14 06:44:14

标签: wpf

是否可以确定用户是发起Selector.SelectionChanged事件还是以编程方式发起?

即。我需要类似布尔"IsUserInitiated"属性的东西,只有在引发SelectionChanged事件时才会生效,因为用户使用鼠标或键盘更改了选择。

8 个答案:

答案 0 :(得分:23)

简单的解决方法:

您可以创建一个临时禁用SelectionChanged事件的方法,并在需要以编程方式更改选择时调用它。

private void SelectGridRow( int SelectedIndex )
{
    myDataGrid.SelectionChanged -= myDataGrid_SelectionChanged;
    myDataGrid.SelectedIndex = SelectedIndex;

    // other work ...

    myDataGrid.SelectionChanged += myDataGrid_SelectionChanged;
}

答案 1 :(得分:15)

这应该适用于大多数情况:

private void cboStatus_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (this.cboStatus.IsDropDownOpen)
    {
        //OPTIONAL:
        //Causes the combobox selection changed to not be fired again if anything
        //in the function below changes the selection (as in my weird case)
        this.cboStatus.IsDropDownOpen = false;

        //now put the code you want to fire when a user selects an option here
    }
}

答案 2 :(得分:11)

这是我自WinForms以来必须解决的一个问题。我希望在WPF中,他们会向问题中提到的SelectionChangedEventArgs添加一个名为IsUserInitiated的布尔值。当我想在数据加载和绑定到屏幕时忽略发生的任何事情时,我最常需要这个。例如,假设我基于SelectionChanged中的新值默认字段但是我希望用户能够覆盖此默认值,我只希望用户覆盖它,而不是屏幕重新加载时的应用程序。我仍然觉得我一直在做的是hacky,但我会发布它,因为我没有看到它提到。没有花哨的技巧,只是简单而有效。

1)创建一个名为_loading

的类级别布尔值
private bool _loading;

2)更新执行加载的方法中的布尔值

private async Task Load()
{
    _loading = true;
    //load some stuff
    _loading = false;
}

3)每当需要

时使用布尔值
    private void SetDefaultValue(object sender, SelectionChangedEventArgs e)
    {
        if (!_loading) {
            //set a default value
        }
    }

答案 3 :(得分:5)

取自用户发布相同问题的http://social.msdn.microsoft.com

  

我认为我们无法区分SelectionChanged事件是由用户输入启动还是以编程方式启动。 SelectionChanged事件并不关心。

     

通常,您现在可以随时以编程方式启动它,因为它是启动它的代码。

     

如果使用DataBinding绑定SelectedItem,则可以将NotifyOnSourceUpdated和NotifyOnTargetUpdated属性设置为True。您可以处理Binding.SourceUpdated和Binding.TargetUpdated事件。在大多数情况下,用户输入启动的更改从Target到Source。如果以编程方式启动更改,则会从“源”更改为“目标”。

我不知道它是否有帮助...

答案 4 :(得分:3)

您可以使用自定义路由事件,并按照以下行为连接相应的处理程序:

    public class UserSelectionChangedEventArgs : RoutedEventArgs
    {
        public UserSelectionChangedEventArgs( RoutedEvent id, SelectionChangedEventArgs args , bool changedByUser) :base(id)
        {
            SelectionChangedByUser = changedByUser;
            RemovedItems = args.RemovedItems;
            AddedItems = args.AddedItems;
        }

        public bool SelectionChangedByUser { get; set; }
        public IList RemovedItems { get; set; }
        public IList AddedItems { get; set; }
    }
    public delegate void UserSelectionChangedEventHandler( object sender, UserSelectionChangedEventArgs e );

    public class UserSelectionChangedBehavior : Behavior<Selector>
    {
        private bool m_expectingSelectionChanged;

        public static readonly RoutedEvent UserSelectionChangedEvent = EventManager.RegisterRoutedEvent( "UserSelectionChanged", RoutingStrategy.Bubble, typeof( UserSelectionChangedEventHandler ), typeof( Selector ) );

        public static void AddUserSelectionChangedHandler( DependencyObject d, UserSelectionChangedEventHandler handler )
        {
            ( (Selector) d ).AddHandler( UserSelectionChangedEvent, handler );
        }

        public static void RemoveUserSelectionChangedHandler( DependencyObject d, UserSelectionChangedEventHandler handler )
        {
            ( (Selector) d ).RemoveHandler( UserSelectionChangedEvent, handler );
        }

        private void RaiseUserSelectionChangedEvent( UserSelectionChangedEventArgs args )
        {
            AssociatedObject.RaiseEvent( args );
        }

        protected override void OnAttached()
        {
            AssociatedObject.PreviewKeyDown += OnKeyDown;
            AssociatedObject.PreviewKeyUp += OnKeyUp;
            AssociatedObject.PreviewMouseLeftButtonDown += OnMouseLeftButtonDown;
            AssociatedObject.PreviewMouseLeftButtonUp += OnMouseLeftButtonUp;
            AssociatedObject.SelectionChanged += OnSelectionChanged;
            base.OnAttached();
        }

        protected override void OnDetaching()
        {
            AssociatedObject.PreviewKeyDown -= OnKeyDown;
            AssociatedObject.PreviewKeyUp -= OnKeyUp;
            AssociatedObject.PreviewMouseLeftButtonDown -= OnMouseLeftButtonDown;
            AssociatedObject.PreviewMouseLeftButtonUp -= OnMouseLeftButtonUp;
            AssociatedObject.SelectionChanged -= OnSelectionChanged;
            base.OnDetaching();
        }

        private void OnMouseLeftButtonUp( object sender, MouseButtonEventArgs e )
        {
            m_expectingSelectionChanged = false;
        }

        private void OnKeyDown( object sender, KeyEventArgs e )
        {
            m_expectingSelectionChanged = true;
        }

        private void OnKeyUp( object sender, KeyEventArgs e )
        {
            m_expectingSelectionChanged = false;
        }

        private void OnMouseLeftButtonDown( object sender, MouseButtonEventArgs e )
        {
            m_expectingSelectionChanged = true;
        }

        private void OnSelectionChanged( object sender, SelectionChangedEventArgs e )
        {
            RaiseUserSelectionChangedEvent( new UserSelectionChangedEventArgs( UserSelectionChangedEvent, e, m_expectingSelectionChanged ) );
        }
    }

在XAML中你可以像这样订阅UserSelectionChangedEvent:

<ListBox ItemsSource="{Binding Items}"  b:UserSelectionChangedBehavior.UserSelectionChanged="OnUserSelectionChanged">
  <i:Interaction.Behaviors>
    <b:UserSelectionChangedBehavior/>
  </i:Interaction.Behaviors>

处理程序:

private void OnUserSelectionChanged( object sender, UserSelectionChangedEventArgs e )
{
    if(e.SelectionChangedByUser)
    {
        Console.WriteLine( "Selection changed by user" );
    }
    else
    {
        Console.WriteLine( "Selection changed by code" );
    }
}

这只是一个想法。可能你甚至不需要这种行为,只需定义附加的路由事件。但后来我不知道在哪里存储m_expectingSelectionChanged标志。我也不知道这是否适用于所有情况。但也许它给你一个起点。

答案 5 :(得分:0)

你为什么想知道? 我编写了很多对话框,我有类似的情况 - 我真的不想知道用户使用鼠标或键盘,但我确实想要一个特定的行为,我确实希望通过触发一些绑定来实现正确的行为效果

对于大多数情况,我发现使用MVVM模式 - 或者至少从ui中分离逻辑 - 你经常会避免这些问题。

因此,对于您的问题,我会尝试消除selectionchanged处理程序并仅使用绑定 - 因此您的gui状态基于后面的模型而不是事件的连线。

MVVM: http://en.wikipedia.org/wiki/Model_View_ViewModel

答案 6 :(得分:0)

您可以检查AddedItems和RemovedItems。如果它是由用户启动的,则两个属性都有一个项目。如果项目刚刚通过代码添加,则RemovedItems列表应为空。所以

if(e.AddedItems.Count&gt; 0&amp;&amp; e.RemovedItems.Count&gt; 0)         //由用户发起

答案 7 :(得分:0)

当控件加载到视图中时,通常Selector设置/更改它的选择。发生这种情况时,IsLoaded属性仍为false。当用户手动进行选择时,控件显然必须是可见的,因此IsLoaded将是true。尝试使用此属性来确定更改是由用户启动还是由于正在加载控件。