public SelectionDescriptorBuilder()
 {
     _builder = VMDescriptorBuilder
                .OfType <TDescriptor>()
                .For <TVM>()
                .WithProperties((d, b) => { });
 }
        public IVMPropertyDescriptor <MultiSelectionVM <TItemSource> > WithCaption(
            Func <TItemSource, string> captionGetter,
            Action <MultiSelectionWithCaptionDescriptorBuilder <TSourceObject, TItemSource> > descriptorConfigurationAction = null
            )
        {
            Check.NotNull(captionGetter, nameof(captionGetter));
            Check.Requires <InvalidOperationException>(_selectedSourceItemsPropertyFactory != null);

            SelectionItemVMDescriptor itemDescriptor = VMDescriptorBuilder
                                                       .OfType <SelectionItemVMDescriptor>()
                                                       .For <SelectionItemVM <TItemSource> >()
                                                       .WithProperties((d, c) => {
                var s = c.GetPropertyBuilder(x => x.Source);

                d.Caption = s.Property.DelegatesTo(captionGetter);
            })
                                                       .WithValidators(b => b.EnableParentViewModelValidation())
                                                       .Build();


            var allSourceItemsPropertyFactory =
                _allSourceItemsPropertyFactory ??
                CreateLocatingPropertyFactory();

            var descriptorBuilder = new MultiSelectionWithCaptionDescriptorBuilder <TSourceObject, TItemSource>(
                _filter,
                itemDescriptor
                );

            descriptorBuilder.WithProperties((d, b) => {
                var s = b.GetPropertyBuilder(x => x.Source);

                d.AllSourceItems      = allSourceItemsPropertyFactory(s);
                d.SelectedSourceItems = _selectedSourceItemsPropertyFactory(s);
            });

            if (_undoIsEnabled)
            {
                descriptorBuilder.WithViewModelBehaviors(b => {
                    b.EnableUndo();
                });
            }

            if (descriptorConfigurationAction != null)
            {
                descriptorConfigurationAction(descriptorBuilder);
            }

            var descriptor = descriptorBuilder.Build();

            var property = _sourceObjectPropertyBuilder.Custom.ViewModelProperty <MultiSelectionVM <TItemSource> >(
                valueAccessor: new MultSelectionAccessor(descriptor),
                sourceAccessor: _sourceObjectPropertyBuilder.Custom.CreateSourceObjectAccessor()
                );

            return(property);
        }
        private static SelectableItemVMDescriptor <TItemVM> CreateItemDescriptor(bool isSelectedRaisesPropertyChanged)
        {
            return(VMDescriptorBuilder
                   .OfType <SelectableItemVMDescriptor <TItemVM> >()
                   .For <SelectableItemVM <TItemSource, TItemVM> >()
                   .WithProperties((d, c) => {
                var v = c.GetPropertyBuilder();

                d.IsSelected = v.Property.Of <bool>();
                d.VM = v.VM.Wraps(x => x.Source).With <TItemVM>();
            })
                   .WithValidators(b => b.EnableParentViewModelValidation())
                   .WithBehaviors(b => {
                if (!isSelectedRaisesPropertyChanged)
                {
                    // HACK: This behavior needs to be disabled to workaround the following problem:
                    //
                    // * A 'MultiSelection' with 'SelectableItemVM' elements (configured with 'Of<TItemVM>' instead
                    //   of 'WithCaption') is bound to a ComboBox (specifically a DevExpress WPF ComboBoxEdit).
                    // * The user changes the selected items in the control.
                    // * The ComboBox sets the selected items via 'SettableListDisplayValueBehavior'.
                    // * Each collection modification in 'SettableListDisplayValueBehavior.SynchronizeCollections' causes
                    //   the 'IsSelected' property of added or removed 'SelectableItemVM' objects to change (because of
                    //   the dependency defined in the constructor of 'MultiSelectionDescriptorBuilder').
                    // * This change raises a 'PropertyChanged' event for the 'IsSelected' property.
                    // * This 'PropertyChanged' event triggers a call of 'VMCollection.OnListChanged' with 'ListChangedType.ItemChanged'.
                    // * The 'ListChanged' event that is raised in the base 'OnListChanged' method is handled by the ComboBox.
                    // * The ComboBox reacts to the 'ListChanged' event by setting the value of the MultiSelection again.
                    // * This calls the 'SettableListDisplayValueBehavior' again. Now there are two calls of its
                    //   'SynchronizeCollections' method in the call stack, one from the initial call caused by the user
                    //   interaction and one that was triggered by the 'ListChanged' event.
                    // * That second call to 'SynchronizeCollections' might again cause 'PropertyChanged' and 'ListChanged' events.
                    // * After the code returns to the first call of 'SynchronizeCollections' and tries to modify the
                    //   next element in the collection, the code in 'SynchronizerCollectionBehavior' notices that the
                    //   collections are not in sync any more since the second call to 'SettableListDisplayValueBehavior.SynchronizeCollections'
                    //   modified the same collection that the first one was still modifying. This causes the
                    //   'SynchronizerCollectionBehavior' to throw an InvalidOperationException
                    //   (error message "The VM collection is not in sync with its source collection anymore.").
                    //
                    // To prevent this, the behavior that causes the 'PropertyChanged' event of the 'IsSelected' property
                    // to be raised is disabled here.
                    // But this is only ok if the 'MultiSelection' is used in a ComboBox. If it's used in a 'ListBox'
                    // where the control directly modifies the 'IsSelected' properties (instead of setting the whole
                    // 'SelectedItems' collection) the 'PropertyChanged' event is required to synchronize the changes
                    // to the collections.
                    b.Property(x => x.IsSelected).Disable(PropertyBehaviorKeys.ChangeNotifier);
                }
            })
                   .Build());
        }
Ejemplo n.º 4
0
        private static SelectableItemVMDescriptor <TItemVM> CreateItemDescriptor()
        {
            return(VMDescriptorBuilder
                   .OfType <SelectableItemVMDescriptor <TItemVM> >()
                   .For <SelectableItemVM <TItemSource, TItemVM> >()
                   .WithProperties((d, c) => {
                var v = c.GetPropertyBuilder();

                d.IsSelected = v.Property.Of <bool>();
                d.VM = v.VM.Wraps(x => x.Source).With <TItemVM>();
            })
                   .WithValidators(b => b.EnableParentViewModelValidation())
                   .Build());
        }