예제 #1
0
        //
        //  All table datastructures read-lock-free/write-lock
        //  UpdateTables writes the datastructures, locks set by callers
        //
        //  This method
        //  1. Adds a ChildValueLookup entry to the given ChildRecord.
        //     This is used in value computation.
        //  2. Optionally adds a ChildPropertyDependent entry to the given
        //     ContainerRecordFromProperty list. This is used to invalidate
        //     container dependents.
        //  3. Optionally adds a ChildPropertyDependent entry to the given
        //     ResourceDependents list. This is used when invalidating resource
        //     references
        //
        internal static void UpdateTables(
            ref PropertyValue                                           propertyValue,
            ref FrugalStructList<ChildRecord>                           childRecordFromChildIndex,
            ref FrugalStructList<ItemStructMap<TriggerSourceRecord>>    triggerSourceRecordFromChildIndex,
            ref FrugalStructList<ChildPropertyDependent>                resourceDependents,
            ref HybridDictionary                                        dataTriggerRecordFromBinding,
            HybridDictionary                                            childIndexFromChildName,
            ref bool                                                    hasInstanceValues)
        {
            //
            //  Record instructions for Child/Self value computation
            //

            // Query for child index (may be 0 if "self")
            int childIndex = QueryChildIndexFromChildName(propertyValue.ChildName, childIndexFromChildName);
            if (childIndex == -1)
            {
                throw new InvalidOperationException(SR.Get(SRID.NameNotFound, propertyValue.ChildName));
            }

            object value = propertyValue.ValueInternal;
            bool requiresInstanceStorage = RequiresInstanceStorage(ref value);
            propertyValue.ValueInternal = value;

            childRecordFromChildIndex.EnsureIndex(childIndex);
            ChildRecord childRecord = childRecordFromChildIndex[childIndex];

            int mapIndex = childRecord.ValueLookupListFromProperty.EnsureEntry(propertyValue.Property.GlobalIndex);

            ChildValueLookup valueLookup = new ChildValueLookup();
            valueLookup.LookupType = (ValueLookupType)propertyValue.ValueType; // Maps directly to ValueLookupType for applicable values
            valueLookup.Conditions = propertyValue.Conditions;
            valueLookup.Property = propertyValue.Property;
            valueLookup.Value = propertyValue.ValueInternal;

            childRecord.ValueLookupListFromProperty.Entries[mapIndex].Value.Add(ref valueLookup);

            // Put back modified struct
            childRecordFromChildIndex[childIndex] = childRecord;

            //
            //  Container property invalidation
            //

            switch ((ValueLookupType)propertyValue.ValueType)
            {
            case ValueLookupType.Simple:
                {
                    hasInstanceValues |= requiresInstanceStorage;
                }
                break;

            case ValueLookupType.Trigger:
            case ValueLookupType.PropertyTriggerResource:
                {
                    if( propertyValue.Conditions != null )
                    {
                        // Record the current property as a dependent to each on of the
                        // properties in the condition. This is to facilitate the invalidation
                        // of the current property in the event that any one of the properties
                        // in the condition change. This will allow the current property to get
                        // re-evaluated.
                        for (int i = 0; i < propertyValue.Conditions.Length; i++)
                        {
                            int sourceChildIndex = propertyValue.Conditions[i].SourceChildIndex;
                            triggerSourceRecordFromChildIndex.EnsureIndex(sourceChildIndex);
                            ItemStructMap<TriggerSourceRecord> triggerSourceRecordMap = triggerSourceRecordFromChildIndex[sourceChildIndex];

                            if (propertyValue.Conditions[i].Property == null)
                            {
                                throw new InvalidOperationException(SR.Get(SRID.MissingTriggerProperty));
                            }
                            int index = triggerSourceRecordMap.EnsureEntry(propertyValue.Conditions[i].Property.GlobalIndex);
                            AddPropertyDependent(childIndex, propertyValue.Property,
                                ref triggerSourceRecordMap.Entries[index].Value.ChildPropertyDependents);

                            // Store the triggerSourceRecordMap back into the list after it has been updated
                            triggerSourceRecordFromChildIndex[sourceChildIndex] = triggerSourceRecordMap;
                        }

                        // If value is a resource reference, add dependent on resource changes
                        if ((ValueLookupType)propertyValue.ValueType == ValueLookupType.PropertyTriggerResource)
                        {
                            AddResourceDependent(childIndex, propertyValue.Property, propertyValue.ValueInternal, ref resourceDependents);
                        }
                    }

                    // values in a Trigger may require per-instance storage
                    if ((ValueLookupType)propertyValue.ValueType != ValueLookupType.PropertyTriggerResource)
                    {
                        hasInstanceValues |= requiresInstanceStorage;
                    }
                }
                break;

            case ValueLookupType.DataTrigger:
            case ValueLookupType.DataTriggerResource:
                {
                    if( propertyValue.Conditions != null )
                    {
                        if (dataTriggerRecordFromBinding == null)
                        {
                            dataTriggerRecordFromBinding = new HybridDictionary();
                        }

                        // Record container conditional child property dependents
                        for (int i = 0; i < propertyValue.Conditions.Length; i++)
                        {
                            DataTriggerRecord record = (DataTriggerRecord)dataTriggerRecordFromBinding[propertyValue.Conditions[i].Binding];
                            if (record == null)
                            {
                                record = new DataTriggerRecord();
                                dataTriggerRecordFromBinding[propertyValue.Conditions[i].Binding] = record;
                            }

                            // Add dependent on trigger
                            AddPropertyDependent(childIndex, propertyValue.Property,
                                ref record.Dependents);
                        }

                        // If value is a resource reference, add dependent on resource changes
                        if ((ValueLookupType)propertyValue.ValueType == ValueLookupType.DataTriggerResource)
                        {
                            AddResourceDependent(childIndex, propertyValue.Property, propertyValue.ValueInternal, ref resourceDependents);
                        }
                    }

                    // values in a DataTrigger may require per-instance storage
                    if ((ValueLookupType)propertyValue.ValueType != ValueLookupType.DataTriggerResource)
                    {
                        hasInstanceValues |= requiresInstanceStorage;
                    }
                }
                break;

            case ValueLookupType.TemplateBinding:
                {
                    TemplateBindingExtension templateBinding = (TemplateBindingExtension)propertyValue.ValueInternal;
                    DependencyProperty destinationProperty = propertyValue.Property; // Child
                    DependencyProperty sourceProperty = templateBinding.Property; // Container

                    // Record the current property as a dependent to the aliased
                    // property on the container. This is to facilitate the
                    // invalidation of the current property in the event that the
                    // aliased container property changes. This will allow the current
                    // property to get re-evaluated.
                    int sourceChildIndex = 0; // TemplateBinding is always sourced off of the container
                    triggerSourceRecordFromChildIndex.EnsureIndex(sourceChildIndex);
                    ItemStructMap<TriggerSourceRecord> triggerSourceRecordMap = triggerSourceRecordFromChildIndex[sourceChildIndex];

                    int index = triggerSourceRecordMap.EnsureEntry(sourceProperty.GlobalIndex);
                    AddPropertyDependent(childIndex, destinationProperty, ref triggerSourceRecordMap.Entries[index].Value.ChildPropertyDependents);

                    // Store the triggerSourceRecordMap back into the list after it has been updated
                    triggerSourceRecordFromChildIndex[sourceChildIndex] = triggerSourceRecordMap;
                }
                break;

            case ValueLookupType.Resource:
                {
                    AddResourceDependent(childIndex, propertyValue.Property, propertyValue.ValueInternal, ref resourceDependents);
                }
                break;
            }
        }