0

I have a child view-model which notifies on a property change:

public class ChildViewModel : INotifyPropertyChanged {
    private bool isSelected;
    public bool IsSelected {
        get => isSelected;
        set {
            if (isSelected == value) { return; }
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("IsSelected"));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

and a parent view-model which creates instances of the child view-model, and listens to the property change notification:

public class ParentViewModel {
    public readonly List<ChildViewModel> Children = 
        Enumerable.Range(1,5)
            .Select(x => {
                var ret = new ChildViewModel();
                ret.PropertyChanged += (s, e) => {
                    if (e.PropertyName == "IsSelected") {
                        SyncSelection();
                    }
                };
                return ret;
            })

    private void SyncSelection() { /.../ }
}

However, I feel something is wrong with this design, because if I bind a WPF Listbox to the Children property, and the IsSelected of each item container to ChildViewModel.IsSelected:

<ListBox ItemsSource="{Binding Children}" SelectionMode="Multiple" >
    <ListBox.ItemContainerStyle>
        <Style TargetType="ListBoxItem">
            <Setter Property="IsSelected" Value="{Binding IsSelected}" />
        </Style>
    </ListBox.ItemContainerStyle>
</ListBox>

then when the user selects multiple items at once (e.g. using SHIFT and extended mode selection), SyncSelection will be called multiple times, once for each change of selection. In that case I would only want SyncSelected to be called a single time.

Is there a better design I could use here?

(For reference, a screenshot of the UI which this is supposed to enable:)

enter image description here

0

Delay the Event.

When an event source is firing faster and updating more often than desired, you can slow it down.

In the listener you will need to buffer the event/events you receive and start a timer. Each time an event is received the timer needs to be reset.

This can lead to a problem where the events keep streaming in. Imagine a user desperately trying to get feed back that their mouse movement was noticed. Naturally you don't want the user to buy a replacement mouse. To fix this you will need two timers.

  • The first timer has a short delay, and is reset each time an event is received.
  • The second timer has a longer delay. If it isn't currently running the next event will set it. If it is running that second timer is left alone.

Whichever timer expires first, the event buffer is swapped for a new one, and both timers are cancelled. The old buffer is then processed.

  1. The old event buffer is analysed to suppress duplicate, old, cancelling events.
    • this may cause some new events to be synthesised as either the result of a merge, or to keep something logically consistent.
  2. The remaining events are processed in the order they were received.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.