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()); }
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()); }