projects/wms-framework/src/lib/models/controls/ItemsControlModel.ts
Properties |
Accessors |
constructor(instance: ItemsCollectionModel)
|
||||||
Creates an instance of ItemsCollectionModel_INotifyCollectionChangedWrapper.
Parameters :
|
Public instance |
Type : ItemsCollectionModel
|
isExplicitInterfaceImplementationWrapper |
Default value : true
|
Used to recognize if the class is an explicit implementation |
CollectionChanged |
getCollectionChanged()
|
Collection changed subsccription event
Returns :
SubscriptionEvent<void>
|
import { Control } from '../../basecomponentmodel/Control';
import { Dependency } from '../../basecomponentmodel/Dependency';
import { DependencyObject } from '../../basecomponentmodel/DependencyObject';
import { DependencyProperty } from '../../basecomponentmodel/DependencyProperty';
import { DependencyPropertyChangedEventArgs } from '../../basecomponentmodel/DependencyPropertyChangedEventArgs';
import { FrameworkElement } from '../../basecomponentmodel/FrameworkElement';
import { tryToConvertType } from '../../baseframework/ReflectionSupport';
import {
CollectionChangeAction,
CollectionChangeInfo,
IComparer,
IList,
INotifyCollectionChanged,
SimpleList,
} from '../../baseframework/collections';
import { ReflectionHelper } from '../../baseframework/ReflectionSupport';
import { asExplicitImplementation } from '../../decorators/AsExplicitImplementation';
import { ClassInfo } from '../../decorators/ClassInfo';
import { AngularComponentId } from '../../helpers/AngularComponentId';
import { SubscriptionEvent } from '../../utils/SubscriptionEvent';
/**
* Model class to represent Image component
*
* @export
* @class ItemsControlModel
* @extends {Control}
* @wType System.Windows.Controls.ItemsControl
* @wType Telerik.Windows.Controls.ItemsControl
*/
export class ItemsControlModel extends Control {
/**
* DisplayMemberPathProperty dependency property
*
* @static
* @type {DependencyProperty}
* @memberof ItemsControlModel
*/
static DisplayMemberPathProperty: DependencyProperty = new DependencyProperty(
'DisplayMemberPath',
null,
null
);
/**
* ItemsPanelProperty dependency property
*
* @static
* @type {DependencyProperty}
* @memberof ItemsControlModel
*/
static ItemsPanelProperty: DependencyProperty = new DependencyProperty(
'ItemsPanel',
null,
null
);
/**
* ItemsSourceProperty dependency property
*
* @static
* @type {DependencyProperty}
* @memberof ItemsControlModel
*/
static ItemsSourceProperty: DependencyProperty = new DependencyProperty(
'ItemsSource',
null,
ItemsControlModel.ItemsSourceCallback
);
/**
* ItemTemplateProperty dependency property
*
* @static
* @type {DependencyProperty}
* @memberof ItemsControlModel
*/
static ItemTemplateProperty: DependencyProperty = new DependencyProperty(
'ItemTemplate',
null,
null
);
/**
* Force sync items collection
*
* @memberof ItemsControlModel
*/
public forceSyncItemsWithItemsSource() {
if (this.ItemsSource) {
this.setItemsProperty(this.ItemsSource);
}
}
private itemContainerGenerator: any = null;
/**
* Call this method to fill the items property
*
* @private
* @param {*} collection
* @memberof ItemsControlModel
*/
private setItemsProperty(collection: any) {
this.items.clear();
if (collection) {
this.items.setWithCollection(collection);
}
}
/**
* Call this method to fill the items property
*
* @private
* @param {*} collection
* @memberof ItemsControlModel
*/
private setItemsPropertyEvent(
collection: any,
info: CollectionChangeInfo = null
): void {
if (info) {
this.propagateCollectionChanges(collection, info);
} else {
this.rebuildCollection(collection);
}
}
public items: ItemsCollectionModel;
/**
* Items source
*
* @type {Iterable<any>}
* @memberof ItemsControlModel
*/
@Dependency(ItemsControlModel.ItemsSourceProperty)
public ItemsSource: Iterable<any>;
/**
* The items panel
*
* @type {*}
* @memberof ItemsControlModel
*/
@Dependency(ItemsControlModel.ItemsPanelProperty)
public ItemsPanel: any;
/**
* The display member path
*
* @type {string}
* @memberof ItemsControlModel
*/
@Dependency(ItemsControlModel.DisplayMemberPathProperty)
public DisplayMemberPath: string;
/**
* The item template
*
* @type {*}
* @memberof ItemsControlModel
*/
@Dependency(ItemsControlModel.ItemTemplateProperty)
public ItemTemplate: any;
/**
* Identifies the angular component associated with the model
*
* @type {string}
* @memberof ItemsControlModel
*/
public AngularComponentId = AngularComponentId.itemsControl;
/**
* Rebuild collection without triggering
* reset event and restore the items
*
* @private
* @param {*} collection
* @memberof ItemsControlModel
*/
private rebuildCollection(collection: any, info: any = null) {
this.items.clearWithoutReset();
/* istanbul ignore else */
if (collection) {
this.items.setWithCollection(collection);
}
/* istanbul ignore else */
if (info) {
this.items.fire(info);
}
}
/**
* Gets or sets the item container generator
*
* @type {*}
* @memberof ItemsControlModel
*/
public get ItemContainerGenerator(): any {
return this.itemContainerGenerator;
}
public set ItemContainerGenerator(v: any) {
this.itemContainerGenerator = v;
}
/**
* Return the amount of children the parent has
*
* @return {*} {number}
* @memberof ItemsControlModel
* @wIgnore
*/
GetChildrenCount(): number {
return this.items.count;
}
/**
* Gets the child element at the index position
*
* @param {number} idx
* @return {*} {DependencyObject}
* @memberof ItemsControlModel
* @wIgnore
*/
GetChild(idx: number): DependencyObject {
return this.items.getItem(idx);
}
constructor() {
super();
this.items = new ItemsCollectionModel();
this.SetUpHierarchyListeners(this.items);
}
/**
* Subscription for the CollectionChanged event of the items source (if exists)
*
* @memberof ItemsControlModel
*/
private collectionChangedSubscription: (e: any, args: any) => void;
/**
* Reference to callbar called whenever the ItemsSource have changed.
*
* @private
* @type {INotifyCollectionChanged}
* @memberof ItemsControlModel
*/
private valueConvertRef: INotifyCollectionChanged;
/**
* Reference to subscription when the model is initilized.
*
* @private
* @type {INotifyCollectionChanged}
* @memberof ItemsControlModel
*/
private itemsCollectionListenerRef: INotifyCollectionChanged;
/**
* Called when the component is being destroyed, clear some handlers attach to this model.
*
* @type {void}
* @memberof ItemsControlModel
*/
OnDestroy(): void {
super.OnDestroy();
this.valueConvertRef?.CollectionChanged.removeHandler(
this.collectionChangedSubscription,
this
);
this.itemsCollectionListenerRef?.CollectionChanged.removeHandler(
this.ValidateInfo,
this
);
this.collectionChangedSubscription = null;
}
/**
* On ItemsSource changed handler
*
* @param {DependencyPropertyChangedEventArgs} args
* @memberof ItemsControlModel
* @wIgnore
*/
public OnItemsSourceChanged(args: DependencyPropertyChangedEventArgs) {
// deregister previous handler
if (args.OldValue && this.collectionChangedSubscription) {
this.valueConvertRef = tryToConvertType<INotifyCollectionChanged>(
args.OldValue,
ReflectionHelper.getInterfaceRuntimeTypeInfo(
'System.Collections.Specialized.INotifyCollectionChanged'
)
);
this.valueConvertRef?.CollectionChanged.removeHandler(
this.collectionChangedSubscription,
this
);
this.collectionChangedSubscription = null;
}
// adds new handler
try {
this.valueConvertRef = tryToConvertType<INotifyCollectionChanged>(
args.NewValue,
ReflectionHelper.getInterfaceRuntimeTypeInfo(
'System.Collections.Specialized.INotifyCollectionChanged'
)
);
if (this.valueConvertRef) {
this.collectionChangedSubscription =
this.valueConvertRef.CollectionChanged.addHandler(
this.setItemsPropertyEvent,
this
);
}
this.setItemsProperty(args.NewValue);
} catch (error) {
this.setItemsProperty(args.NewValue);
}
}
/**
* ItemsSource dependency property callback
*
* @static
* @param {DependencyObject} sender
* @param {DependencyPropertyChangedEventArgs} args
* @memberof ItemsControlModel
* @wIgnore
*/
public static ItemsSourceCallback(
sender: DependencyObject,
args: DependencyPropertyChangedEventArgs
) {
if (sender instanceof ItemsControlModel) {
sender.OnItemsSourceChanged(args);
}
}
/**
* Sets the listeners for the items property
*
* @param {Iterable<any>} value
* @memberof ItemsControlModel
*/
private SetUpHierarchyListeners(value: Iterable<any>) {
this.itemsCollectionListenerRef =
tryToConvertType<INotifyCollectionChanged>(
value,
ReflectionHelper.getInterfaceRuntimeTypeInfo(
'System.Collections.Specialized.INotifyCollectionChanged'
)
);
this.itemsCollectionListenerRef?.CollectionChanged.addHandler(
this.ValidateInfo,
this
);
}
/**
* Validates the listener information for the items modifed
*
* @param {CollectionChangeInfo} info
* @memberof ItemsControlModel
*/
private ValidateInfo(_: any, info: CollectionChangeInfo) {
if (info.action == CollectionChangeAction.Add) {
for (const item of info.NewItems) {
if (item instanceof FrameworkElement) {
item.Parent = this;
}
}
} else if (
(info.action == CollectionChangeAction.Reset && info.OldItems) ||
info.action == CollectionChangeAction.Remove
) {
for (const item of info.OldItems) {
if (item instanceof FrameworkElement) {
item.Parent = null;
}
}
}
}
/**
* Applies the changes in bound collection to this { ItemsConttrolModel } internal
* collection
*
* @private
* @param {CollectionChangeInfo} info the { @link CollectionChangeInfo } object containing the
* changes in bound collection
* @memberof ItemsControlModel
*/
private propagateCollectionChanges(
collection: any,
info: CollectionChangeInfo
) {
switch (info.action) {
case CollectionChangeAction.Add:
case CollectionChangeAction.Replace:
let i = info.NewStartingIndex;
for (const e of info.NewItems) {
if (info.action === CollectionChangeAction.Add) {
this.items.insert(i++, e);
} else {
this.items.setItem(i++, e);
this.items.fire(info);
}
}
break;
case CollectionChangeAction.Remove:
for (let index = 0; index < info.OldItems.count; index++) {
this.items.removeAt(info.OldStartingIndex);
}
break;
case CollectionChangeAction.Reset:
this.rebuildCollection(collection, info);
break;
}
}
}
export abstract class PresentationFrameworkCollectionModel<T>
extends DependencyObject
implements IList<T>
{
private innerCollection = new SimpleList<T>();
getItem(index: number): T {
return this.innerCollection.getItem(index);
}
setItem(index: number, value: T) {
this.innerCollection.setItem(index, value);
this.change.fire(['Count']);
}
indexOf(value: T): number {
return this.innerCollection.indexOf(value);
}
insert(index: number, value: T) {
this.innerCollection.insert(index, value);
this.change.fire(['Count']);
}
removeAt(index: number) {
this.innerCollection.removeAt(index);
this.change.fire(['Count']);
}
get count(): number {
return this.innerCollection.count;
}
sort();
sort(comparer: IComparer<T>);
sort(comparer?) {
this.innerCollection.sort(comparer);
}
add(value: T): void {
this.internalAdd(value);
this.change.fire(['Count']);
}
/**
* Add used for internal processing of added elements to collection without trigger change
*
* @param {T} value
* @memberof PresentationFrameworkCollectionModel
*/
internalAdd(value: T): void {
if (
value &&
value instanceof Object &&
value.hasOwnProperty &&
value.hasOwnProperty('Parent')
) {
// eslint-disable-next-line @typescript-eslint/dot-notation
value['Parent'] = this;
}
this.innerCollection.add(value);
}
clear(): void {
const count = this.innerCollection.count;
this.innerCollection.clear();
if (count !== this.innerCollection.count) {
this.change.fire(['Count']);
}
}
contains(value: T): boolean {
return this.innerCollection.contains(value);
}
remove(value: T): boolean {
const result = this.innerCollection.remove(value);
this.change.fire(['Count']);
return result;
}
copyTo(target: T[], index: number): void {
this.innerCollection.copyTo(target, index);
}
[Symbol.iterator](): Iterator<T, any, undefined> {
return this.innerCollection[Symbol.iterator]();
}
get internalArray(): T[] {
return this.innerCollection.internalArray;
}
}
@ClassInfo({
classId: 'ItemsCollectionModel',
implements: ['System.Collections.Specialized.INotifyCollectionChanged'],
})
/**
* Items Collection Model
*
* @export
* @class ItemsCollectionModel
* @extends {PresentationFrameworkCollectionModel<any>}
*/
export class ItemsCollectionModel extends PresentationFrameworkCollectionModel<any> {
/**
* Function that allows to do sync of the items collection in the model
*
* @memberof ItemsCollectionModel
*/
syncItems: Function;
/**
* Collection changed subsccription event
*
* @memberof ItemsCollectionModel
*/
#CollectionChanged: SubscriptionEvent<
(e: any, args: CollectionChangeInfo) => void
> = new SubscriptionEvent();
/**
* Internal access for explicit implemented property
*
* @readonly
* @memberof ItemsCollectionModel
*/
get CollectionChanged_INotifyCollectionChanged(): SubscriptionEvent<
(e: any, args: CollectionChangeInfo) => void
> {
return this.#CollectionChanged;
}
/**
* Insert item at postion
*
* @param {number} index
* @param {*} value
* @memberof ItemsCollectionModel
*/
insert(index: number, value: any) {
if (index <= this.internalArray.length && index >= 0) {
super.insert(index, value);
const info = new CollectionChangeInfo(CollectionChangeAction.Add);
const newItems = new SimpleList();
newItems.add(value);
info.NewItems = newItems;
info.NewStartingIndex = index;
this.#CollectionChanged.fire([this, info]);
this.syncItems?.();
} else {
throw new Error(
'Specified argument was out of the range of valid values.'
);
}
}
/**
* Removes item in the given index
*
* @param {number} index
* @memberof ItemsCollectionModel
*/
removeAt(index: number) {
if (index < this.internalArray.length && index >= 0) {
const itemToRemove = super.getItem(index);
super.removeAt(index);
const info = new CollectionChangeInfo(CollectionChangeAction.Remove);
const oldItems = new SimpleList();
oldItems.add(itemToRemove);
info.OldItems = oldItems;
info.OldStartingIndex = index;
this.#CollectionChanged.fire([this, info]);
this.syncItems?.();
} else {
throw new Error(
'Specified argument was out of the range of valid values.'
);
}
}
/**
* Adds item to the collection
*
* @param {*} value
* @memberof ItemsCollectionModel
*/
add(value: any): void {
super.add(value);
const info = new CollectionChangeInfo(CollectionChangeAction.Add);
const newItems = new SimpleList();
newItems.add(value);
info.NewItems = newItems;
info.NewStartingIndex = this.internalArray.length - 1;
this.#CollectionChanged.fire([this, info]);
this.syncItems?.();
}
/**
* Adds a new collections to the items collection.
* Clear the items collection before adding the new values.
*
* @param {*} collection
* @memberof ItemsCollectionModel
*/
setWithCollection(collection: any): void {
for (const item of collection) {
super.internalAdd(item);
}
this.change.fire(['Count']);
this.syncItems?.();
}
/**
* Clears the collection without firing the CollectionChange event.
*
* @memberof ItemsCollectionModel
*/
clearWithoutReset() {
super.clear();
this.syncItems?.();
}
/**
* Clears the collection
*
* @memberof ItemsCollectionModel
*/
clear(): void {
const currentValues = this.internalArray.map((e) => e);
super.clear();
const info = new CollectionChangeInfo(CollectionChangeAction.Reset);
info.OldItems = new SimpleList(currentValues);
this.#CollectionChanged.fire([this, info]);
this.syncItems?.();
}
/**
* Fire event to external handler
*
* @param {CollectionChangeInfo} info
* @memberof ItemsCollectionModel
*/
fire(info: CollectionChangeInfo): void {
this.#CollectionChanged.fire([this, info]);
}
/**
* Removes the first element from the collection which match with the value
*
* @param {*} value
* @returns {boolean}
* @memberof ItemsCollectionModel
*/
remove(value: any): boolean {
const removeItemIndex = super.indexOf(value);
if (removeItemIndex > -1) {
super.removeAt(removeItemIndex);
const info = new CollectionChangeInfo(CollectionChangeAction.Remove);
const oldItems = new SimpleList();
oldItems.add(value);
info.OldItems = oldItems;
info.OldStartingIndex = removeItemIndex;
this.#CollectionChanged.fire([this, info]);
this.syncItems?.();
return true;
}
return false;
}
@asExplicitImplementation(
'System.Collections.Specialized.INotifyCollectionChanged'
)
asINotifyCollectionChanged(): INotifyCollectionChanged {
return new ItemsCollectionModel_INotifyCollectionChangedWrapper(this);
}
}
class ItemsCollectionModel_INotifyCollectionChangedWrapper
implements INotifyCollectionChanged
{
/**
* Collection changed subsccription event
*
* @memberof ItemsCollectionModel
*/
get CollectionChanged(): SubscriptionEvent<
(e: any, args: CollectionChangeInfo) => void
> {
return this.instance.CollectionChanged_INotifyCollectionChanged;
}
/**
* Used to recognize if the class is an explicit implementation
*
* @memberof ItemsCollectionModel_INotifyCollectionChangedWrapper
*/
isExplicitInterfaceImplementationWrapper = true;
/**
*Creates an instance of ItemsCollectionModel_INotifyCollectionChangedWrapper.
*
* @param {ItemsCollectionModel} instance
* @memberof ItemsCollectionModel_INotifyCollectionChangedWrapper
*/
constructor(public instance: ItemsCollectionModel) {}
}