// Given a multi data trigger and associated context information, // evaluate the old and new states of the trigger. // The short-circuit logic of multi property trigger applies here too. // we bail if any of the "other" conditions evaluate to false. private static void EvaluateOldNewStates( MultiDataTrigger multiDataTrigger, DependencyObject triggerContainer, BindingBase binding, BindingValueChangedEventArgs changedArgs, UncommonField<HybridDictionary[]> dataField, Style style, FrameworkTemplate frameworkTemplate, out bool oldState, out bool newState ) { BindingBase evaluationBinding = null; object evaluationValue = null; TriggerCondition[] conditions = multiDataTrigger.TriggerConditions; // Set up the default condition: A trigger with no conditions will never evaluate to true. oldState = false; newState = false; for( int i = 0; i < multiDataTrigger.Conditions.Count; i++ ) { evaluationBinding = conditions[i].Binding; Debug.Assert( evaluationBinding != null, "A null binding was encountered in a MultiDataTrigger conditions collection - this is invalid input and should have been caught earlier."); if( evaluationBinding == binding ) { // The binding that changed belonged to the current condition. oldState = conditions[i].ConvertAndMatch(changedArgs.OldValue); newState = conditions[i].ConvertAndMatch(changedArgs.NewValue); if( oldState == newState ) { // There couldn't possibly be a transition here, abort. The // returned values here aren't necessarily the state of the // triggers, but we only care about a transition today. If // we care about actual values, we'll need to continue evaluation. return; } } else { evaluationValue = GetDataTriggerValue(dataField, triggerContainer, evaluationBinding); if( !conditions[i].ConvertAndMatch(evaluationValue) ) { // A condition other than the one changed has evaluated to false. // There couldn't possibly be a transition here, short-circuit and abort. oldState = false; newState = false; return; } } } // We should only get this far only if the binding change causes // a true->false (or vice versa) transition in one of the conditions, // AND that every other condition evaluated to true. return; }
// Given a single data trigger and associated context information, // evaluate the old and new states of the trigger. private static void EvaluateOldNewStates( DataTrigger dataTrigger, DependencyObject triggerContainer, BindingBase binding, BindingValueChangedEventArgs bindingChangedArgs, UncommonField<HybridDictionary[]> dataField, Style style, FrameworkTemplate frameworkTemplate, out bool oldState, out bool newState ) { TriggerCondition[] conditions = dataTrigger.TriggerConditions; Debug.Assert( conditions != null && conditions.Length == 1, "This method assumes there is exactly one TriggerCondition." ); oldState = conditions[0].ConvertAndMatch(bindingChangedArgs.OldValue); newState = conditions[0].ConvertAndMatch(bindingChangedArgs.NewValue); }
// Called from either the Style-specific ExecuteOnApplyEnterActions or the // Template-specific version. This section is the common code for both that // walks through the trigger collection and execute applicable actions. private static void ExecuteOnApplyEnterExitActionsLoop( DependencyObject triggerContainer, TriggerCollection triggers, Style style, FrameworkTemplate ft, UncommonField<HybridDictionary[]> dataField ) { TriggerBase triggerBase; bool triggerState; for( int i = 0; i < triggers.Count; i++ ) { triggerBase = triggers[i]; if( (!triggerBase.HasEnterActions) && (!triggerBase.HasExitActions) ) { ; // Trigger has neither enter nor exit actions. There's nothing to run anyway, so skip. } else if( triggerBase.ExecuteEnterActionsOnApply || triggerBase.ExecuteExitActionsOnApply ) { // Look for any SourceName in the condition if( NoSourceNameInTrigger( triggerBase ) ) { // Evaluate the current state of the trigger. triggerState = triggerBase.GetCurrentState( triggerContainer, dataField ); if( triggerState && triggerBase.ExecuteEnterActionsOnApply ) { // Trigger is true, and Trigger wants EnterActions to be executed on Style/Template application. InvokeActions( triggerBase.EnterActions, triggerBase, triggerContainer, style, ft ); } else if( !triggerState && triggerBase.ExecuteExitActionsOnApply ) { // Trigger is false, and Trigger wants ExitActions to be executed on Style/Template application. InvokeActions( triggerBase.ExitActions, triggerBase, triggerContainer, style, ft ); } } else { // If one or more conditions are dependent on a template // child, then it can't possibly apply immediately. } } } }
// // This method // 1. Is called whenever a new Style/Template is applied to an FE/FCE // 2. It adds per-instance StyleData/TemplateData for the new Style/Template // internal static void CreateInstanceData( UncommonField<HybridDictionary[]> dataField, DependencyObject container, FrameworkElement fe, FrameworkContentElement fce, Style newStyle, FrameworkTemplate newFrameworkTemplate ) { Debug.Assert((fe != null && fce == null) || (fe == null && fce != null)); Debug.Assert((fe != null && fe == container) || (fce != null && fce == container)); Debug.Assert(newStyle != null || newFrameworkTemplate != null ); if (newStyle != null) { if (newStyle.HasInstanceValues) { HybridDictionary instanceValues = EnsureInstanceData(dataField, container, InstanceStyleData.InstanceValues); StyleHelper.ProcessInstanceValuesForChild( container, container, 0, instanceValues, true, ref newStyle.ChildRecordFromChildIndex); } } else if (newFrameworkTemplate != null) { if (newFrameworkTemplate.HasInstanceValues) { HybridDictionary instanceValues = EnsureInstanceData(dataField, container, InstanceStyleData.InstanceValues); StyleHelper.ProcessInstanceValuesForChild( container, container, 0, instanceValues, true, ref newFrameworkTemplate.ChildRecordFromChildIndex); } } }
// evaluate the current state of the trigger internal override bool GetCurrentState(DependencyObject container, UncommonField<HybridDictionary[]> dataField) { bool retVal = (TriggerConditions.Length > 0); for( int i = 0; retVal && i < TriggerConditions.Length; i++ ) { Debug.Assert( TriggerConditions[i].SourceChildIndex == 0, "This method was created to handle properties on the containing object, more work is needed to handle templated children too." ); retVal = TriggerConditions[i].Match(container.GetValue(TriggerConditions[i].Property)); } return retVal; }
// The ordering of how things are created and which methods are called are ABSOLUTELY critical // Getting this order slightly off will result in several issues that are very hard to debug. // // The order must be: // Register name to the TemplateNameScope (Either the real name specified by x:Name or // RuntimeNameProperty or a fake name that we have to generate to call // RegisterName. This is CRUCIAL since RegisterName sets the TemplatedParent // and the TemplateChildIndex on the object // If we're dealing with the root, wire the object to the parent (via FE.TemplateChild // if we're dealing with an FE as the container or using FEF if it's not an FE) // Invalidate properties on the object private DependencyObject LoadOptimizedTemplateContent(DependencyObject container, IComponentConnector componentConnector, IStyleConnector styleConnector, List<DependencyObject> affectedChildren, UncommonField<Hashtable> templatedNonFeChildrenField) { if (Names == null) { Names = new XamlContextStack<Frame>(() => new Frame()); } DependencyObject rootObject = null; if (TraceMarkup.IsEnabled) { TraceMarkup.Trace(TraceEventType.Start, TraceMarkup.Load); } FrameworkElement feContainer = container as FrameworkElement; bool isTemplatedParentAnFE = feContainer != null; TemplateNameScope nameScope = new TemplateNameScope(container, affectedChildren, this); XamlObjectWriterSettings settings = System.Windows.Markup.XamlReader.CreateObjectWriterSettings(_templateHolder.ObjectWriterParentSettings); settings.ExternalNameScope = nameScope; settings.RegisterNamesOnExternalNamescope = true; IEnumerator<String> nameEnumerator = ChildNames.GetEnumerator(); // Delegate for AfterBeginInit event settings.AfterBeginInitHandler = delegate(object sender, System.Xaml.XamlObjectEventArgs args) { HandleAfterBeginInit(args.Instance, ref rootObject, container, feContainer, nameScope, nameEnumerator); if (XamlSourceInfoHelper.IsXamlSourceInfoEnabled) { XamlSourceInfoHelper.SetXamlSourceInfo(args.Instance, args, null); } }; // Delegate for BeforeProperties event settings.BeforePropertiesHandler = delegate(object sender, System.Xaml.XamlObjectEventArgs args) { HandleBeforeProperties(args.Instance, ref rootObject, container, feContainer, nameScope); }; // Delegate for XamlSetValue event settings.XamlSetValueHandler = delegate(object sender, System.Windows.Markup.XamlSetValueEventArgs setArgs) { setArgs.Handled = ReceivePropertySet(sender, setArgs.Member, setArgs.Value, container); }; XamlObjectWriter objectWriter = _templateHolder.ObjectWriterFactory.GetXamlObjectWriter(settings); try { LoadTemplateXaml(objectWriter); } finally { if (TraceMarkup.IsEnabled) { TraceMarkup.Trace(TraceEventType.Stop, TraceMarkup.Load, rootObject); } } return rootObject; }
// Instantiate a tree. This is a recursive routine that will build the // subtree via calls to itself. The root node being instantiated will // have identical references for the "container" and "parent" parameters. // The "affectedChildren" and "noChildIndexChildren" parameters refer to the children // chain for the "container" object. This chain will have all the // children - not just the immediate children. The node being // instantiated here will be added to this chain. // The tree is instantiated in a depth-first traversal, so children nodes // are added to the chain in depth-first order as well. //[CodeAnalysis("AptcaMethodsShouldOnlyCallAptcaMethods")] //Tracking Bug: 29647 internal DependencyObject InstantiateTree( UncommonField<HybridDictionary[]> dataField, DependencyObject container, DependencyObject parent, List<DependencyObject> affectedChildren, ref List<DependencyObject> noChildIndexChildren, ref FrugalStructList<ChildPropertyDependent> resourceDependents) { EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordXamlBaml, EventTrace.Level.Verbose, EventTrace.Event.WClientParseFefCrInstBegin); FrameworkElement containerAsFE = container as FrameworkElement; bool isContainerAnFE = containerAsFE != null; DependencyObject treeNode = null; // If we have text, just add it to the parent. Otherwise create the child // subtree if (_text != null) { // of FrameworkContentElement parent. This is the logical equivalent // to what happens when adding a child to a visual collection. IAddChild addChildParent = parent as IAddChild; if (addChildParent == null) { throw new InvalidOperationException(SR.Get(SRID.TypeMustImplementIAddChild, parent.GetType().Name)); } else { addChildParent.AddText(_text); } } else { // Factory create instance treeNode = CreateDependencyObject(); EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordXamlBaml, EventTrace.Level.Verbose, EventTrace.Event.WClientParseFefCrInstEnd); // The tree node is either a FrameworkElement or a FrameworkContentElement. // we'll deal with one or the other... FrameworkObject treeNodeFO = new FrameworkObject(treeNode); Visual3D treeNodeVisual3D = null; bool treeNodeIsVisual3D = false; if (!treeNodeFO.IsValid) { // If it's neither of those, we have special support for Visual3D treeNodeVisual3D = treeNode as Visual3D; if (treeNodeVisual3D != null) treeNodeIsVisual3D = true; } Debug.Assert( treeNodeFO.IsValid || (treeNodeVisual3D != null), "We should not be trying to instantiate a node that is neither FrameworkElement nor FrameworkContentElement. A type check should have been done when Type is set"); // And here's the bool we'll use to make the decision. bool treeNodeIsFE = treeNodeFO.IsFE; // Handle FE/FCE-specific optimizations if (!treeNodeIsVisual3D) { // Postpone "Initialized" event NewNodeBeginInit( treeNodeIsFE, treeNodeFO.FE, treeNodeFO.FCE ); // Set the resource reference flags if (StyleHelper.HasResourceDependentsForChild(_childIndex, ref resourceDependents)) { treeNodeFO.HasResourceReference = true; } // Update the two chains that tracks all the nodes created // from all the FrameworkElementFactory of this Style. UpdateChildChains( _childName, _childIndex, treeNodeIsFE, treeNodeFO.FE, treeNodeFO.FCE, affectedChildren, ref noChildIndexChildren ); // All FrameworkElementFactory-created elements point to the object // whose Style.VisualTree definition caused all this to occur NewNodeStyledParentProperty( container, isContainerAnFE, treeNodeIsFE, treeNodeFO.FE, treeNodeFO.FCE ); // Initialize the per-instance data for the new element. This // needs to be done before any properties are invalidated. if (_childIndex != -1) { Debug.Assert( _frameworkTemplate != null ); StyleHelper.CreateInstanceDataForChild(dataField, container, treeNode, _childIndex, _frameworkTemplate.HasInstanceValues, ref _frameworkTemplate.ChildRecordFromChildIndex); } // If this element needs to know about the Loaded or Unloaded events, set the optimization // bit in the element if (HasLoadedChangeHandler) { BroadcastEventHelper.AddHasLoadedChangeHandlerFlagInAncestry(treeNode); } } else { if (_childName != null) { // Add this instance to the child index chain so that it may // be tracked by the style affectedChildren.Add(treeNode); } else { // Child nodes with no _childID (hence no _childIndex) are // tracked on a separate chain that will be appended to the // main chain for cleanup purposes. if (noChildIndexChildren == null) { noChildIndexChildren = new List<DependencyObject>(4); } noChildIndexChildren.Add(treeNode); } } // New node is initialized, build tree top down // (Node added before children of node) if (container == parent) { // Set the NameScope on the root of the Template generated tree TemplateNameScope templateNameScope = new TemplateNameScope(container); NameScope.SetNameScope(treeNode, templateNameScope); // This is the root of the tree if (isContainerAnFE) { // The root is added to the Visual tree (not logical) for the // case of FrameworkElement parents containerAsFE.TemplateChild = treeNodeFO.FE; } else { // The root is added to the logical tree for the case // of FrameworkContentElement parent. This is the logical equivalent // to what happens when adding a child to a visual collection. AddNodeToLogicalTree( (FrameworkContentElement)parent, _type, treeNodeIsFE, treeNodeFO.FE, treeNodeFO.FCE ); } } else { // Call parent IAddChild to add treeNodeFO AddNodeToParent( parent, treeNodeFO ); } // Either set properties or invalidate them, depending on the type if (!treeNodeIsVisual3D) { // For non-3D content, we need to invalidate any properties that // came from FrameworkElementFactory.SetValue or VisulaTrigger.SetValue // so that they can get picked up. Debug.Assert( _frameworkTemplate != null ); StyleHelper.InvalidatePropertiesOnTemplateNode( container, treeNodeFO, _childIndex, ref _frameworkTemplate.ChildRecordFromChildIndex, false /*isDetach*/, this); } else { // For 3D, which doesn't understand templates, we set the properties directly // onto the newly-instantiated element. for (int i = 0; i < PropertyValues.Count; i++) { if (PropertyValues[i].ValueType == PropertyValueType.Set) { // Get the value out of the table. object o = PropertyValues[i].ValueInternal; // If it's a freezable that can't be frozen, it's probably not sharable, // so we make a copy of it. Freezable freezableValue = o as Freezable; if (freezableValue != null && !freezableValue.CanFreeze) { o = freezableValue.Clone(); } // Or, if it's a markup extension, get the value // to set on this property from the MarkupExtension itself. MarkupExtension me = o as MarkupExtension; if (me != null) { ProvideValueServiceProvider serviceProvider = new ProvideValueServiceProvider(); serviceProvider.SetData( treeNodeVisual3D, PropertyValues[i].Property ); o = me.ProvideValue( serviceProvider ); } // Finally, set the value onto the object. treeNodeVisual3D.SetValue(PropertyValues[i].Property, o); } else { // We don't support resource references, triggers, etc within the 3D content throw new NotSupportedException(SR.Get(SRID.Template3DValueOnly, PropertyValues[i].Property) ); } } } // Build child tree from factories FrameworkElementFactory childFactory = _firstChild; while (childFactory != null) { childFactory.InstantiateTree( dataField, container, treeNode, affectedChildren, ref noChildIndexChildren, ref resourceDependents); childFactory = childFactory._nextSibling; } if (!treeNodeIsVisual3D) { // Fire "Initialized" event NewNodeEndInit( treeNodeIsFE, treeNodeFO.FE, treeNodeFO.FCE ); } } return treeNode; }
// evaluate the current state of the trigger internal override bool GetCurrentState(DependencyObject container, UncommonField<HybridDictionary[]> dataField) { Debug.Assert( TriggerConditions != null && TriggerConditions.Length == 1, "This method assumes there is exactly one TriggerCondition." ); return TriggerConditions[0].ConvertAndMatch(StyleHelper.GetDataTriggerValue(dataField, container, TriggerConditions[0].Binding)); }
// =========================================================================== // These methods are invoked when a Property // value is fetched from a Style/Template // =========================================================================== #region GetValueMethods // // This method // 1. Computes the value of a template child // (Index is '0' when the styled container is asking) // internal static object GetChildValue( UncommonField<HybridDictionary[]> dataField, DependencyObject container, int childIndex, FrameworkObject child, DependencyProperty dp, ref FrugalStructList<ChildRecord> childRecordFromChildIndex, ref EffectiveValueEntry entry, out ValueLookupType sourceType, FrameworkElementFactory templateRoot) { object value = DependencyProperty.UnsetValue; sourceType = ValueLookupType.Simple; // Check if this Child Index is represented in given data-structure if ((0 <= childIndex) && (childIndex < childRecordFromChildIndex.Count)) { // Fetch the childRecord for the given childIndex ChildRecord childRecord = childRecordFromChildIndex[childIndex]; // Check if this Property is represented in the childRecord int mapIndex = childRecord.ValueLookupListFromProperty.Search(dp.GlobalIndex); if (mapIndex >= 0) { if (childRecord.ValueLookupListFromProperty.Entries[mapIndex].Value.Count > 0) { // Child Index/Property are both represented in this style/template, // continue with value computation // Pass into helper so ValueLookup struct can be accessed by ref value = GetChildValueHelper( dataField, ref childRecord.ValueLookupListFromProperty.Entries[mapIndex].Value, dp, container, child, childIndex, true, ref entry, out sourceType, templateRoot); } } } return value; }
//+---------------------------------------------------------------------------------- // // ApplyTemplateContent // // Instantiate the content of the template (either from FEFs or from Baml). // This is done for every element to which this template is attached. // //+---------------------------------------------------------------------------------- #region InstantiateSubTree //[CodeAnalysis("AptcaMethodsShouldOnlyCallAptcaMethods")] //Tracking Bug: 29647 internal static bool ApplyTemplateContent( UncommonField<HybridDictionary[]> dataField, DependencyObject container, FrameworkElementFactory templateRoot, int lastChildIndex, HybridDictionary childIndexFromChildID, FrameworkTemplate frameworkTemplate) { Debug.Assert(frameworkTemplate != null ); bool visualsCreated = false; FrameworkElement feContainer = container as FrameworkElement; // Is this a FEF-style template? if (templateRoot != null) { // Yes, we'll instantiate from a FEF tree. EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordXamlBaml, EventTrace.Level.Verbose, EventTrace.Event.WClientParseInstVisTreeBegin); CheckForCircularReferencesInTemplateTree(container, frameworkTemplate ); // Container is considered ChildIndex '0' (Self), but, // Container.ChildIndex isn't set List<DependencyObject> affectedChildren = new List<DependencyObject>(lastChildIndex); // Assign affectedChildren to container TemplatedFeChildrenField.SetValue(container, affectedChildren); // When building the template children chain, we keep a chain of // nodes that don't need to be in the chain for property invalidation // or lookup purposes. (And hence not assigned a TemplateChildIndex) // We only need them in order to clean up their _templatedParent // references (see FrameworkElement.ClearTemplateChain) List<DependencyObject> noChildIndexChildren = null; // Instantiate template // Setup container's reference to first child in chain // and add to tree DependencyObject treeRoot = templateRoot.InstantiateTree( dataField, container, container, affectedChildren, ref noChildIndexChildren, ref frameworkTemplate.ResourceDependents); Debug.Assert(treeRoot is FrameworkElement || treeRoot is FrameworkContentElement, "Root node of tree must be a FrameworkElement or FrameworkContentElement. This should have been caught by set_VisualTree" ); // From childFirst to childLast is the chain of child nodes with // childIndex. Append that chain with the chain of child nodes // with no childIndex assigned. if( noChildIndexChildren != null ) { affectedChildren.AddRange(noChildIndexChildren); } visualsCreated = true; if (feContainer != null && EventTrace.IsEnabled(EventTrace.Keyword.KeywordXamlBaml, EventTrace.Level.Verbose)) { string label = feContainer.Name; if (label == null || label.Length == 0) label = container.GetHashCode().ToString(System.Globalization.CultureInfo.InvariantCulture); EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientParseInstVisTreeEnd, EventTrace.Keyword.KeywordXamlBaml, EventTrace.Level.Verbose, String.Format(System.Globalization.CultureInfo.InvariantCulture, "Style.InstantiateSubTree for {0} {1}", container.GetType().Name, label)); } } // It's not a FEF-style template, is it a non-empty optimized one? else if (frameworkTemplate != null && frameworkTemplate.HasXamlNodeContent) { // Yes, create from the optimized template content. EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordXamlBaml, EventTrace.Level.Verbose, EventTrace.Event.WClientParseInstVisTreeBegin); CheckForCircularReferencesInTemplateTree(container, frameworkTemplate ); // Container is considered ChildIndex '0' (Self), but, // Container.ChildIndex isn't set List<DependencyObject> affectedChildren = new List<DependencyObject>(lastChildIndex); // Assign affectedChildren to container TemplatedFeChildrenField.SetValue(container, affectedChildren); DependencyObject treeRoot; // Load the content treeRoot = frameworkTemplate.LoadContent( container, affectedChildren); Debug.Assert(treeRoot == null || treeRoot is FrameworkElement || treeRoot is FrameworkContentElement, "Root node of tree must be a FrameworkElement or FrameworkContentElement. This should have been caught by set_VisualTree" ); visualsCreated = true; if (feContainer != null && EventTrace.IsEnabled(EventTrace.Keyword.KeywordXamlBaml, EventTrace.Level.Verbose)) { string label = feContainer.Name; if (label == null || label.Length == 0) label = container.GetHashCode().ToString(System.Globalization.CultureInfo.InvariantCulture); EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientParseInstVisTreeEnd, EventTrace.Keyword.KeywordXamlBaml, EventTrace.Level.Verbose, String.Format(System.Globalization.CultureInfo.InvariantCulture, "Style.InstantiateSubTree for {0} {1}", container.GetType().Name, label)); } } // No template was supplied. Allow subclasses to provide the template. // This is currently only implemented for FrameworkElement's. else { if (feContainer != null) { // This template will not be driven by the Style in any way. // Rather, it will be built and initialized by the callee // CALLBACK #if DEBUG Debug.Assert( feContainer._buildVisualTreeVerification == VerificationState.WaitingForBuildVisualTree, "The BuildVisualTree override has triggered another call to itself. This is not good - something between the two Style.InstantiateSubTree calls on the stack is doing A Very Bad Thing."); feContainer._buildVisualTreeVerification = VerificationState.WaitingForAddCustomTemplateRoot; bool exceptionThrown = true; try { #endif Debug.Assert(frameworkTemplate != null, "Only FrameworkTemplate has the ability to build a VisualTree by this means"); visualsCreated = frameworkTemplate.BuildVisualTree(feContainer); #if DEBUG exceptionThrown = false; } finally { if (!exceptionThrown) // results are unreliable if an exception was thrown { if( visualsCreated ) { Debug.Assert( feContainer._buildVisualTreeVerification == VerificationState.WaitingForBuildVisualTreeCompletion, "A derived class overriding BuildVisualTree must call AddCustomTemplateRoot to attach its custom subtree before exiting."); } else { Debug.Assert( feContainer._buildVisualTreeVerification == VerificationState.WaitingForAddCustomTemplateRoot, "If a derived class overriding BuildVisualTree has called AddCustomTemplateRoot to attach its custom subtree, its BuildVisualTree must return true to indicate that it has done so."); } } // All done building this visual tree, stand by for the next one. feContainer._buildVisualTreeVerification = VerificationState.WaitingForBuildVisualTree; } #endif } } return visualsCreated; }
// // This method // 1. Is called when a style/template is removed from a container. // 2. It is meant to release its data trigger information. // private static void ReleaseInstanceDataForDataTriggers( UncommonField<HybridDictionary[]> dataField, HybridDictionary instanceValues, Style oldStyle, FrameworkTemplate oldFrameworkTemplate) { Debug.Assert(oldStyle != null || oldFrameworkTemplate != null ); if (instanceValues == null) return; // the event handler depends only on whether the instance data // applies to Style, Template, or ThemeStyle EventHandler<BindingValueChangedEventArgs> handler; if (dataField == StyleDataField) { handler = new EventHandler<BindingValueChangedEventArgs>(OnBindingValueInStyleChanged); } else if (dataField == TemplateDataField) { handler = new EventHandler<BindingValueChangedEventArgs>(OnBindingValueInTemplateChanged); } else { Debug.Assert(dataField == ThemeStyleDataField); handler = new EventHandler<BindingValueChangedEventArgs>(OnBindingValueInThemeStyleChanged); } // clean up triggers with setters HybridDictionary dataTriggerRecordFromBinding = null; if (oldStyle != null) { dataTriggerRecordFromBinding = oldStyle._dataTriggerRecordFromBinding; } else if (oldFrameworkTemplate != null) { dataTriggerRecordFromBinding = oldFrameworkTemplate._dataTriggerRecordFromBinding; } if (dataTriggerRecordFromBinding != null) { foreach (object o in dataTriggerRecordFromBinding.Keys) { BindingBase binding = (BindingBase)o; ReleaseInstanceDataForTriggerBinding(binding, instanceValues, handler); } } // clean up triggers with actions HybridDictionary dataTriggersWithActions = null; if (oldStyle != null) { dataTriggersWithActions = oldStyle.DataTriggersWithActions; } else if (oldFrameworkTemplate != null) { dataTriggersWithActions = oldFrameworkTemplate.DataTriggersWithActions; } if (dataTriggersWithActions != null) { foreach (object o in dataTriggersWithActions.Keys) { BindingBase binding = (BindingBase)o; ReleaseInstanceDataForTriggerBinding(binding, instanceValues, handler); } } }
// // This method // 1. Ensures that the desired per-instance storage // for Style/Template exists // 2. Also allows you to specify initial capacity // internal static HybridDictionary EnsureInstanceData( UncommonField<HybridDictionary[]> dataField, DependencyObject container, InstanceStyleData dataType, int initialSize) { Debug.Assert((container is FrameworkElement) || (container is FrameworkContentElement), "Caller has queried with non-framework element. Bad caller, bad!"); Debug.Assert(dataType < InstanceStyleData.ArraySize, "Caller has queried using a value outside the range of the Enum. Bad caller, bad!"); HybridDictionary[] data = dataField.GetValue(container); if (data == null) { data = new HybridDictionary[(int)InstanceStyleData.ArraySize]; dataField.SetValue(container, data); } if (data[(int)dataType] == null ) { if( initialSize < 0 ) { data[(int)dataType] = new HybridDictionary(); } else { data[(int)dataType] = new HybridDictionary(initialSize); } } return (HybridDictionary)data[(int)dataType]; }
// // This method // 1. Ensures that the desired per-instance storage // for Style/Template exists // internal static HybridDictionary EnsureInstanceData( UncommonField<HybridDictionary[]> dataField, DependencyObject container, InstanceStyleData dataType) { return EnsureInstanceData(dataField, container, dataType, -1); }
// // This method // 1. Is called whenever a new Style/Template is upapplied from an FE/FCE // 2. It removes per-instance StyleData/TemplateData for the old Style/Template // internal static void ReleaseInstanceData( UncommonField<HybridDictionary[]> dataField, DependencyObject container, FrameworkElement fe, FrameworkContentElement fce, Style oldStyle, FrameworkTemplate oldFrameworkTemplate, InternalFlags hasGeneratedSubTreeFlag) { Debug.Assert((fe != null && fce == null) || (fe == null && fce != null)); Debug.Assert((fe != null && fe == container) || (fce != null && fce == container)); Debug.Assert(oldStyle != null || oldFrameworkTemplate != null ); // Fetch the per-instance data field value HybridDictionary[] styleData = dataField.GetValue(container); if (oldStyle != null) { HybridDictionary instanceValues = (styleData != null) ? styleData[(int)InstanceStyleData.InstanceValues] : null; ReleaseInstanceDataForDataTriggers(dataField, instanceValues, oldStyle, oldFrameworkTemplate ); if (oldStyle.HasInstanceValues) { StyleHelper.ProcessInstanceValuesForChild( container, container, 0, instanceValues, false, ref oldStyle.ChildRecordFromChildIndex); } } else if (oldFrameworkTemplate != null) { HybridDictionary instanceValues = (styleData != null) ? styleData[(int)InstanceStyleData.InstanceValues] : null; ReleaseInstanceDataForDataTriggers(dataField, instanceValues, oldStyle, oldFrameworkTemplate ); if (oldFrameworkTemplate.HasInstanceValues) { StyleHelper.ProcessInstanceValuesForChild( container, container, 0, instanceValues, false, ref oldFrameworkTemplate.ChildRecordFromChildIndex); } } else { HybridDictionary instanceValues = (styleData != null) ? styleData[(int)InstanceStyleData.InstanceValues] : null; ReleaseInstanceDataForDataTriggers(dataField, instanceValues, oldStyle, oldFrameworkTemplate ); } }
// // This method // 1. Adds the new TemplateNode's information to the container's per-instance // StyleData/TemplateData. (This only makes sense for children created via // FrameworkElementFactory. Children acquired via BuildVisualTree don't use // any property-related funtionality of the Style/Template.) // internal static void CreateInstanceDataForChild( UncommonField<HybridDictionary[]> dataField, DependencyObject container, DependencyObject child, int childIndex, bool hasInstanceValues, ref FrugalStructList<ChildRecord> childRecordFromChildIndex) { if (hasInstanceValues) { HybridDictionary instanceValues = EnsureInstanceData(dataField, container, InstanceStyleData.InstanceValues); StyleHelper.ProcessInstanceValuesForChild( container, child, childIndex, instanceValues, true, ref childRecordFromChildIndex); } }
// // This method // 1. Computes the property value given the ChildLookupValue list for it // private static object GetChildValueHelper( UncommonField<HybridDictionary[]> dataField, ref ItemStructList<ChildValueLookup> valueLookupList, DependencyProperty dp, DependencyObject container, FrameworkObject child, int childIndex, bool styleLookup, ref EffectiveValueEntry entry, out ValueLookupType sourceType, FrameworkElementFactory templateRoot) { Debug.Assert(child.IsValid, "child should either be an FE or an FCE"); object value = DependencyProperty.UnsetValue; sourceType = ValueLookupType.Simple; // Walk list backwards since highest priority lookup items are inserted last for (int i = valueLookupList.Count - 1; i >= 0; i--) { sourceType = valueLookupList.List[i].LookupType; // Lookup logic is determined by lookup type. "Trigger" // is misleading right now because today it's also being used // for Storyboard timeline lookups. switch (valueLookupList.List[i].LookupType) { case ValueLookupType.Simple: { // Simple value value = valueLookupList.List[i].Value; } break; case ValueLookupType.Trigger: case ValueLookupType.PropertyTriggerResource: case ValueLookupType.DataTrigger: case ValueLookupType.DataTriggerResource: { // Conditional value based on Container state bool triggerMatch = true; if( valueLookupList.List[i].Conditions != null ) { // Check whether the trigger applies. All conditions must match, // so the loop can terminate as soon as it finds a condition // that doesn't match. for (int j = 0; triggerMatch && j < valueLookupList.List[i].Conditions.Length; j++) { object state; switch (valueLookupList.List[i].LookupType) { case ValueLookupType.Trigger: case ValueLookupType.PropertyTriggerResource: // Find the source node DependencyObject sourceNode; int sourceChildIndex = valueLookupList.List[i].Conditions[j].SourceChildIndex; if (sourceChildIndex == 0) { sourceNode = container; } else { sourceNode = StyleHelper.GetChild(container, sourceChildIndex); } // Note that the sourceNode could be null when the source // property for this trigger is on a node that hasn't been // instantiated yet. DependencyProperty sourceProperty = valueLookupList.List[i].Conditions[j].Property; if (sourceNode != null) { state = sourceNode.GetValue(sourceProperty); } else { Type sourceNodeType; if( templateRoot != null ) { sourceNodeType = FindFEF(templateRoot, sourceChildIndex).Type; } else { sourceNodeType = (container as FrameworkElement).TemplateInternal.ChildTypeFromChildIndex[sourceChildIndex]; } state = sourceProperty.GetDefaultValue(sourceNodeType); } triggerMatch = valueLookupList.List[i].Conditions[j].Match(state); break; case ValueLookupType.DataTrigger: case ValueLookupType.DataTriggerResource: default: // this cannot happen - but make the compiler happy state = GetDataTriggerValue(dataField, container, valueLookupList.List[i].Conditions[j].Binding); triggerMatch = valueLookupList.List[i].Conditions[j].ConvertAndMatch(state); break; } } } if (triggerMatch) { // Conditionals matched, use the value if (valueLookupList.List[i].LookupType == ValueLookupType.PropertyTriggerResource || valueLookupList.List[i].LookupType == ValueLookupType.DataTriggerResource) { // Resource lookup object source; value = FrameworkElement.FindResourceInternal(child.FE, child.FCE, dp, valueLookupList.List[i].Value, // resourceKey null, // unlinkedParent true, // allowDeferredResourceReference false, // mustReturnDeferredResourceReference null, // boundaryElement false, // disableThrowOnResourceNotFound out source); // Try to freeze the value SealIfSealable(value); } else { value = valueLookupList.List[i].Value; } } } break; case ValueLookupType.TemplateBinding: { TemplateBindingExtension templateBinding = (TemplateBindingExtension)valueLookupList.List[i].Value; DependencyProperty sourceProperty = templateBinding.Property; // Direct binding of Child property to Container value = container.GetValue(sourceProperty); // Apply the converter, if any if (templateBinding.Converter != null) { DependencyProperty targetProperty = valueLookupList.List[i].Property; System.Globalization.CultureInfo culture = child.Language.GetCompatibleCulture(); value = templateBinding.Converter.Convert( value, targetProperty.PropertyType, templateBinding.ConverterParameter, culture); } // if the binding returns an invalid value, fallback to default value if ((value != DependencyProperty.UnsetValue) && !dp.IsValidValue(value)) { value = DependencyProperty.UnsetValue; } } break; case ValueLookupType.Resource: { // Resource lookup object source; value = FrameworkElement.FindResourceInternal( child.FE, child.FCE, dp, valueLookupList.List[i].Value, // resourceKey null, // unlinkedParent true, // allowDeferredResourceReference false, // mustReturnDeferredResourceReference null, // boundaryElement false, // disableThrowOnResourceNotFound out source); // Try to freeze the value SealIfSealable(value); } break; } // See if value needs per-instance storage if (value != DependencyProperty.UnsetValue) { entry.Value = value; // When the value requires per-instance storage (and comes from this style), // get the real value from per-instance data. switch (valueLookupList.List[i].LookupType) { case ValueLookupType.Simple: case ValueLookupType.Trigger: case ValueLookupType.DataTrigger: { MarkupExtension me; Freezable freezable; if ((me = value as MarkupExtension) != null) { value = GetInstanceValue( dataField, container, child.FE, child.FCE, childIndex, valueLookupList.List[i].Property, i, ref entry); } else if ((freezable = value as Freezable) != null && !freezable.IsFrozen) { value = GetInstanceValue( dataField, container, child.FE, child.FCE, childIndex, valueLookupList.List[i].Property, i, ref entry); } } break; default: break; } } if (value != DependencyProperty.UnsetValue) { // Found a value, break out of the for() loop. break; } } return value; }
// Given a Style/Template and a Binding whose value has changed, look for // any data triggers that have trigger actions (EnterAction/ExitAction) // and see if any of those actions need to run as a response to this change. private static void InvokeApplicableDataTriggerActions( Style style, FrameworkTemplate frameworkTemplate, DependencyObject container, BindingBase binding, BindingValueChangedEventArgs e, UncommonField<HybridDictionary[]> dataField) { HybridDictionary dataTriggersWithActions; if( style != null ) { dataTriggersWithActions = style.DataTriggersWithActions; } else if( frameworkTemplate != null ) { dataTriggersWithActions = frameworkTemplate.DataTriggersWithActions; } else { dataTriggersWithActions = null; } if( dataTriggersWithActions != null ) { object candidateTrigger = dataTriggersWithActions[binding]; if( candidateTrigger != null ) { // 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 ) { InvokeDataTriggerActions( triggerBase, container, binding, e, style, frameworkTemplate, dataField); } else { Debug.Assert(candidateTrigger is List<TriggerBase>, "Internal data structure error: The HybridDictionary [Style/Template].DataTriggersWithActions " + "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++ ) { InvokeDataTriggerActions( triggerList[i], container, binding, e, style, frameworkTemplate, dataField); } } } } }
// // This method // 1. Retrieves a value from a binding in the condition of a data trigger // internal static object GetDataTriggerValue( UncommonField<HybridDictionary[]> dataField, DependencyObject container, BindingBase binding) { // get the container's instance value list - the bindings are stored there HybridDictionary[] data = dataField.GetValue(container); HybridDictionary instanceValues = EnsureInstanceData(dataField, container, InstanceStyleData.InstanceValues); // get the binding, creating it if necessary BindingExpressionBase bindingExpr = (BindingExpressionBase)instanceValues[binding]; if (bindingExpr == null) { bindingExpr = BindingExpression.CreateUntargetedBindingExpression(container, binding); instanceValues[binding] = bindingExpr; if (dataField == StyleDataField) { bindingExpr.ValueChanged += new EventHandler<BindingValueChangedEventArgs>(OnBindingValueInStyleChanged); } else if (dataField == TemplateDataField) { bindingExpr.ResolveNamesInTemplate = true; bindingExpr.ValueChanged += new EventHandler<BindingValueChangedEventArgs>(OnBindingValueInTemplateChanged); } else { Debug.Assert(dataField == ThemeStyleDataField); bindingExpr.ValueChanged += new EventHandler<BindingValueChangedEventArgs>(OnBindingValueInThemeStyleChanged); } bindingExpr.Attach(container); } // get the value return bindingExpr.Value; }
// evaluate the current state of the trigger internal override bool GetCurrentState(DependencyObject container, UncommonField<HybridDictionary[]> dataField) { Debug.Assert( TriggerConditions != null && TriggerConditions.Length == 1, "This method assumes there is exactly one TriggerCondition." ); Debug.Assert( TriggerConditions[0].SourceChildIndex == 0, "This method was created to handle properties on the containing object, more work is needed to handle templated children too." ); return TriggerConditions[0].Match(container.GetValue(TriggerConditions[0].Property)); }
// // This method // 1. Retrieves an instance value from per-instance StyleData. // 2. Creates the StyleData if this is the first request. // internal static object GetInstanceValue( UncommonField<HybridDictionary []> dataField, DependencyObject container, FrameworkElement feChild, FrameworkContentElement fceChild, int childIndex, DependencyProperty dp, int i, ref EffectiveValueEntry entry) { object rawValue = entry.Value; DependencyObject child = null; FrameworkElement feContainer; FrameworkContentElement fceContainer; Helper.DowncastToFEorFCE(container, out feContainer, out fceContainer, true); HybridDictionary[] styleData = (dataField != null) ? dataField.GetValue(container) : null; HybridDictionary instanceValues = (styleData != null) ? styleData[(int)InstanceStyleData.InstanceValues] : null; InstanceValueKey key = new InstanceValueKey(childIndex, dp.GlobalIndex, i); object value = (instanceValues != null)? instanceValues[key] : null; bool isRequestingExpression = (feChild != null) ? feChild.IsRequestingExpression : fceChild.IsRequestingExpression; if (value == null) { value = NotYetApplied; } // if the value is a detached expression, replace it with a new one Expression expr = value as Expression; if (expr != null && expr.HasBeenDetached) { value = NotYetApplied; } // if this is the first request, create the value if (value == NotYetApplied) { child = feChild; if (child == null) child = fceChild; MarkupExtension me; Freezable freezable; if ((me = rawValue as MarkupExtension) != null) { // exception: if the child is not yet initialized and the request // is for an expression, don't create the value. This gives the parser // a chance to set local values, to override the style-defined values. if (isRequestingExpression) { bool isInitialized = (feChild != null) ? feChild.IsInitialized : fceChild.IsInitialized; if (!isInitialized) { return DependencyProperty.UnsetValue; } } ProvideValueServiceProvider provideValueServiceProvider = new ProvideValueServiceProvider(); provideValueServiceProvider.SetData( child, dp ); value = me.ProvideValue(provideValueServiceProvider); } else if ((freezable = rawValue as Freezable) != null) { value = freezable.Clone(); child.ProvideSelfAsInheritanceContext(value, dp); } // store it in per-instance StyleData (even if it's DependencyProperty.UnsetValue) Debug.Assert(value != NotYetApplied, "attempt to retrieve instance value that was never set"); instanceValues[key] = value; if (value != DependencyProperty.UnsetValue) { expr = value as Expression; // if the instance value is an expression, attach it if (expr != null) { expr.OnAttach(child, dp); } } } // if the value is an Expression (and we're being asked for the real value), // delegate to the expression. if (expr != null) { if (!isRequestingExpression) { if (child == null) { child = feChild; if (child == null) child = fceChild; } entry.ResetValue(DependencyObject.ExpressionInAlternativeStore, true); entry.SetExpressionValue(expr.GetValue(child, dp), DependencyObject.ExpressionInAlternativeStore); } else { entry.Value = value; } } else { entry.Value = value; } return value; }
// =========================================================================== // These methods are invoked to during a call call to // FE.EnsureVisual or FCE.EnsureLogical // =========================================================================== #region InstantiateSubTree // // This method // Creates the VisualTree // //[CodeAnalysis("AptcaMethodsShouldOnlyCallAptcaMethods")] //Tracking Bug: 29647 internal bool ApplyTemplateContent( UncommonField<HybridDictionary[]> templateDataField, FrameworkElement container) { #if STYLE_TRACE _timer.Begin(); #endif if (TraceDependencyProperty.IsEnabled) { TraceDependencyProperty.Trace( TraceEventType.Start, TraceDependencyProperty.ApplyTemplateContent, container, this); } ValidateTemplatedParent(container); bool visualsCreated = StyleHelper.ApplyTemplateContent(templateDataField, container, _templateRoot, _lastChildIndex, ChildIndexFromChildName, this); if (TraceDependencyProperty.IsEnabled) { TraceDependencyProperty.Trace( TraceEventType.Stop, TraceDependencyProperty.ApplyTemplateContent, container, this); } #if STYLE_TRACE _timer.End(); if (visualsCreated) { string label = container.ID; if (label == null || label.Length == 0) label = container.GetHashCode().ToString(); Console.WriteLine(" Style.VT created for {0} {1} in {2:f2} msec", container.GetType().Name, label, _timer.TimeOfLastPeriod); } #endif return visualsCreated; }
// This is expected to be called when a Binding for a DataTrigger has // changed and we need to look for true/false transitions. If found, // invoke EnterAction (or ExitAction) as needed. private static void InvokeDataTriggerActions( TriggerBase triggerBase, DependencyObject triggerContainer, BindingBase binding, BindingValueChangedEventArgs bindingChangedArgs, Style style, FrameworkTemplate frameworkTemplate, UncommonField<HybridDictionary[]> dataField) { bool oldState; bool newState; DataTrigger dataTrigger = triggerBase as DataTrigger; if( dataTrigger != null ) { EvaluateOldNewStates( dataTrigger, triggerContainer, binding, bindingChangedArgs, dataField, style, frameworkTemplate, out oldState, out newState ); } else { Debug.Assert( triggerBase is MultiDataTrigger, "This method only handles DataTrigger and MultiDataTrigger. Other types should have been handled elsewhere." ); EvaluateOldNewStates( (MultiDataTrigger)triggerBase, triggerContainer, binding, bindingChangedArgs, dataField, style, frameworkTemplate, out oldState, out newState ); } InvokeEnterOrExitActions( triggerBase, oldState, newState, triggerContainer, style, frameworkTemplate ); }
// evaluate the current state of the trigger internal virtual bool GetCurrentState(DependencyObject container, UncommonField<HybridDictionary[]> dataField) { Debug.Assert( false, "This method was written to handle Trigger, MultiTrigger, DataTrigger, and MultiDataTrigger. It looks like a new trigger type was added - please add support as appropriate."); return false; }
// Called from UpdateStyleCache - When an object's Style changes, some of // the triggers contain EnterActions and they want to be run immediately // if the trigger condition evaluates to true. // Usually these EnterActions are to be run when there's a False->True // transition. This code treats "true at time Style is applied" as // a False->True transition even though it's possible no transition ever // took place. private static void ExecuteOnApplyEnterExitActions( FrameworkElement fe, FrameworkContentElement fce, Style style, UncommonField<HybridDictionary[]> dataField ) { // If the "Style Change" is the style being set to null - exit. if( style == null ) { return; } // Note: PropertyTriggersWithActions is a FrugalMap, so its count is checked against zero. // DataTriggersWithActions is a HybridDictionary allocated on demand, so it's checked against null. if( style.PropertyTriggersWithActions.Count == 0 && style.DataTriggersWithActions == null ) { // Style has no trigger actions at all, exit. return; } TriggerCollection triggers = style.Triggers; DependencyObject triggerContainer = (fe != null) ? (DependencyObject)fe : (DependencyObject)fce; ExecuteOnApplyEnterExitActionsLoop( triggerContainer, triggers, style, null, dataField ); }
// evaluate the current state of the trigger internal override bool GetCurrentState(DependencyObject container, UncommonField<HybridDictionary[]> dataField) { bool retVal = (TriggerConditions.Length > 0); for( int i = 0; retVal && i < TriggerConditions.Length; i++ ) { retVal = TriggerConditions[i].ConvertAndMatch(StyleHelper.GetDataTriggerValue(dataField, container, TriggerConditions[i].Binding)); } return retVal; }
// =========================================================================== // These methods are invoked when a visual tree is // being created/destroyed via a Style/Template and // when a Style/Template is being applied or // unapplied to a FE/FCE // =========================================================================== #region WriteInstanceData // // This method // 1. Is called whenever a Style/Template is [un]applied to an FE/FCE // 2. It updates the per-instance StyleData/TemplateData // internal static void UpdateInstanceData( UncommonField<HybridDictionary[]> dataField, FrameworkElement fe, FrameworkContentElement fce, Style oldStyle, Style newStyle, FrameworkTemplate oldFrameworkTemplate, FrameworkTemplate newFrameworkTemplate, InternalFlags hasGeneratedSubTreeFlag) { Debug.Assert((fe != null && fce == null) || (fe == null && fce != null)); DependencyObject container = (fe != null) ? (DependencyObject)fe : (DependencyObject)fce; if (oldStyle != null || oldFrameworkTemplate != null ) { ReleaseInstanceData(dataField, container, fe, fce, oldStyle, oldFrameworkTemplate, hasGeneratedSubTreeFlag); } if (newStyle != null || newFrameworkTemplate != null ) { CreateInstanceData(dataField, container, fe, fce, newStyle, newFrameworkTemplate ); } else { dataField.ClearValue(container); } }