Remove selection if the selected item is removed from the list.

I have ListBox, which has its own ItemsSourceassociated with the custom class that (correctly) implement binding INotifyCollectionChangedand SelectedItemto the field in the ViewModel.

The problem is that when I delete the current one SelectedItemfrom the collection ItemsSource, it immediately changes the selection to a neighboring element. I would really like if he just deleted the selection.

The reason this problem is for me is as follows. The class ItemsSourcecontains elements from some other collection that either satisfy some (at runtime constant) Predicate, or are Active. Be Active"synchronized" with SelectedItem(there are reasons for this). Therefore, it is very possible for an item to be allowed in ListBoxonly if it has been selected, which means that it can disappear when the user selects some other one.

My function (deep in the "model") called upon change SelectedItem:

//Gets old Active item
var oldActiveSchema = Schemas.FirstOrDefault(sch => sch.IsActive);

//Makes the new Item active (which triggers adding it into `ItemsSource` in case it didn't satisfy the Predicate)
((PowerSchema)newActiveSchema).IsActive = true;
//Triggers PropertyChanged on ViewModel with the new Active item
CurrentSchema = newActiveSchema;
RaisePropertyChangedEvent(nameof(CurrentSchema)); (#1)

//Changes the old item so it stops being Active -> gets removed from `ItemsSource` (#2)
if (oldActiveSchema != null) { ((PowerSchema)oldActiveSchema).IsActive = false; }

The problem is that for some reason, the update ListBoxdue to the change SelectedItemthat should be caused (# 1) is delayed (the update message ListBoxprobably gets into WPF and waits there until the current calculation is completed).

oldActiveSchema ItemsSource, , SelectedItem , ( , ). SelectedItem , CurrentSchema () , CurrentSchema (# 1) ListBox - PropertyChanged , .

.


, - :

  • ListBox
  • ViewModel
  • Callstack, SelectedItem ,
    • 46: SelectedItem, , ,
    • 45: SelectedItem → (44-41)
    • 32: MoveCurrencyOffDeletedElement SelectedItem
    • 5: SelectedItem .
+6
1

IsSynchronizedWithCurrentItem="True" ListBox. , ListBox.SelectedItem ListBox.Items.CurrentItem. , ListBox.Items.CurrentItem ICollectionView.CurrentItem ( CollectionViewSource.GetDefaultView(Schemas) ). , Schemas, CurrentItem , CurrentItem ( , , null, ).

, ListBox.SelectedItem, , RaisePropertyChangedEvent(nameof(ActiveSchema)) , , , ActiveSchema. , , , . , CurrentItem Schemas , . , IsActive = false , "" Schemas, , , CurrentItem ListBox.SelectedItem. , ActiveSchema . , ActiveSchema ( ) , ( , ).

:

# 1

IsSynchronizedWithCurrentItem="False" ListBox ( ). . - , .

# 2

ActiveSchema :

bool ignoreActiveSchemaChanges = false;
public IPowerSchema ActiveSchema
{
    get { return pwrManager.CurrentSchema; }
    set
    {
        if (ignoreActiveSchemaChanges) return;
        if (value != null && !value.IsActive)
        {
            ignoreActiveSchemaChanges = true;
            pwrManager.SetPowerSchema(value);
            ignoreActiveSchemaChanges = false;
        }
    }
}

, CurrentItem , ActiveSchema .

# 3

CurrentItem , "" . MainWindowViewModel.Schemas, setNewCurrSchema . :

PowerManager:

//we pass the action as an optional parameter so that we don't need to update
//other code that uses this method
private void setNewCurrSchema(IPowerSchema newActiveSchema, Action action = null)
{
    var oldActiveSchema = Schemas.FirstOrDefault(sch => sch.IsActive);

    ((PowerSchema)newActiveSchema).IsActive = true;
    CurrentSchema = newActiveSchema;
    RaisePropertyChangedEvent(nameof(CurrentSchema));

    action?.Invoke();

    if (oldActiveSchema != null)
    {
        ((PowerSchema)oldActiveSchema).IsActive = false;
    }
}

MainWindowViewModel:

public IPowerSchema ActiveSchema
{
    get { return pwrManager.CurrentSchema; }
    set
    {
        if (value != null && !value.IsActive)
        {
            var action = new Action(() =>
            {
                //this will cause a reentrant attempt to set the ActiveSchema,
                //but it will be ignored because at this point value.IsActive == true
                CollectionViewSource.GetDefaultView(Schemas).MoveCurrentTo(value);
            });
            pwrManager.SetPowerSchema(value, action);
        }
    }
}

, PresentationFramework. , , , ( PresentationFramework). (. ) Prism 5.0 MSDN).

# 4

"" . Dispatcher:

private void setNewCurrSchema(IPowerSchema newActiveSchema)
{
    var oldActiveSchema = Schemas.FirstOrDefault(sch => sch.IsActive);

    ((PowerSchema)newActiveSchema).IsActive = true;
    CurrentSchema = newActiveSchema;
    RaisePropertyChangedEvent(nameof(CurrentSchema));

    if (oldActiveSchema != null)
    {
        //queue the code for execution
        //in case this code is called due to binding update the current dispatcher will be
        //the one associated with UI thread so everything should work as expected
        Dispatcher.CurrentDispatcher.InvokeAsync(() =>
        {
            ((PowerSchema)oldActiveSchema).IsActive = false;
        });
    }
}

WindowsBase, , , # 3.

# 1 # 2, PowerManager , # 3 # 4 .

+1

Source: https://habr.com/ru/post/1015631/


All Articles