internal static void UpdateSourceDependentLists(DependencyObject d, DependencyProperty dp, DependencySource[] sources, Expression expr, bool add) { // Sources already validated to be on the same thread as Dependent (d) if (sources != null) { // don't hold a reference on the dependent if the expression is doing // the invalidations. This helps avoid memory leaks (bug 871139) if (expr.ForwardsInvalidations) { d = null; dp = null; } for (int i = 0; i < sources.Length; i++) { DependencySource source = sources[i]; // A Sealed DependencyObject does not have a Dependents list // so don't bother updating it (or attempt to add one). Debug.Assert((!source.DependencyObject.IsSealed) || (DependentListMapField.GetValue(source.DependencyObject) == default(object))); if (!source.DependencyObject.IsSealed) { // Retrieve the DependentListMap for this source // The list of dependents to invalidate is stored using a special negative key FrugalMap dependentListMap; object value = DependentListMapField.GetValue(source.DependencyObject); if (value != null) { dependentListMap = (FrugalMap)value; } else { dependentListMap = new FrugalMap(); } // Get list of DependentList off of ID map of Source object dependentListObj = dependentListMap[source.DependencyProperty.GlobalIndex]; Debug.Assert(dependentListObj != null, "dependentList should either be unset or non-null"); // Add/Remove new Dependent (this) to Source's list if (add) { DependentList dependentList; if (dependentListObj == DependencyProperty.UnsetValue) { dependentListMap[source.DependencyProperty.GlobalIndex] = dependentList = new DependentList(); } else { dependentList = (DependentList)dependentListObj; } dependentList.Add(d, dp, expr); } else { if (dependentListObj != DependencyProperty.UnsetValue) { DependentList dependentList = (DependentList)dependentListObj; dependentList.Remove(d, dp, expr); if (dependentList.IsEmpty) { // No more dependencies for this property; reclaim the space if we can. dependentListMap[source.DependencyProperty.GlobalIndex] = DependencyProperty.UnsetValue; } } } // Set the updated struct back into the source's _localStore. DependentListMapField.SetValue(source.DependencyObject, dependentListMap); } } } }
/// <summary> /// Constructor for EventHandlersStore /// </summary> public EventHandlersStore() { _entries = new FrugalMap(); }
// Called during Style/Template Seal when encountering a property trigger that // has associated TriggerActions. internal static void AddPropertyTriggerWithAction(TriggerBase triggerBase, DependencyProperty property, ref FrugalMap triggersWithActions) { object existing = triggersWithActions[property.GlobalIndex]; if( existing == DependencyProperty.UnsetValue ) { // No existing trigger, we put given trigger as entry. triggersWithActions[property.GlobalIndex] = triggerBase; } else { TriggerBase existingTriggerBase = existing as TriggerBase; if( existingTriggerBase != null ) { List<TriggerBase> newList = new List<TriggerBase>(); newList.Add(existingTriggerBase); newList.Add(triggerBase); triggersWithActions[property.GlobalIndex] = newList; } else { Debug.Assert( existing is List<TriggerBase>, "FrugalMap for holding List<TriggerBase> is holding an instance of unexpected type " + existing.GetType() ); List<TriggerBase> existingList = (List<TriggerBase>)existing; existingList.Add(triggerBase); } } // Note the order in which we processed this trigger. triggerBase.EstablishLayer(); }
// // This method // 1. Adds shared table entries for property values set via Triggers // private static void ProcessTemplateTriggers( TriggerCollection triggers, FrameworkTemplate frameworkTemplate, ref FrugalStructList<ChildRecord> childRecordFromChildIndex, ref FrugalStructList<ItemStructMap<TriggerSourceRecord>> triggerSourceRecordFromChildIndex, ref FrugalStructList<ContainerDependent> containerDependents, ref FrugalStructList<ChildPropertyDependent> resourceDependents, ref ItemStructList<ChildEventDependent> eventDependents, ref HybridDictionary dataTriggerRecordFromBinding, HybridDictionary childIndexFromChildID, ref bool hasInstanceValues, ref HybridDictionary triggerActions, FrameworkElementFactory templateRoot, ref EventHandlersStore eventHandlersStore, ref FrugalMap propertyTriggersWithActions, ref HybridDictionary dataTriggersWithActions, ref bool hasLoadedChangeHandler) { if (triggers != null) { int triggerCount = triggers.Count; for (int i = 0; i < triggerCount; i++) { TriggerBase triggerBase = triggers[i]; Trigger trigger; MultiTrigger multiTrigger; DataTrigger dataTrigger; MultiDataTrigger multiDataTrigger; EventTrigger eventTrigger; DetermineTriggerType( triggerBase, out trigger, out multiTrigger, out dataTrigger, out multiDataTrigger, out eventTrigger ); if ( trigger != null || multiTrigger != null|| dataTrigger != null || multiDataTrigger != null ) { // Update the SourceChildIndex for each of the conditions for this trigger TriggerCondition[] conditions = triggerBase.TriggerConditions; for (int k=0; k<conditions.Length; k++) { conditions[k].SourceChildIndex = StyleHelper.QueryChildIndexFromChildName(conditions[k].SourceName, childIndexFromChildID); } // Set things up to handle Setter values for (int j = 0; j < triggerBase.PropertyValues.Count; j++) { PropertyValue propertyValue = triggerBase.PropertyValues[j]; // Check for trigger rules that act on template children if (propertyValue.ChildName == StyleHelper.SelfName) { // "Self" (container) trigger // Track properties on the container that are being driven by // the Template so that they can be invalidated during Template changes StyleHelper.AddContainerDependent(propertyValue.Property, true /*fromVisualTrigger*/, ref containerDependents); } StyleHelper.UpdateTables(ref propertyValue, ref childRecordFromChildIndex, ref triggerSourceRecordFromChildIndex, ref resourceDependents, ref dataTriggerRecordFromBinding, childIndexFromChildID, ref hasInstanceValues); } // Set things up to handle TriggerActions if( triggerBase.HasEnterActions || triggerBase.HasExitActions ) { if( trigger != null ) { StyleHelper.AddPropertyTriggerWithAction( triggerBase, trigger.Property, ref propertyTriggersWithActions ); } else if( multiTrigger != null ) { for( int k = 0; k < multiTrigger.Conditions.Count; k++ ) { Condition triggerCondition = multiTrigger.Conditions[k]; StyleHelper.AddPropertyTriggerWithAction( triggerBase, triggerCondition.Property, ref propertyTriggersWithActions ); } } else if( dataTrigger != null ) { StyleHelper.AddDataTriggerWithAction( triggerBase, dataTrigger.Binding, ref dataTriggersWithActions ); } else if( multiDataTrigger != null ) { for( int k = 0; k < multiDataTrigger.Conditions.Count; k++ ) { Condition dataCondition = multiDataTrigger.Conditions[k]; StyleHelper.AddDataTriggerWithAction( triggerBase, dataCondition.Binding, ref dataTriggersWithActions ); } } else { throw new InvalidOperationException(SR.Get(SRID.UnsupportedTriggerInTemplate, triggerBase.GetType().Name)); } } } else if( eventTrigger != null ) { StyleHelper.ProcessEventTrigger(eventTrigger, childIndexFromChildID, ref triggerActions, ref eventDependents, templateRoot, frameworkTemplate, ref eventHandlersStore, ref hasLoadedChangeHandler); } else { throw new InvalidOperationException(SR.Get(SRID.UnsupportedTriggerInTemplate, triggerBase.GetType().Name)); } } } }
// // This method // 1. Is invoked when Styled/Templated container property invalidation // is propagated to its dependent properties from Style/Template. // internal static void OnTriggerSourcePropertyInvalidated( Style ownerStyle, FrameworkTemplate frameworkTemplate, DependencyObject container, DependencyProperty dp, DependencyPropertyChangedEventArgs changedArgs, bool invalidateOnlyContainer, ref FrugalStructList<ItemStructMap<TriggerSourceRecord>> triggerSourceRecordFromChildIndex, ref FrugalMap propertyTriggersWithActions, int sourceChildIndex) { Debug.Assert(ownerStyle != null || frameworkTemplate != null ); /////////////////////////////////////////////////////////////////// // Update all values affected by property trigger Setters // Check if this Child Index is represented in given data-structure if ((0 <= sourceChildIndex) && (sourceChildIndex < triggerSourceRecordFromChildIndex.Count)) { // Fetch the triggerSourceRecordMap for the given childIndex ItemStructMap<TriggerSourceRecord> triggerSourceRecordMap = triggerSourceRecordFromChildIndex[sourceChildIndex]; // Check if this Container property is represented in style int mapIndex = triggerSourceRecordMap.Search(dp.GlobalIndex); if (mapIndex >= 0) { // Container's property is represented in style TriggerSourceRecord record = triggerSourceRecordMap.Entries[mapIndex].Value; // Invalidate all Self/Child-Index/Property dependents InvalidateDependents(ownerStyle, frameworkTemplate, container, dp, ref record.ChildPropertyDependents, invalidateOnlyContainer); } } /////////////////////////////////////////////////////////////////// // Find all TriggerActions that may need to execute in response to // the property change. object candidateTrigger = propertyTriggersWithActions[dp.GlobalIndex]; if( candidateTrigger != DependencyProperty.UnsetValue ) { // One or more trigger objects need to be evaluated. The candidateTrigger // object may be a single trigger or a collection of them. TriggerBase triggerBase = candidateTrigger as TriggerBase; if( triggerBase != null ) { InvokePropertyTriggerActions( triggerBase, container, dp, changedArgs, sourceChildIndex, ownerStyle, frameworkTemplate ); } else { Debug.Assert(candidateTrigger is List<TriggerBase>, "Internal data structure error: The FrugalMap [Style/Template].PropertyTriggersWithActions " + "is expected to hold a single TriggerBase or a List<T> of them. An object of type " + candidateTrigger.GetType().ToString() + " is not expected. Where did this object come from?"); List<TriggerBase> triggerList = (List<TriggerBase>)candidateTrigger; for( int i = 0; i < triggerList.Count; i++ ) { InvokePropertyTriggerActions( triggerList[i], container, dp, changedArgs, sourceChildIndex, ownerStyle, frameworkTemplate ); } } } }