Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Framework / System / Windows / BroadcastEventHelper.cs / 1 / BroadcastEventHelper.cs
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Windows; using System.Windows.Media; using System.Windows.Media.Media3D; using System.Windows.Threading; using System.Windows.Documents; using MS.Internal; using MS.Internal.PresentationFramework; // SafeSecurityHelper namespace System.Windows { internal static class BroadcastEventHelper { ////// Add the loaded callback to the MediaContext queue /// internal static void AddLoadedCallback(DependencyObject d, DependencyObject logicalParent) { Debug.Assert(d is FrameworkElement || d is FrameworkContentElement); DispatcherOperationCallback loadedCallback = new DispatcherOperationCallback(BroadcastEventHelper.BroadcastLoadedEvent); // Add the pending loaded event information to the MediaContext's pending // LoadedOrUnloadedCallbacks list so these can be called pre render LoadedOrUnloadedOperation loadedOp = MediaContext.From(d.Dispatcher).AddLoadedOrUnloadedCallback(loadedCallback, d); // Post to the dispatcher queue as a backup to fire the broadcast // event in case the tree change never triggers a Layout DispatcherOperation operation = d.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, loadedCallback, d); // Set the LoadedPending property d.SetValue(FrameworkElement.LoadedPendingPropertyKey, new object[]{loadedOp, operation, logicalParent}); } ////// Remove the loaded callback from the MediaContext queue /// internal static void RemoveLoadedCallback(DependencyObject d, object[] loadedPending) { Debug.Assert(d is FrameworkElement || d is FrameworkContentElement); if (loadedPending != null) { Debug.Assert(loadedPending.Length == 3); // Clear the LoadedPending property d.ClearValue(FrameworkElement.LoadedPendingPropertyKey); // If the dispatcher operation is pending abort it DispatcherOperation operation = (DispatcherOperation)loadedPending[1]; if (operation.Status == DispatcherOperationStatus.Pending) { operation.Abort(); } // Remove the pending loaded information from the MediaContext's pending // LoadedOrUnloadedCallbacks list MediaContext.From(d.Dispatcher).RemoveLoadedOrUnloadedCallback((LoadedOrUnloadedOperation)loadedPending[0]); } } ////// Add the unloaded callback to the MediaContext queue /// internal static void AddUnloadedCallback(DependencyObject d, DependencyObject logicalParent) { Debug.Assert(d is FrameworkElement || d is FrameworkContentElement); DispatcherOperationCallback unloadedCallback = new DispatcherOperationCallback(BroadcastEventHelper.BroadcastUnloadedEvent); // Add the pending unloaded event information to the MediaContext's pending // LoadedOrUnloadedCallbacks list so these can be called pre render LoadedOrUnloadedOperation unloadedOp = MediaContext.From(d.Dispatcher).AddLoadedOrUnloadedCallback(unloadedCallback, d); // Post to the dispatcher queue as a backup to fire the broadcast // event in case the tree change never triggers a Layout DispatcherOperation operation = d.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, unloadedCallback, d); // Set the UnloadedPending property d.SetValue(FrameworkElement.UnloadedPendingPropertyKey, new object[]{unloadedOp, operation, logicalParent}); } ////// Remove the unloaded callback from the MediaContext queue /// internal static void RemoveUnloadedCallback(DependencyObject d, object[] unloadedPending) { Debug.Assert(d is FrameworkElement || d is FrameworkContentElement); if (unloadedPending != null) { Debug.Assert(unloadedPending.Length == 3); // Clear the UnloadedPending property d.ClearValue(FrameworkElement.UnloadedPendingPropertyKey); // If the dispatcher operation is pending abort it DispatcherOperation operation = (DispatcherOperation)unloadedPending[1]; if (operation.Status == DispatcherOperationStatus.Pending) { operation.Abort(); } // Remove the pending unloaded information from the MediaContext's pending // LoadedOrUnloadedCallbacks list MediaContext.From(d.Dispatcher).RemoveLoadedOrUnloadedCallback((LoadedOrUnloadedOperation)unloadedPending[0]); } } ////// Fire the [Loaded/Unloaded] broadcast events based upon the old and new parent values. /// This method is called from ChangeLogicalParent() and OnVisualParentChanged(). /// /// /// Node to begin the broadcast /// /// /// Old Parent /// /// /// New Parent /// internal static void BroadcastLoadedOrUnloadedEvent( DependencyObject d, DependencyObject oldParent, DependencyObject newParent) { // Added to a tree if (oldParent == null && newParent != null) { if(IsLoadedHelper(newParent) == true) { // Broadcast Loaded event if your new parent is loaded // Note that this broadcast will take place when you are // attached to your loaded visual parent FireLoadedOnDescendentsHelper(d); } } // Removed from a tree else if (oldParent != null && newParent == null) { if (IsLoadedHelper(oldParent) == true) { // Broadcast Unloaded event if your old parent was loaded // Note that this broadcast will take place when you are // detached from your loaded visual parent FireUnloadedOnDescendentsHelper(d); } } } ////// Broadcast the Loaded event when UI is rendered and ready for user interaction. /// /// /// Root of the sub-tree that the broadcast will start at /// internal static object BroadcastLoadedEvent(object root) { DependencyObject rootDO = (DependencyObject)root; object[] loadedPending = (object[])rootDO.GetValue(FrameworkElement.LoadedPendingProperty); // The LoadedPendingProperty must be set if we have reached this far Debug.Assert(loadedPending != null && loadedPending.Length == 3, "The LoadedPendingProperty must be set if we have reached this far"); bool isLoaded = IsLoadedHelper(rootDO); // Remove the Loaded callback from the MediaContext's queue RemoveLoadedCallback(rootDO, loadedPending); BroadcastLoadedSynchronously(rootDO, isLoaded); return null; } internal static void BroadcastLoadedSynchronously(DependencyObject rootDO, bool isLoaded) { // It is possible that the loaded broadcast for a parent caused you to be loaded before // your broadcast item got dequeued. In that case simply ignore the operation if (!isLoaded) { // Broadcast the Loaded event BroadcastEventHelper.BroadcastEvent(rootDO, FrameworkElement.LoadedEvent); } } ////// Broadcast the Unloaded event when the element is detached from a Loaded Tree /// /// /// Root of the sub-tree that the broadcast will start at /// internal static object BroadcastUnloadedEvent(object root) { DependencyObject rootDO = (DependencyObject)root; object[] unloadedPending = (object[])rootDO.GetValue(FrameworkElement.UnloadedPendingProperty); // The UnloadedPendingProperty must be set if we have reached this far Debug.Assert(unloadedPending != null && unloadedPending.Length == 3, "The UnloadedPendingProperty must be set if we have reached this far"); bool isLoaded = IsLoadedHelper(rootDO); // Remove the Unloaded callback from the MediaContext's queue RemoveUnloadedCallback(rootDO, unloadedPending); BroadcastUnloadedSynchronously(rootDO, isLoaded); return null; } internal static void BroadcastUnloadedSynchronously(DependencyObject rootDO, bool isLoaded) { // It is possible that the unloaded broadcast for a parent caused you to be unloaded before // your broadcast item got dequeued. In that case simply ignore the operation if (isLoaded) { // Broadcast the Unloaded event BroadcastEvent(rootDO, FrameworkElement.UnloadedEvent); } } private static VisitedCallbackBroadcastDelegate = new VisitedCallback (OnBroadcastCallback); private struct BroadcastEventData { internal BroadcastEventData(DependencyObject root, RoutedEvent routedEvent, List eventRoute) { Root = root; RoutedEvent = routedEvent; EventRoute = eventRoute; } internal DependencyObject Root; internal RoutedEvent RoutedEvent; internal List EventRoute; } /// /// Broadcast the Loaded/Unloaded event in the sub-tree starting at the given root /// /// /// Root of the sub-tree that the event will be broadcast to /// /// /// RoutedEventID for the event we wish to broadcast /// private static void BroadcastEvent(DependencyObject root, RoutedEvent routedEvent) { // Broadcast to the tree and collect the set of nodes // on which we need fire the Loaded event ListeventRoute = new List (); // Create a DescendentsWalker for the broadcast DescendentsWalker walker = new DescendentsWalker ( TreeWalkPriority.VisualTree, BroadcastDelegate, new BroadcastEventData(root, routedEvent, eventRoute)); // Start the walk down walker.StartWalk(root); // Iterate and raise the event on each of the nodes in the tree for (int i=0; i< eventRoute.Count; i++) { DependencyObject d = eventRoute[i]; RoutedEventArgs args = new RoutedEventArgs(routedEvent, d); FrameworkObject fo = new FrameworkObject(d, true /*throwIfNeither*/); if (routedEvent == FrameworkElement.LoadedEvent) { fo.OnLoaded(args); } else { fo.OnUnloaded(args); } } } // Callback on visiting each node in the descendency during a broadcast event private static bool OnBroadcastCallback(DependencyObject d, BroadcastEventData data) { DependencyObject root = data.Root; RoutedEvent routedEvent = data.RoutedEvent; List eventRoute = data.EventRoute; if (FrameworkElement.DType.IsInstanceOfType(d)) { // If this is a FrameworkElement FrameworkElement fe = (FrameworkElement)d; if (fe != root && routedEvent == FrameworkElement.LoadedEvent && fe.UnloadedPending != null) { // If there is a pending Unloaded event wait till we've broadcast // that event before we can fire the new Loaded event. fe.FireLoadedOnDescendentsInternal(); } else if (fe != root && routedEvent == FrameworkElement.UnloadedEvent && fe.LoadedPending != null) { // If there is a pending Loaded event abort it because we are now // being Unloaded. RemoveLoadedCallback(fe, fe.LoadedPending); } else { // If element has handlers fire the event and continue to walk down the tree if (fe.SubtreeHasLoadedChangeHandler) { // We cannot assert this condition here for the following reason. // If the [Un]LoadedHandler is added to the current node after the parent // for this node has been [Un]Loaded but before the current node has been [Un]Loaded // (example: within the [Un]Loaded handler for the parent), then the IsLoaded // cache on the current node has been updated to match that of the parent, // and this Assert will be violated. See BroadcastEventHelper.UpdateHasHandlerFlag // for further description. // Debug.Assert(IsLoaded == [false/true], // "Element should have been [Un]loaded before it is [Un]Loaded back again"); fe.IsLoadedCache = (routedEvent == FrameworkElement.LoadedEvent); eventRoute.Add(fe); // Continue walk down subtree return true; } } } else { // If this is a FrameworkContentElement FrameworkContentElement fce = (FrameworkContentElement)d; if (fce != root && routedEvent == FrameworkElement.LoadedEvent && fce.UnloadedPending != null) { // If there is a pending Unloaded event wait till we've broadcast // that event before we can fire the new Loaded event. fce.FireLoadedOnDescendentsInternal(); } else if (fce != root && routedEvent == FrameworkElement.UnloadedEvent && fce.LoadedPending != null) { // If there is a pending Loaded event abort it because we are now // being Unloaded. RemoveLoadedCallback(fce, fce.LoadedPending); } else { // If element has handlers fire the event and continue to walk down the tree if (fce.SubtreeHasLoadedChangeHandler) { // We cannot assert this condition here for the following reason. // If the [Un]LoadedHandler is added to the current node after the parent // for this node has been [Un]Loaded but before the current node has been [Un]Loaded // (example: within the [Un]Loaded handler for the parent), then the IsLoaded // cache on the current node has been updated to match that of the parent, // and this Assert will be violated. See BroadcastEventHelper.UpdateHasHandlerFlag // for further description. // Debug.Assert(IsLoaded == [false/true], // "Element should have been [Un]loaded before it is [Un]Loaded back again"); fce.IsLoadedCache = (routedEvent == FrameworkElement.LoadedEvent); eventRoute.Add(fce); // Continue walk down subtree return true; } } } // Stop walk down subtree return false; } private static bool SubtreeHasLoadedChangeHandlerHelper(DependencyObject d) { if (FrameworkElement.DType.IsInstanceOfType(d)) { return ((FrameworkElement)d).SubtreeHasLoadedChangeHandler; } else if (FrameworkContentElement.DType.IsInstanceOfType(d)) { return ((FrameworkContentElement)d).SubtreeHasLoadedChangeHandler; } return false; } private static void FireLoadedOnDescendentsHelper(DependencyObject d) { if (FrameworkElement.DType.IsInstanceOfType(d)) { ((FrameworkElement)d).FireLoadedOnDescendentsInternal(); } else { ((FrameworkContentElement)d).FireLoadedOnDescendentsInternal(); } } private static void FireUnloadedOnDescendentsHelper(DependencyObject d) { if (FrameworkElement.DType.IsInstanceOfType(d)) { ((FrameworkElement)d).FireUnloadedOnDescendentsInternal(); } else { ((FrameworkContentElement)d).FireUnloadedOnDescendentsInternal(); } } private static bool IsLoadedHelper(DependencyObject d) { FrameworkObject fo = new FrameworkObject(d); return fo.IsLoaded; } // Helper method that recursively queries the parents to see if they are loaded. // This method is invoked only when the loaded cache on the given node isn't valid. internal static bool IsParentLoaded(DependencyObject d) { FrameworkObject fo = new FrameworkObject(d); DependencyObject parent = fo.EffectiveParent; Visual visual; Visual3D visual3D; if (parent != null) { return IsLoadedHelper(parent); } else if ((visual = d as Visual) != null) { // If parent is null then this is the root element return SafeSecurityHelper.IsConnectedToPresentationSource(visual); } else if ((visual3D = d as Visual3D) != null) { // IsConnectedToPresentationSource could also be modified to take // a DO - instead though we'll just get the containing visual2D for // this 3D object. visual = VisualTreeHelper.GetContainingVisual2D(visual3D); if (visual != null) { return SafeSecurityHelper.IsConnectedToPresentationSource(visual); } else { return false; } } else return false; } /// /// Check if the Framework Element Factory that produced the Template /// that created this control has a Loaded Change Handler. /// /// /// The caller must pass in a non-null templatedParent. /// internal static FrameworkElementFactory GetFEFTreeRoot(DependencyObject templatedParent) { FrameworkObject fo = new FrameworkObject(templatedParent, true); Debug.Assert( fo.IsFE ); FrameworkTemplate templatedParentTemplate = fo.FE.TemplateInternal; FrameworkElementFactory fefTree = templatedParentTemplate.VisualTree; return fefTree; } ////// Update the Has[Loaded/UnLoaded]Handler flags if required. /// This method is called from OnNewParent/OnVisualParentChanged. /// /// /// Node to begin the update /// /// /// Old Parent /// /// /// New Parent /// internal static void AddOrRemoveHasLoadedChangeHandlerFlag( DependencyObject d, DependencyObject oldParent, DependencyObject newParent) { bool hasLoadChangedHandler = SubtreeHasLoadedChangeHandlerHelper(d); if(hasLoadChangedHandler) { // Attaching to a Parent if (oldParent == null && newParent != null) { // Subtree with a handler got added AddHasLoadedChangeHandlerFlagInAncestry(newParent); } // Detaching from a Parent else if (oldParent != null && newParent == null) { // Subtree with a handler got removed RemoveHasLoadedChangeHandlerFlagInAncestry(oldParent); } } } internal static void AddHasLoadedChangeHandlerFlagInAncestry(DependencyObject d) { UpdateHasLoadedChangeHandlerFlagInAncestry(d, true); } internal static void RemoveHasLoadedChangeHandlerFlagInAncestry(DependencyObject d) { UpdateHasLoadedChangeHandlerFlagInAncestry(d, false); } ////// Evaluate the HasLoadedChangeHandler flag on the given node by /// querying its children, and styles, and templates. /// /// /// Node /// private static bool AreThereLoadedChangeHandlersInSubtree(ref FrameworkObject fo) { // HasHandler flag can be evaluated only for a FE/FCE. if (!fo.IsValid) return false; if (fo.ThisHasLoadedChangeEventHandler) return true; if (fo.IsFE) { // Check if any of your visual children have the flag set Visual v = (Visual)fo.FE; int count = VisualTreeHelper.GetChildrenCount(v); for(int i = 0; i < count; i++) { FrameworkElement child = VisualTreeHelper.GetChild(v, i) as FrameworkElement; if (child != null && child.SubtreeHasLoadedChangeHandler) { return true; } } } // Check if any of your logical children have the flag set foreach(object o in LogicalTreeHelper.GetChildren(fo.DO)) { DependencyObject child = o as DependencyObject; if(null != child && SubtreeHasLoadedChangeHandlerHelper(child)) { return true; } } return false; } ////// This is a recursive function that walks up the tree Adding or Removing /// HasLoadedChangeHander bits. It also inits the IsLoadedCache on Add. /// /// /// Node to update /// /// /// Is it an AddHandler/ Add Child with Handler Operation /// private static void UpdateHasLoadedChangeHandlerFlagInAncestry(DependencyObject d, bool addHandler) { FrameworkObject fo = new FrameworkObject(d); if (!addHandler) { if ( AreThereLoadedChangeHandlersInSubtree(ref fo) ) return; // done } if (fo.IsValid) { if (fo.SubtreeHasLoadedChangeHandler != addHandler) { DependencyObject coreParent = (fo.IsFE) ? VisualTreeHelper.GetParent(fo.FE) : null; DependencyObject logicalParent = fo.Parent; DependencyObject parent = null; fo.SubtreeHasLoadedChangeHandler = addHandler; // Propagate the change to your visual ancestry if (coreParent != null) { UpdateHasLoadedChangeHandlerFlagInAncestry(coreParent, addHandler); parent = coreParent; } // Propagate the change to your logical ancestry if (logicalParent != null && logicalParent != coreParent) { UpdateHasLoadedChangeHandlerFlagInAncestry(logicalParent, addHandler); if (fo.IsFCE) parent = logicalParent; } // Propagate the change to your mentor, if any if (logicalParent == null && coreParent == null) { parent = Helper.FindMentor(fo.DO.InheritanceContext); if (parent != null) { fo.ChangeSubtreeHasLoadedChangedHandler(parent); } } if(addHandler) { // The HasLoadedChangeHandler flag is used for two purposes. // 1. To indicate that the sub-tree starting at the current node has // handlers for Loaded / Unloaded event. So broadcast logic // can walk down that path to fire the events. // 2. To indicate that the IsLoaded cache on the node is valid. // If we are adding a handler: // On the POP side of the recursion, as we come back down from the root, // pull the value of IsLoadedCache from the parent in to the child. if (fo.IsFE) { UpdateIsLoadedCache(fo.FE, parent); } else { UpdateIsLoadedCache(fo.FCE, parent); } } } } else // neither a FE or an FCE { DependencyObject coreParent = null; Visual v; Visual3D v3D; ContentElement ce; // This is neither an FE nor and FCE // Propagate the change to your visual ancestry if ((v = d as Visual) != null) { coreParent = VisualTreeHelper.GetParent(v); } else if ((ce = d as ContentElement) != null) { coreParent = ContentOperations.GetParent(ce); } else if ((v3D = d as Visual3D) != null) { coreParent = VisualTreeHelper.GetParent(v3D); } if (coreParent != null) { UpdateHasLoadedChangeHandlerFlagInAncestry(coreParent, addHandler); } } } ////// Updates the IsLoadedCache on the current FrameworkElement /// private static void UpdateIsLoadedCache( FrameworkElement fe, DependencyObject parent) { if (fe.GetValue(FrameworkElement.LoadedPendingProperty) == null) { // Propagate the change to your visual ancestry if (parent != null) { fe.IsLoadedCache = IsLoadedHelper(parent); } // This is the root visual. else if ( SafeSecurityHelper.IsConnectedToPresentationSource( fe )) { fe.IsLoadedCache = true; } else { fe.IsLoadedCache = false; } } else { // Clear the cache if Loaded is pending fe.IsLoadedCache = false; } } ////// Updates the IsLoadedCache on the current FrameworkContentElement /// private static void UpdateIsLoadedCache( FrameworkContentElement fce, DependencyObject parent) { if (fce.GetValue(FrameworkElement.LoadedPendingProperty) == null) { // Propagate the change to your logical ancestry fce.IsLoadedCache = IsLoadedHelper(parent); } else { // Clear the cache if Loaded is pending fce.IsLoadedCache = false; } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Windows; using System.Windows.Media; using System.Windows.Media.Media3D; using System.Windows.Threading; using System.Windows.Documents; using MS.Internal; using MS.Internal.PresentationFramework; // SafeSecurityHelper namespace System.Windows { internal static class BroadcastEventHelper { ////// Add the loaded callback to the MediaContext queue /// internal static void AddLoadedCallback(DependencyObject d, DependencyObject logicalParent) { Debug.Assert(d is FrameworkElement || d is FrameworkContentElement); DispatcherOperationCallback loadedCallback = new DispatcherOperationCallback(BroadcastEventHelper.BroadcastLoadedEvent); // Add the pending loaded event information to the MediaContext's pending // LoadedOrUnloadedCallbacks list so these can be called pre render LoadedOrUnloadedOperation loadedOp = MediaContext.From(d.Dispatcher).AddLoadedOrUnloadedCallback(loadedCallback, d); // Post to the dispatcher queue as a backup to fire the broadcast // event in case the tree change never triggers a Layout DispatcherOperation operation = d.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, loadedCallback, d); // Set the LoadedPending property d.SetValue(FrameworkElement.LoadedPendingPropertyKey, new object[]{loadedOp, operation, logicalParent}); } ////// Remove the loaded callback from the MediaContext queue /// internal static void RemoveLoadedCallback(DependencyObject d, object[] loadedPending) { Debug.Assert(d is FrameworkElement || d is FrameworkContentElement); if (loadedPending != null) { Debug.Assert(loadedPending.Length == 3); // Clear the LoadedPending property d.ClearValue(FrameworkElement.LoadedPendingPropertyKey); // If the dispatcher operation is pending abort it DispatcherOperation operation = (DispatcherOperation)loadedPending[1]; if (operation.Status == DispatcherOperationStatus.Pending) { operation.Abort(); } // Remove the pending loaded information from the MediaContext's pending // LoadedOrUnloadedCallbacks list MediaContext.From(d.Dispatcher).RemoveLoadedOrUnloadedCallback((LoadedOrUnloadedOperation)loadedPending[0]); } } ////// Add the unloaded callback to the MediaContext queue /// internal static void AddUnloadedCallback(DependencyObject d, DependencyObject logicalParent) { Debug.Assert(d is FrameworkElement || d is FrameworkContentElement); DispatcherOperationCallback unloadedCallback = new DispatcherOperationCallback(BroadcastEventHelper.BroadcastUnloadedEvent); // Add the pending unloaded event information to the MediaContext's pending // LoadedOrUnloadedCallbacks list so these can be called pre render LoadedOrUnloadedOperation unloadedOp = MediaContext.From(d.Dispatcher).AddLoadedOrUnloadedCallback(unloadedCallback, d); // Post to the dispatcher queue as a backup to fire the broadcast // event in case the tree change never triggers a Layout DispatcherOperation operation = d.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, unloadedCallback, d); // Set the UnloadedPending property d.SetValue(FrameworkElement.UnloadedPendingPropertyKey, new object[]{unloadedOp, operation, logicalParent}); } ////// Remove the unloaded callback from the MediaContext queue /// internal static void RemoveUnloadedCallback(DependencyObject d, object[] unloadedPending) { Debug.Assert(d is FrameworkElement || d is FrameworkContentElement); if (unloadedPending != null) { Debug.Assert(unloadedPending.Length == 3); // Clear the UnloadedPending property d.ClearValue(FrameworkElement.UnloadedPendingPropertyKey); // If the dispatcher operation is pending abort it DispatcherOperation operation = (DispatcherOperation)unloadedPending[1]; if (operation.Status == DispatcherOperationStatus.Pending) { operation.Abort(); } // Remove the pending unloaded information from the MediaContext's pending // LoadedOrUnloadedCallbacks list MediaContext.From(d.Dispatcher).RemoveLoadedOrUnloadedCallback((LoadedOrUnloadedOperation)unloadedPending[0]); } } ////// Fire the [Loaded/Unloaded] broadcast events based upon the old and new parent values. /// This method is called from ChangeLogicalParent() and OnVisualParentChanged(). /// /// /// Node to begin the broadcast /// /// /// Old Parent /// /// /// New Parent /// internal static void BroadcastLoadedOrUnloadedEvent( DependencyObject d, DependencyObject oldParent, DependencyObject newParent) { // Added to a tree if (oldParent == null && newParent != null) { if(IsLoadedHelper(newParent) == true) { // Broadcast Loaded event if your new parent is loaded // Note that this broadcast will take place when you are // attached to your loaded visual parent FireLoadedOnDescendentsHelper(d); } } // Removed from a tree else if (oldParent != null && newParent == null) { if (IsLoadedHelper(oldParent) == true) { // Broadcast Unloaded event if your old parent was loaded // Note that this broadcast will take place when you are // detached from your loaded visual parent FireUnloadedOnDescendentsHelper(d); } } } ////// Broadcast the Loaded event when UI is rendered and ready for user interaction. /// /// /// Root of the sub-tree that the broadcast will start at /// internal static object BroadcastLoadedEvent(object root) { DependencyObject rootDO = (DependencyObject)root; object[] loadedPending = (object[])rootDO.GetValue(FrameworkElement.LoadedPendingProperty); // The LoadedPendingProperty must be set if we have reached this far Debug.Assert(loadedPending != null && loadedPending.Length == 3, "The LoadedPendingProperty must be set if we have reached this far"); bool isLoaded = IsLoadedHelper(rootDO); // Remove the Loaded callback from the MediaContext's queue RemoveLoadedCallback(rootDO, loadedPending); BroadcastLoadedSynchronously(rootDO, isLoaded); return null; } internal static void BroadcastLoadedSynchronously(DependencyObject rootDO, bool isLoaded) { // It is possible that the loaded broadcast for a parent caused you to be loaded before // your broadcast item got dequeued. In that case simply ignore the operation if (!isLoaded) { // Broadcast the Loaded event BroadcastEventHelper.BroadcastEvent(rootDO, FrameworkElement.LoadedEvent); } } ////// Broadcast the Unloaded event when the element is detached from a Loaded Tree /// /// /// Root of the sub-tree that the broadcast will start at /// internal static object BroadcastUnloadedEvent(object root) { DependencyObject rootDO = (DependencyObject)root; object[] unloadedPending = (object[])rootDO.GetValue(FrameworkElement.UnloadedPendingProperty); // The UnloadedPendingProperty must be set if we have reached this far Debug.Assert(unloadedPending != null && unloadedPending.Length == 3, "The UnloadedPendingProperty must be set if we have reached this far"); bool isLoaded = IsLoadedHelper(rootDO); // Remove the Unloaded callback from the MediaContext's queue RemoveUnloadedCallback(rootDO, unloadedPending); BroadcastUnloadedSynchronously(rootDO, isLoaded); return null; } internal static void BroadcastUnloadedSynchronously(DependencyObject rootDO, bool isLoaded) { // It is possible that the unloaded broadcast for a parent caused you to be unloaded before // your broadcast item got dequeued. In that case simply ignore the operation if (isLoaded) { // Broadcast the Unloaded event BroadcastEvent(rootDO, FrameworkElement.UnloadedEvent); } } private static VisitedCallbackBroadcastDelegate = new VisitedCallback (OnBroadcastCallback); private struct BroadcastEventData { internal BroadcastEventData(DependencyObject root, RoutedEvent routedEvent, List eventRoute) { Root = root; RoutedEvent = routedEvent; EventRoute = eventRoute; } internal DependencyObject Root; internal RoutedEvent RoutedEvent; internal List EventRoute; } /// /// Broadcast the Loaded/Unloaded event in the sub-tree starting at the given root /// /// /// Root of the sub-tree that the event will be broadcast to /// /// /// RoutedEventID for the event we wish to broadcast /// private static void BroadcastEvent(DependencyObject root, RoutedEvent routedEvent) { // Broadcast to the tree and collect the set of nodes // on which we need fire the Loaded event ListeventRoute = new List (); // Create a DescendentsWalker for the broadcast DescendentsWalker walker = new DescendentsWalker ( TreeWalkPriority.VisualTree, BroadcastDelegate, new BroadcastEventData(root, routedEvent, eventRoute)); // Start the walk down walker.StartWalk(root); // Iterate and raise the event on each of the nodes in the tree for (int i=0; i< eventRoute.Count; i++) { DependencyObject d = eventRoute[i]; RoutedEventArgs args = new RoutedEventArgs(routedEvent, d); FrameworkObject fo = new FrameworkObject(d, true /*throwIfNeither*/); if (routedEvent == FrameworkElement.LoadedEvent) { fo.OnLoaded(args); } else { fo.OnUnloaded(args); } } } // Callback on visiting each node in the descendency during a broadcast event private static bool OnBroadcastCallback(DependencyObject d, BroadcastEventData data) { DependencyObject root = data.Root; RoutedEvent routedEvent = data.RoutedEvent; List eventRoute = data.EventRoute; if (FrameworkElement.DType.IsInstanceOfType(d)) { // If this is a FrameworkElement FrameworkElement fe = (FrameworkElement)d; if (fe != root && routedEvent == FrameworkElement.LoadedEvent && fe.UnloadedPending != null) { // If there is a pending Unloaded event wait till we've broadcast // that event before we can fire the new Loaded event. fe.FireLoadedOnDescendentsInternal(); } else if (fe != root && routedEvent == FrameworkElement.UnloadedEvent && fe.LoadedPending != null) { // If there is a pending Loaded event abort it because we are now // being Unloaded. RemoveLoadedCallback(fe, fe.LoadedPending); } else { // If element has handlers fire the event and continue to walk down the tree if (fe.SubtreeHasLoadedChangeHandler) { // We cannot assert this condition here for the following reason. // If the [Un]LoadedHandler is added to the current node after the parent // for this node has been [Un]Loaded but before the current node has been [Un]Loaded // (example: within the [Un]Loaded handler for the parent), then the IsLoaded // cache on the current node has been updated to match that of the parent, // and this Assert will be violated. See BroadcastEventHelper.UpdateHasHandlerFlag // for further description. // Debug.Assert(IsLoaded == [false/true], // "Element should have been [Un]loaded before it is [Un]Loaded back again"); fe.IsLoadedCache = (routedEvent == FrameworkElement.LoadedEvent); eventRoute.Add(fe); // Continue walk down subtree return true; } } } else { // If this is a FrameworkContentElement FrameworkContentElement fce = (FrameworkContentElement)d; if (fce != root && routedEvent == FrameworkElement.LoadedEvent && fce.UnloadedPending != null) { // If there is a pending Unloaded event wait till we've broadcast // that event before we can fire the new Loaded event. fce.FireLoadedOnDescendentsInternal(); } else if (fce != root && routedEvent == FrameworkElement.UnloadedEvent && fce.LoadedPending != null) { // If there is a pending Loaded event abort it because we are now // being Unloaded. RemoveLoadedCallback(fce, fce.LoadedPending); } else { // If element has handlers fire the event and continue to walk down the tree if (fce.SubtreeHasLoadedChangeHandler) { // We cannot assert this condition here for the following reason. // If the [Un]LoadedHandler is added to the current node after the parent // for this node has been [Un]Loaded but before the current node has been [Un]Loaded // (example: within the [Un]Loaded handler for the parent), then the IsLoaded // cache on the current node has been updated to match that of the parent, // and this Assert will be violated. See BroadcastEventHelper.UpdateHasHandlerFlag // for further description. // Debug.Assert(IsLoaded == [false/true], // "Element should have been [Un]loaded before it is [Un]Loaded back again"); fce.IsLoadedCache = (routedEvent == FrameworkElement.LoadedEvent); eventRoute.Add(fce); // Continue walk down subtree return true; } } } // Stop walk down subtree return false; } private static bool SubtreeHasLoadedChangeHandlerHelper(DependencyObject d) { if (FrameworkElement.DType.IsInstanceOfType(d)) { return ((FrameworkElement)d).SubtreeHasLoadedChangeHandler; } else if (FrameworkContentElement.DType.IsInstanceOfType(d)) { return ((FrameworkContentElement)d).SubtreeHasLoadedChangeHandler; } return false; } private static void FireLoadedOnDescendentsHelper(DependencyObject d) { if (FrameworkElement.DType.IsInstanceOfType(d)) { ((FrameworkElement)d).FireLoadedOnDescendentsInternal(); } else { ((FrameworkContentElement)d).FireLoadedOnDescendentsInternal(); } } private static void FireUnloadedOnDescendentsHelper(DependencyObject d) { if (FrameworkElement.DType.IsInstanceOfType(d)) { ((FrameworkElement)d).FireUnloadedOnDescendentsInternal(); } else { ((FrameworkContentElement)d).FireUnloadedOnDescendentsInternal(); } } private static bool IsLoadedHelper(DependencyObject d) { FrameworkObject fo = new FrameworkObject(d); return fo.IsLoaded; } // Helper method that recursively queries the parents to see if they are loaded. // This method is invoked only when the loaded cache on the given node isn't valid. internal static bool IsParentLoaded(DependencyObject d) { FrameworkObject fo = new FrameworkObject(d); DependencyObject parent = fo.EffectiveParent; Visual visual; Visual3D visual3D; if (parent != null) { return IsLoadedHelper(parent); } else if ((visual = d as Visual) != null) { // If parent is null then this is the root element return SafeSecurityHelper.IsConnectedToPresentationSource(visual); } else if ((visual3D = d as Visual3D) != null) { // IsConnectedToPresentationSource could also be modified to take // a DO - instead though we'll just get the containing visual2D for // this 3D object. visual = VisualTreeHelper.GetContainingVisual2D(visual3D); if (visual != null) { return SafeSecurityHelper.IsConnectedToPresentationSource(visual); } else { return false; } } else return false; } /// /// Check if the Framework Element Factory that produced the Template /// that created this control has a Loaded Change Handler. /// /// /// The caller must pass in a non-null templatedParent. /// internal static FrameworkElementFactory GetFEFTreeRoot(DependencyObject templatedParent) { FrameworkObject fo = new FrameworkObject(templatedParent, true); Debug.Assert( fo.IsFE ); FrameworkTemplate templatedParentTemplate = fo.FE.TemplateInternal; FrameworkElementFactory fefTree = templatedParentTemplate.VisualTree; return fefTree; } ////// Update the Has[Loaded/UnLoaded]Handler flags if required. /// This method is called from OnNewParent/OnVisualParentChanged. /// /// /// Node to begin the update /// /// /// Old Parent /// /// /// New Parent /// internal static void AddOrRemoveHasLoadedChangeHandlerFlag( DependencyObject d, DependencyObject oldParent, DependencyObject newParent) { bool hasLoadChangedHandler = SubtreeHasLoadedChangeHandlerHelper(d); if(hasLoadChangedHandler) { // Attaching to a Parent if (oldParent == null && newParent != null) { // Subtree with a handler got added AddHasLoadedChangeHandlerFlagInAncestry(newParent); } // Detaching from a Parent else if (oldParent != null && newParent == null) { // Subtree with a handler got removed RemoveHasLoadedChangeHandlerFlagInAncestry(oldParent); } } } internal static void AddHasLoadedChangeHandlerFlagInAncestry(DependencyObject d) { UpdateHasLoadedChangeHandlerFlagInAncestry(d, true); } internal static void RemoveHasLoadedChangeHandlerFlagInAncestry(DependencyObject d) { UpdateHasLoadedChangeHandlerFlagInAncestry(d, false); } ////// Evaluate the HasLoadedChangeHandler flag on the given node by /// querying its children, and styles, and templates. /// /// /// Node /// private static bool AreThereLoadedChangeHandlersInSubtree(ref FrameworkObject fo) { // HasHandler flag can be evaluated only for a FE/FCE. if (!fo.IsValid) return false; if (fo.ThisHasLoadedChangeEventHandler) return true; if (fo.IsFE) { // Check if any of your visual children have the flag set Visual v = (Visual)fo.FE; int count = VisualTreeHelper.GetChildrenCount(v); for(int i = 0; i < count; i++) { FrameworkElement child = VisualTreeHelper.GetChild(v, i) as FrameworkElement; if (child != null && child.SubtreeHasLoadedChangeHandler) { return true; } } } // Check if any of your logical children have the flag set foreach(object o in LogicalTreeHelper.GetChildren(fo.DO)) { DependencyObject child = o as DependencyObject; if(null != child && SubtreeHasLoadedChangeHandlerHelper(child)) { return true; } } return false; } ////// This is a recursive function that walks up the tree Adding or Removing /// HasLoadedChangeHander bits. It also inits the IsLoadedCache on Add. /// /// /// Node to update /// /// /// Is it an AddHandler/ Add Child with Handler Operation /// private static void UpdateHasLoadedChangeHandlerFlagInAncestry(DependencyObject d, bool addHandler) { FrameworkObject fo = new FrameworkObject(d); if (!addHandler) { if ( AreThereLoadedChangeHandlersInSubtree(ref fo) ) return; // done } if (fo.IsValid) { if (fo.SubtreeHasLoadedChangeHandler != addHandler) { DependencyObject coreParent = (fo.IsFE) ? VisualTreeHelper.GetParent(fo.FE) : null; DependencyObject logicalParent = fo.Parent; DependencyObject parent = null; fo.SubtreeHasLoadedChangeHandler = addHandler; // Propagate the change to your visual ancestry if (coreParent != null) { UpdateHasLoadedChangeHandlerFlagInAncestry(coreParent, addHandler); parent = coreParent; } // Propagate the change to your logical ancestry if (logicalParent != null && logicalParent != coreParent) { UpdateHasLoadedChangeHandlerFlagInAncestry(logicalParent, addHandler); if (fo.IsFCE) parent = logicalParent; } // Propagate the change to your mentor, if any if (logicalParent == null && coreParent == null) { parent = Helper.FindMentor(fo.DO.InheritanceContext); if (parent != null) { fo.ChangeSubtreeHasLoadedChangedHandler(parent); } } if(addHandler) { // The HasLoadedChangeHandler flag is used for two purposes. // 1. To indicate that the sub-tree starting at the current node has // handlers for Loaded / Unloaded event. So broadcast logic // can walk down that path to fire the events. // 2. To indicate that the IsLoaded cache on the node is valid. // If we are adding a handler: // On the POP side of the recursion, as we come back down from the root, // pull the value of IsLoadedCache from the parent in to the child. if (fo.IsFE) { UpdateIsLoadedCache(fo.FE, parent); } else { UpdateIsLoadedCache(fo.FCE, parent); } } } } else // neither a FE or an FCE { DependencyObject coreParent = null; Visual v; Visual3D v3D; ContentElement ce; // This is neither an FE nor and FCE // Propagate the change to your visual ancestry if ((v = d as Visual) != null) { coreParent = VisualTreeHelper.GetParent(v); } else if ((ce = d as ContentElement) != null) { coreParent = ContentOperations.GetParent(ce); } else if ((v3D = d as Visual3D) != null) { coreParent = VisualTreeHelper.GetParent(v3D); } if (coreParent != null) { UpdateHasLoadedChangeHandlerFlagInAncestry(coreParent, addHandler); } } } ////// Updates the IsLoadedCache on the current FrameworkElement /// private static void UpdateIsLoadedCache( FrameworkElement fe, DependencyObject parent) { if (fe.GetValue(FrameworkElement.LoadedPendingProperty) == null) { // Propagate the change to your visual ancestry if (parent != null) { fe.IsLoadedCache = IsLoadedHelper(parent); } // This is the root visual. else if ( SafeSecurityHelper.IsConnectedToPresentationSource( fe )) { fe.IsLoadedCache = true; } else { fe.IsLoadedCache = false; } } else { // Clear the cache if Loaded is pending fe.IsLoadedCache = false; } } ////// Updates the IsLoadedCache on the current FrameworkContentElement /// private static void UpdateIsLoadedCache( FrameworkContentElement fce, DependencyObject parent) { if (fce.GetValue(FrameworkElement.LoadedPendingProperty) == null) { // Propagate the change to your logical ancestry fce.IsLoadedCache = IsLoadedHelper(parent); } else { // Clear the cache if Loaded is pending fce.IsLoadedCache = false; } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- ProtocolsConfigurationHandler.cs
- SecurityNegotiationException.cs
- securitycriticaldata.cs
- SourceFileInfo.cs
- XPathMultyIterator.cs
- SqlBuffer.cs
- CLSCompliantAttribute.cs
- RowToFieldTransformer.cs
- NamespaceCollection.cs
- EventLogConfiguration.cs
- AsymmetricAlgorithm.cs
- DataGridViewElement.cs
- SafeNativeMethods.cs
- WriteFileContext.cs
- COM2ExtendedBrowsingHandler.cs
- SpeechSeg.cs
- StringStorage.cs
- MatrixAnimationUsingPath.cs
- X509CertificateCollection.cs
- Constants.cs
- DbProviderFactory.cs
- FormsAuthenticationCredentials.cs
- CharacterBufferReference.cs
- SupportingTokenSecurityTokenResolver.cs
- ScriptResourceInfo.cs
- TypeConstant.cs
- ProcessHost.cs
- FontCacheUtil.cs
- CategoryNameCollection.cs
- TextDataBindingHandler.cs
- SafeEventLogReadHandle.cs
- jithelpers.cs
- Activity.cs
- AuthenticatedStream.cs
- HttpProfileBase.cs
- HtmlContainerControl.cs
- ColumnPropertiesGroup.cs
- DebugTraceHelper.cs
- SiteMapProvider.cs
- StringResourceManager.cs
- AttributeUsageAttribute.cs
- HttpApplicationFactory.cs
- OdbcException.cs
- LostFocusEventManager.cs
- SizeFConverter.cs
- ObjectStateFormatter.cs
- TemplateManager.cs
- RepeatBehaviorConverter.cs
- BitConverter.cs
- Comparer.cs
- ArrayItemReference.cs
- ISAPIRuntime.cs
- ClonableStack.cs
- ChangeBlockUndoRecord.cs
- TextInfo.cs
- errorpatternmatcher.cs
- FixedSOMTableRow.cs
- DesignerSerializationOptionsAttribute.cs
- SmiEventSink.cs
- XPathNodeList.cs
- Registry.cs
- ExpressionSelection.cs
- ActivityBindForm.Designer.cs
- webbrowsersite.cs
- XmlObjectSerializer.cs
- AuthorizationContext.cs
- PolyBezierSegmentFigureLogic.cs
- SoapObjectInfo.cs
- QueryStringParameter.cs
- baseaxisquery.cs
- Filter.cs
- FixedNode.cs
- TdsRecordBufferSetter.cs
- RetrieveVirtualItemEventArgs.cs
- DBAsyncResult.cs
- DefaultEvaluationContext.cs
- SeparatorAutomationPeer.cs
- COSERVERINFO.cs
- SQLInt32.cs
- PrinterResolution.cs
- VirtualDirectoryMapping.cs
- TemplateApplicationHelper.cs
- _SSPISessionCache.cs
- InlinedAggregationOperatorEnumerator.cs
- MemberRelationshipService.cs
- ChineseLunisolarCalendar.cs
- CompilerScope.cs
- ContextBase.cs
- IssuedSecurityTokenProvider.cs
- StretchValidation.cs
- CustomError.cs
- _ShellExpression.cs
- CodeParameterDeclarationExpression.cs
- NavigationWindow.cs
- Console.cs
- XdrBuilder.cs
- BoolExpression.cs
- Int32KeyFrameCollection.cs
- initElementDictionary.cs
- Encoder.cs