BroadcastEventHelper.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / 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 VisitedCallback BroadcastDelegate = 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 
            List eventRoute = 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 VisitedCallback BroadcastDelegate = 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 
            List eventRoute = 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

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK