Подтвердить что ты не робот

Определите, было ли инициировано событие Selector.SelectionChanged пользователем

Можно ли определить, было ли событие Selector.SelectionChanged инициировано пользователем или программно?

т.е. Мне нужно что-то вроде логического свойства "IsUserInitiated", которое истинно, только если событие SelectionChanged было поднято, потому что пользователь изменил выделение с помощью мыши или клавиатуры.

4b9b3361

Ответ 1

Это должно работать в большинстве сценариев:

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

Простая работа:

Вы можете создать метод, который временно отключает событие SelectionChanged и вызывает его, когда вам необходимо программно изменить выбор.

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

    // other work ...

    myDataGrid.SelectionChanged += myDataGrid_SelectionChanged;
}

Ответ 3

Это проблема, с которой мне пришлось работать с WinForms. Я надеялся, что в WPF они добавят логическое значение SelectionChangedEventArgs, называемое как IsUserInitiated, как указано в вопросе. Обычно мне это нужно, когда я хочу игнорировать все, что происходит, когда данные загружаются и привязываются к экрану. Например, скажем, что я дефолтовал поле, основанное на новом значении в SelectionChanged, но я хочу, чтобы пользователь мог перезаписать это значение по умолчанию, и я хочу, чтобы пользователь перезаписывал его, а не приложение, когда перезагрузка экрана. Я все еще чувствую, что то, что я делаю, является взломанным, но я опубликую его, потому что я не вижу в нем упоминания. Никаких причудливых трюков, просто и эффективно.

1) Создайте класс boolean с именем _loading

private bool _loading;

2) Обновите логическое значение в методе, выполняющем загрузку

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

3) Используйте boolean, когда вам нужно

    private void SetDefaultValue(object sender, SelectionChangedEventArgs e)
    {
        if (!_loading) {
            //set a default value
        }
    }

Ответ 4

Взято из http://social.msdn.microsoft.com, где пользователь отправляет тот же вопрос

Я не думаю, что мы можем отличить, было ли событие SelectionChanged инициировано пользователем или программно. Событие SelectionChanged не волнует.

Как правило, вы всегда можете узнать, запускается ли он программно, потому что это ваш код, который его инициирует.

Если вы используете DataBinding для привязки SelectedItem, вы можете установить для свойств NotifyOnSourceUpdated и NotifyOnTargetUpdated значение True. И вы можете обрабатывать события Binding.SourceUpdated и Binding.TargetUpdated. В большинстве случаев изменение, инициированное пользовательскими вводами, происходит от Target to Source. Если изменение инициируется программно, оно переходит от источника к цели.

Я не знаю, может ли это помочь...

Ответ 5

Вы можете использовать настраиваемое маршрутизируемое событие и подключить соответствующие обработчики таким образом:

    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. Я также не знаю, работает ли это во всех случаях. Но, возможно, это дает вам отправную точку.

Ответ 6

Почему вы хотите знать? Я закодировал много диалогов, где у меня были подобные ситуации - я действительно не хотел знать, что пользователь использовал мышь или клавиатуру, но мне действительно нужно определенное поведение, и я действительно хотел, чтобы эффекты вызывали некоторую привязку, чтобы вести себя правильно.

В большинстве случаев я обнаружил, что с использованием шаблона MVVM или, по крайней мере, отделяя логику от ui, вы часто избегаете этих проблем.

Итак, для вашей проблемы я попытался бы исключить обработчик selectionchanged и использовать только привязки - поэтому ваше состояние gui основано на модели, а не на проводке событий.

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

Ответ 7

Вы можете проверить на AddItems и RemovedItems. Если он был инициирован пользователем, у обоих свойств есть элемент. Если элемент был добавлен только через код, список RemovedItems должен быть пустым. Так

if (e.AddedItems.Count > 0 & e.RemovedItems.Count > 0)       // Инициировано пользователем