Code:
/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Core / System / Windows / Media / Visual.cs / 8 / Visual.cs
//------------------------------------------------------------------------------ // //// Copyright (C) Microsoft Corporation. All rights reserved. // // // Description: // Defines a node in the composition scene graph. // //----------------------------------------------------------------------------- using System; using System.Security; using System.Windows.Threading; using MS.Win32; using System.Windows.Media; using System.Windows.Media.Media3D; using System.Windows.Media.Animation; using System.Windows.Media.Composition; using System.Windows.Media.Effects; using System.Diagnostics; using System.Collections; using System.Collections.Generic; using MS.Internal; using MS.Internal.Media; using MS.Internal.Media3D; using System.Resources; using MS.Utility; using System.Runtime.InteropServices; using MS.Internal.PresentationCore; using SR=MS.Internal.PresentationCore.SR; using SRID=MS.Internal.PresentationCore.SRID; //----------------------------------------------------------------------------- // This section lists various things that we could improve on the Visual class. // // - (Pail) Don't allocate a managed Pail object. // - (Finalizer) Currently we delete the cob explicitly when a node is removed from // the scene graph. However, we don't do this when we remove the root node. // currently this is done by the finalizer. If we clean explicitly up when we // remove the root node we won't need a finalizer. //----------------------------------------------------------------------------- //------------------------------------------------------------------------------ // PUBLIC API EXPOSURE RULES // // If you expose a public/protected API you need to check a couple things: // // A) Call the correct version of VerifyAPI. This checks the following // 1) That the calling thread has entered the context of this object // 2) That the current object is not disposed. // 3) If another object is passed in, that it has the same // context affinity as this object. This should be used for // arguments to the API // 4) That the current permissions are acceptable. // // B) That other arguments are not disposed if needed. // // //----------------------------------------------------------------------------- namespace System.Windows.Media { // this class is used to wrap the Map struct into an object so // that we can use it with the UncommonField infrastructure. internal class MapClass { internal MapClass() { _map_ofBrushes = new DUCE.Map(); } internal bool IsEmpty { get { return _map_ofBrushes.IsEmpty(); } } public DUCE.Map _map_ofBrushes; } /// /// The Visual class is the base class for all Visual types. It provides /// services and properties that all Visuals have in common. Services include /// hit-testing, coordinate transformation, bounding box calculations. Properties /// are for example a transform property and an opacity property. /// /// Derived Visuals render their content first and then render the children, or in other /// words, the content of a Visual is always behind the content of its children. /// public abstract partial class Visual : DependencyObject, DUCE.IResource { // -------------------------------------------------------------------- // // Constants // // -------------------------------------------------------------------- #region Constants ////// This is the dirty mask for a visual, set every time we marshall /// a visual to a channel and reset by the end of the render pass. /// private const VisualProxyFlags c_ProxyFlagsDirtyMask = VisualProxyFlags.IsSubtreeDirtyForRender | VisualProxyFlags.IsContentDirty | VisualProxyFlags.IsTransformDirty | VisualProxyFlags.IsGuidelineCollectionDirty | VisualProxyFlags.IsClipDirty | VisualProxyFlags.IsOpacityDirty | VisualProxyFlags.IsOpacityMaskDirty | VisualProxyFlags.IsOffsetDirty | VisualProxyFlags.IsEdgeModeDirty | VisualProxyFlags.IsBitmapScalingModeDirty; ////// This is the dirty mask for a BitmapEffect. /// private const VisualProxyFlags c_BitmapEffectDirtyMask = VisualProxyFlags.IsSubtreeDirtyForRender | VisualProxyFlags.IsContentDirty | VisualProxyFlags.IsOpacityDirty | VisualProxyFlags.IsOpacityMaskDirty | VisualProxyFlags.IsEdgeModeDirty | VisualProxyFlags.IsBitmapScalingModeDirty; ////// This is the dirty mask for a visual, set every time we marshall /// a visual to a channel and reset by the end of the render pass. /// /// This mask is only for Viewport3D visual, since the contents /// of the Viewport3D are rendered during RenderContent, we /// need to set those flags as dirty if the visual is not on /// channel yet /// private const VisualProxyFlags c_Viewport3DProxyFlagsDirtyMask = VisualProxyFlags.Viewport3DVisual_IsCameraDirty | VisualProxyFlags.Viewport3DVisual_IsViewportDirty; #endregion Constants // ------------------------------------------------------------------- // // Internal Constructor // // -------------------------------------------------------------------- #region Internal Constructor ////// This internal ctor is a hook to allow Visual subclasses /// to create their unique type of a visual resource. /// internal Visual(DUCE.ResourceType resourceType) { #if DEBUG _parentIndex = -1; #endif switch (resourceType) { case DUCE.ResourceType.TYPE_VISUAL: // Default setting break; case DUCE.ResourceType.TYPE_VIEWPORT3DVISUAL: SetFlags(true, VisualFlags.IsViewport3DVisual); break; default: Debug.Assert(false, "TYPE_VISUAL or TYPE_VIEWPORT3DVISUAL expected."); break; } } #endregion Protected Constructor // ------------------------------------------------------------------- // // Protected Constructor // // ------------------------------------------------------------------- #region Protected Constructor ////// Ctor Visual /// protected Visual() : this(DUCE.ResourceType.TYPE_VISUAL) { } #endregion Protected Constructor // ------------------------------------------------------------------- // // IResource implementation // // -------------------------------------------------------------------- #region IResource implementation ////// This is used to check if the composition node /// for the visual is on channel /// /// ///internal bool IsOnChannel(DUCE.Channel channel) { return _proxy.IsOnChannel(channel); } /// /// This is used to check if the composition node /// for the visual is on a set of channels /// /// ArrayList of DUCE.Channel ///true if and only if the composition node is on all the channels internal bool IsOnChannels(Listchannels) { // Check to ensure that we find at least // one channel in the ArrayList. bool foundChannel = false; foreach (DUCE.ChannelSet channelSet in channels) { if (!IsOnChannel(channelSet.Channel)) { return false; } foundChannel = true; } return foundChannel; } /// /// Returns the handle this visual has on the given channel. /// /// ///DUCE.ResourceHandle DUCE.IResource.GetHandle(DUCE.Channel channel) { return _proxy.GetHandle(channel); } /// /// Only Viewport3DVisual and Visual3D implements this. /// Vieport3DVisual has two handles. One stored in _proxy /// and the other one stored in _proxy3D. This function returns /// the handle stored in _proxy3D. /// DUCE.ResourceHandle DUCE.IResource.Get3DHandle(DUCE.Channel channel) { throw new NotImplementedException(); } ////// This is used to create or addref the visual resource /// on the given channel /// /// ///DUCE.ResourceHandle DUCE.IResource.AddRefOnChannel(DUCE.Channel channel) { return AddRefOnChannelCore(channel); } internal virtual DUCE.ResourceHandle AddRefOnChannelCore(DUCE.Channel channel) { DUCE.ResourceType resourceType = DUCE.ResourceType.TYPE_VISUAL; if (CheckFlagsAnd(VisualFlags.IsViewport3DVisual)) { resourceType = DUCE.ResourceType.TYPE_VIEWPORT3DVISUAL; } _proxy.CreateOrAddRefOnChannel(channel, resourceType); return _proxy.GetHandle(channel); } /// /// this is used to release the comp node of the visual /// on the given channel /// /// ///internal virtual void ReleaseOnChannelCore(DUCE.Channel channel) { _proxy.ReleaseOnChannel(channel); } /// /// Sends a command to compositor to remove the child /// from its parent on the channel. /// void DUCE.IResource.RemoveChildFromParent( DUCE.IResource parent, DUCE.Channel channel) { DUCE.CompositionNode.RemoveChild( parent.GetHandle(channel), _proxy.GetHandle(channel), channel); } int DUCE.IResource.GetChannelCount() { return _proxy.Count; } DUCE.Channel DUCE.IResource.GetChannel(int index) { return _proxy.GetChannel(index); } #endregion IResource implementation // ------------------------------------------------------------------- // // IElement implementation // // -------------------------------------------------------------------- // -------------------------------------------------------------------- // // Internal Properties // // ------------------------------------------------------------------- #region Internal Properties // Are we in the process of iterating the visual children. // This flag is set during a descendents walk, for property invalidation. internal bool IsVisualChildrenIterationInProgress { [FriendAccessAllowed] get { return CheckFlagsAnd(VisualFlags.IsVisualChildrenIterationInProgress); } [FriendAccessAllowed] set { SetFlags(value, VisualFlags.IsVisualChildrenIterationInProgress); } } ////// The CompositionTarget marks the root element. The root element is responsible /// for posting renders. /// ////// The property getter is also used to ensure that the Visual is /// not used in multiple CompositionTargets. /// internal bool IsRootElement { get { return CheckFlagsAnd(VisualFlags.ShouldPostRender); } set { SetFlags(value, VisualFlags.ShouldPostRender); } } #endregion Internal Properties // -------------------------------------------------------------------- // // Visual Content // // ------------------------------------------------------------------- #region Visual Content ////// Derived classes must override this method and return the bounding /// box of their content. /// internal virtual Rect GetContentBounds() { return Rect.Empty; } ////// RenderContent is implemented by derived classes to hook up their /// content. The implementer of this function can assert that the /// visual is marshaled on the current channel when the function /// is executed. /// internal virtual void RenderContent(RenderContext ctx, bool isOnChannel) { /* do nothing */ } ////// This method is overrided on Visuals that can instantiate IDrawingContext /// Currently, DrawingVisual and UIElement /// internal virtual void RenderClose(IDrawingContent newContent) { } ////// This method is called when the realization caches need to be updated. /// internal virtual void UpdateRealizations(RealizationContext ctx) { // // Visuals have only one type of realizable content: bitmap effects, // which are handled as a special case for rendering purposes, making // UpdateRealization essentialy a no-op. // // We allow the Visual descendants to handle other kinds of realizable // content (for example, glyph runs referenced from render data on // a DrawingVisual) by overriding this method. // } ////// VisualContentBounds returns the bounding box for the contents of the current visual. /// internal Rect VisualContentBounds { get { // Probably too restrictive. Let's see who wants it during OnRender. VerifyAPIReadWrite(); return GetContentBounds(); } } ////// VisualDescendantBounds returns the union of all of the content bounding /// boxes for all of the descendants of the current visual,and also including /// the contents of the current visual. So we end up with the /// bounds of the whole sub-graph in inner space. /// internal Rect VisualDescendantBounds { get { // Probably too restrictive. Let's see who wants it during OnRender. VerifyAPIReadWrite(); Rect bboxSubgraph = CalculateSubgraphBoundsInnerSpace(); // If bounding box has NaN, then we set the bounding box to infinity. if (DoubleUtil.RectHasNaN(bboxSubgraph)) { bboxSubgraph.X = Double.NegativeInfinity; bboxSubgraph.Y = Double.NegativeInfinity; bboxSubgraph.Width = Double.PositiveInfinity; bboxSubgraph.Height = Double.PositiveInfinity; } return bboxSubgraph; } } ////// Computes the union of all content bounding boxes of this Visual's sub-graph /// in inner space. Note that the result includes the root Visual's content. /// /// Definition: Outer/Inner Space: /// /// A Visual has a set of properties which include clip, transform, offset /// and bitmap effect. Those properties affect in which space (coordinate /// clip space) a Visual's vector graphics and sub-graph is interpreted. /// Inner space is the space before applying any of the properties. Outer /// space is the space where all the properties have been taken into account. /// For example if the Visual draws a rectangle {0, 0, 100, 100} and the /// Offset property is set to {20, 20} and the clip is set to {40, 40, 10, 10} /// then the bounding box of the Visual in inner space is {0, 0, 100, 100} and /// in outer space {60, 60, 10, 10} (start out with the bbox of {0, 0, 100, 100} /// then apply the clip {40, 40, 10, 10} which leaves us with a bbox of /// {40, 40, 10, 10} and finally apply the offset and we end up with a bbox /// of {60, 60, 10, 10} /// internal Rect CalculateSubgraphBoundsInnerSpace() { return CalculateSubgraphBoundsInnerSpace(false); } ////// Computes the union of all rendering bounding boxes of this Visual's sub-graph /// in inner space. Note that the result includes the root Visual's content. /// internal Rect CalculateSubgraphRenderBoundsInnerSpace() { return CalculateSubgraphBoundsInnerSpace(true); } ////// Same as the parameterless CalculateSubgraphBoundsInnerSpace except it takes a /// boolean indicating whether or not to calculate the rendering bounds. /// If the renderBounds parameter is set to true then the render bounds are returned. /// The render bounds differ in that they treat zero area bounds as emtpy rectangles. /// /// internal virtual Rect CalculateSubgraphBoundsInnerSpace(bool renderBounds) { Rect bboxSubgraph = Rect.Empty; // Recursively calculate sub-graph bounds of children of this node. We get // the bounds of each child Visual in outer space which is this Visual's // inner space and union them together. int count = VisualChildrenCount; for (int i = 0; i < count; i++) { Visual child = GetVisualChild(i); if (child != null) { Rect bboxSubgraphChild = child.CalculateSubgraphBoundsOuterSpace(renderBounds); bboxSubgraph.Union(bboxSubgraphChild); } } // Get the content bounds of the Visual. In the case that we're interested in render // bounds (i.e. what MIL will consider the size of the object), we set 0 area rects // to be empty so that they don't union to create larger sized rects. Rect contentBounds = GetContentBounds(); if (renderBounds && IsEmptyRenderBounds(ref contentBounds /* ref for perf - not modified */)) { contentBounds = Rect.Empty; } // Union the content bounds to the sub-graph bounds so that we end up with the // bounds of the whole sub-graph in inner space and return it. bboxSubgraph.Union(contentBounds); return bboxSubgraph; } /// /// Computes the union of all content bounding boxes of this Visual's sub-graph /// in outer space. Note that the result includes the root Visual's content. /// For a definition of outer/inner space see CalculateSubgraphBoundsInnerSpace. /// internal Rect CalculateSubgraphBoundsOuterSpace() { return CalculateSubgraphBoundsOuterSpace(false /* renderBounds */); } ////// Computes the union of all rendering bounding boxes of this Visual's sub-graph /// in outer space. Note that the result includes the root Visual's content. /// For a definition of outer/inner space see CalculateSubgraphBoundsInnerSpace. /// internal Rect CalculateSubgraphRenderBoundsOuterSpace() { return CalculateSubgraphBoundsOuterSpace(true /* renderBounds */); } ////// Same as the parameterless CalculateSubgraphBoundsOuterSpace except it takes a /// boolean indicating whether or not to calculate the rendering bounds. /// If the renderBounds parameter is set to true then the render bounds are returned. /// The render bounds differ in that they treat zero area bounds as emtpy rectangles. /// /// private Rect CalculateSubgraphBoundsOuterSpace(bool renderBounds) { Rect bboxSubgraph = Rect.Empty; // Get the inner space bounding box of this node and then transform it into outer // space. bboxSubgraph = CalculateSubgraphBoundsInnerSpace(renderBounds); // Apply Effect if (CheckFlagsAnd(VisualFlags.NodeHasBitmapEffect)) { Rect bitmapEffectBounds = BitmapEffectStateField.GetValue(this).GetBounds(bboxSubgraph); if (renderBounds && IsEmptyRenderBounds(ref bitmapEffectBounds /* ref for perf - not modified */)) { bitmapEffectBounds = Rect.Empty; } bboxSubgraph.Union(bitmapEffectBounds); } // Apply Clip. Geometry clip = ClipField.GetValue(this); if (clip != null) { bboxSubgraph.Intersect(clip.Bounds); } // Apply Transform. Transform transform = TransformField.GetValue(this); if ((transform != null) && (!transform.IsIdentity)) { Matrix m = transform.Value; MatrixUtil.TransformRect(ref bboxSubgraph, ref m); } // Apply Offset. if (!bboxSubgraph.IsEmpty) { bboxSubgraph.X += _offset.X; bboxSubgraph.Y += _offset.Y; } // If bounding box has NaN, then we set the bounding box to infinity. if (DoubleUtil.RectHasNaN(bboxSubgraph)) { bboxSubgraph.X = Double.NegativeInfinity; bboxSubgraph.Y = Double.NegativeInfinity; bboxSubgraph.Width = Double.PositiveInfinity; bboxSubgraph.Height = Double.PositiveInfinity; } return bboxSubgraph; } /// /// This method returns true if the given WPF bounds will be considered /// empty in terms of rendering. This is the case when the bounds describe /// a zero-area space. bounds are passed by ref for speed but are not modified /// /// private bool IsEmptyRenderBounds(ref Rect bounds) { return (bounds.Width <= 0 || bounds.Height <= 0); } #endregion Visual Content // ------------------------------------------------------------------- // // Bitmap Effects // // ------------------------------------------------------------------- #region Bitmap Effects /// /// This method schedules the bitmap effect content for /// realization update /// /// internal void UpdateBitmapEffectRealizations(RealizationContext ctx) { BitmapEffectVisualState effectState = BitmapEffectStateField.GetValue(this); if (effectState.BitmapEffectDrawing.ScheduleForUpdates) { BitmapEffectContent content = new BitmapEffectContent(this, ctx); ctx.ScheduleForRealizationsUpdate(content); // if the bitmap effect or the visual changed, // we need to rerender all realizations foreach (DUCE.ChannelSet channelSet in ctx.Channels) { if (CheckFlagsAnd(channelSet.Channel, VisualProxyFlags.IsBitmapEffectDirty)) { effectState.BitmapEffectDrawing = null; } } effectState.BitmapEffectDrawing.ScheduleForUpdates = false; } effectState.BitmapEffectDrawing.UpdateRealizations(ctx); } #endregion Bitmap Effects // -------------------------------------------------------------------- // // Resource Marshalling and Unmarshalling // // ------------------------------------------------------------------- #region Resource Marshalling and Unmarshalling ////// Override this function in derived classes to release unmanaged resources during Dispose /// and during removal of a subtree. /// internal virtual void FreeContent(DUCE.Channel channel) { Debug.Assert(IsOnChannel(channel)); Debug.Assert(!CheckFlagsAnd(channel, VisualProxyFlags.IsContentNodeConnected)); } ////// Frees up resources in this visual's subtree. /// This is only called during the realization pass if the visual has /// a bitmap effect. Free the resources used to render the visual /// to a bitmap. /// /// /// The channel to release the resources on. /// internal void ReleaseOnChannelForBitmapEffect( DUCE.Channel channel) { if (!IsOnChannel(channel) || CheckFlagsAnd(channel, VisualProxyFlags.IsDeleteResourceInProgress)) { return; } // Set the flag to true to prevent re-entrancy. SetFlags(channel, true, VisualProxyFlags.IsDeleteResourceInProgress); try { // at this point the tree is not connected any more. SetFlags(channel, false, VisualProxyFlags.IsConnectedToParent); FreeContent(channel); // // Free dependent DUCE resources. // // We don't need to free the dependent resources if they're // marked as dirty because when the flag is set, we also // disconnect the resource from the visual resource. Brush opacityMask = OpacityMaskField.GetValue(this); if ((opacityMask != null) && (!CheckFlagsAnd(channel, VisualProxyFlags.IsOpacityMaskDirty))) { ((DUCE.IResource)opacityMask).ReleaseOnChannel(channel); } // // Release the visual. // this.ReleaseOnChannelCore(channel); // // Finally, the children. // int count = VisualChildrenCount; for (int i = 0; i < count; i++) { Visual child = GetVisualChild(i); if (child != null) { ((DUCE.IResource)child).ReleaseOnChannel(channel); } } } finally { // // We need to reset this flag if we are still on channel since we // have only decreased the ref-count and not deleted the resource. // if (IsOnChannel(channel)) { SetFlags(channel, false, VisualProxyFlags.IsDeleteResourceInProgress); } } } ////// Returns true if this is a root of a VisualBrush on the specified channel /// private bool IsVisualBrushRootOnChannel(DUCE.Channel channel) { bool isVisualBrushRootOnChannel = false; DictionarychannelsToVisualBrushMap = ChannelsToVisualBrushMapField.GetValue(this); if (channelsToVisualBrushMap != null) { int references; if (channelsToVisualBrushMap.TryGetValue(channel, out references)) { isVisualBrushRootOnChannel = (references > 0); } } return isVisualBrushRootOnChannel; } /// /// Frees up resources in this visual's subtree. /// /// /// The channel to release the resources on. /// void DUCE.IResource.ReleaseOnChannel(DUCE.Channel channel) { if (!IsOnChannel(channel) || CheckFlagsAnd(channel, VisualProxyFlags.IsDeleteResourceInProgress)) { return; } // Set the flag to true to prevent re-entrancy. SetFlags(channel, true, VisualProxyFlags.IsDeleteResourceInProgress); try { // at this point the tree is not connected any more. SetFlags(channel, false, VisualProxyFlags.IsConnectedToParent); // // Before unmarshaling this visual and its subtree, check if there are any visual // brushes holding references to it. In such case, we want to keep this visual // in the marshaled state and wait for all the visual brushes to release their // references through ReleaseOnChannelForVisualBrush. // // // if ( !CheckFlagsOr(VisualFlags.NodeIsVisualBrushRoot) // If we aren't a root of a VisualBrush, then we aren't referenced // at all and we can go away || !channel.IsConnected // If the channel isn't connected, there's no reason to keep things alive || channel.IsSynchronous // If the channel is synchronous, the node isn't going to stick around // so just delete it. *** THIS IS DANGEROUS ***. See above for // more comments. || !IsVisualBrushRootOnChannel(channel) // If we got to here, we are the root of a VisualBrush. We can go away // only if the VB is on a different channel. This check is more expensive // and not very common so we put it last. ) { FreeContent(channel); // Free dependent DUCE resources. // // We don't need to free the dependent resources if they're // marked as dirty because when the flag is set, we also // disconnect the resource from the visual resource. Transform transform = TransformField.GetValue(this); if ((transform != null) && (!CheckFlagsAnd(channel, VisualProxyFlags.IsTransformDirty))) { // // Note that in this particular case, the transform is not // really dirty. Namely because the visual is not marshalled. // ((DUCE.IResource)transform).ReleaseOnChannel(channel); } Geometry clip = ClipField.GetValue(this); if ((clip != null) && (!CheckFlagsAnd(channel, VisualProxyFlags.IsClipDirty))) { ((DUCE.IResource)clip).ReleaseOnChannel(channel); } // // If the visual has a bitmap effect we don't send the opacity mask // to the async compositor, so we should not try and release it. // We apply the effect above opacity mask. // if (!CheckFlagsAnd(VisualFlags.NodeHasBitmapEffect)) { Brush opacityMask = OpacityMaskField.GetValue(this); if ((opacityMask != null) && (!CheckFlagsAnd(channel, VisualProxyFlags.IsOpacityMaskDirty))) { ((DUCE.IResource)opacityMask).ReleaseOnChannel(channel); } } // // Release the visual. // this.ReleaseOnChannelCore(channel); // // Finally, the children. // int count = VisualChildrenCount; for (int i = 0; i < count; i++) { Visual visual = GetVisualChild(i); if (visual != null) { ((DUCE.IResource)visual).ReleaseOnChannel(channel); } } } } finally { // // We need to reset this flag if we are still on channel since we // have only decreased the ref-count and not deleted the resource. // if (IsOnChannel(channel)) { SetFlags(channel, false, VisualProxyFlags.IsDeleteResourceInProgress); } } } ////// When we attach a bitmap effect to a Visual we are going to render everything /// below the bitmap effect on the synchronous compositor. Therefore we need to /// disconnect/reset all the properties below the bitmap effect property on a /// Visual in the asynchronous compositor since otherwise they would be applied /// twice (on the sync and async compositor). /// /// The properties are content, children, opacity, opacity mask and edge mode /// private void DisconnectBitmapEffectPropertiesOnAllChannels() { // disconnect the visual properties and content double opacity = OpacityCache; Brush opacityMask = OpacityMaskField.GetValue(this); EdgeMode edgeMode = EdgeModeCache; // // Iterate over the channels this visual is being marshaled to // for (int iProxy = 0; iProxy < _proxy.Count; iProxy++) { DUCE.Channel channel = _proxy.GetChannel(iProxy); DUCE.ResourceHandle visualHandle = _proxy.GetHandle(iProxy); //Release content FreeContent(channel); //Reset opacity to 1.0 DUCE.CompositionNode.SetAlpha( visualHandle, 1.0, channel); // Reset render options MILRenderOptions renderOptions = new MILRenderOptions(); renderOptions.Flags = MILRenderOptionFlags.EdgeMode; renderOptions.EdgeMode = EdgeMode.Unspecified; renderOptions.Flags = MILRenderOptionFlags.BitmapScalingMode; renderOptions.BitmapScalingMode = BitmapScalingMode.Unspecified; DUCE.CompositionNode.SetRenderOptions( visualHandle, renderOptions, channel); } if (opacityMask != null) { //Disconnect OpacityMask DisconnectAttachedResource( VisualProxyFlags.IsOpacityMaskDirty, opacityMask); } // disconnect all the children DisconnectChildrenOnAllChannels(); } ////// Disconnect the children of the visual on all channels /// it is being marshalled to. /// private void DisconnectChildrenOnAllChannels() { // if we don't have any children, there is nothing to do if (VisualChildrenCount == 0) return; // // Iterate over the channels this visual is being marshaled to // for (int iProxy = 0; iProxy < _proxy.Count; iProxy++) { DUCE.Channel channel = _proxy.GetChannel(iProxy); int childrenCount = VisualChildrenCount; for (int iChild = 0; iChild < childrenCount; iChild++) { Visual child = GetVisualChild(iChild); if (child != null) { if (child.CheckFlagsAnd(channel, VisualProxyFlags.IsConnectedToParent)) { channel.AddToRemoveAndReleaseQueue( this, child); child.SetFlags(channel, false, VisualProxyFlags.IsConnectedToParent); } } } } } internal virtual void AddRefOnChannelForVisualBrush( VisualBrush visualBrush, DUCE.Channel channel) { // // Since the VisualBrush to visual relationship is being created on this channel, // we need to update the number of VisualBrushes using this visual on this channel. // DictionarychannelsToVisualBrushMap = ChannelsToVisualBrushMapField.GetValue(this); if (channelsToVisualBrushMap == null) { channelsToVisualBrushMap = new Dictionary (); ChannelsToVisualBrushMapField.SetValue(this, channelsToVisualBrushMap); } if (!channelsToVisualBrushMap.ContainsKey(channel)) { channelsToVisualBrushMap[channel] = 1; } else { Debug.Assert(channelsToVisualBrushMap[channel] > 0); channelsToVisualBrushMap[channel] += 1; } // // Since the VisualBrush to visual relationship is being created on this channel, // we need to update the number of times this visual brush is used across all // channels. // Dictionary visualBrushToChannelsMap = VisualBrushToChannelsMapField.GetValue(this); if (visualBrushToChannelsMap == null) { visualBrushToChannelsMap = new Dictionary (); VisualBrushToChannelsMapField.SetValue(this, visualBrushToChannelsMap); } if (!visualBrushToChannelsMap.ContainsKey(visualBrush)) { visualBrushToChannelsMap[visualBrush] = 1; // The visual brush is being used the first time, so also register it. MediaContext.RegisterICompositionTarget(Dispatcher, visualBrush.BrushRegisterToken); } else { Debug.Assert(visualBrushToChannelsMap[visualBrush] > 0); visualBrushToChannelsMap[visualBrush] += 1; } // // Render the brush's visual and update realizatons. // visualBrush.RenderForVisualBrush(channel); } /// /// Override this function in derived classes to release unmanaged resources /// during Dispose and during removal of a subtree. /// internal virtual void ReleaseOnChannelForVisualBrush( VisualBrush visualBrush, DUCE.Channel channel) { // Update the number of times this visual brush uses this visual across all channels. DictionaryvisualBrushToChannelsMap = VisualBrushToChannelsMapField.GetValue(this); Debug.Assert(visualBrushToChannelsMap != null); Debug.Assert(visualBrushToChannelsMap.ContainsKey(visualBrush)); Debug.Assert(visualBrushToChannelsMap[visualBrush] > 0); if (visualBrushToChannelsMap[visualBrush] == 1) { // // If the VisualBrush no longer uses this Visual across all channels, then // we can remove it from the map and also unregister as composition target. // visualBrushToChannelsMap.Remove(visualBrush); MediaContext.UnregisterICompositionTarget(Dispatcher, visualBrush.BrushRegisterToken); } else { // Decrease the number os times this VisualBrush uses this Visual across all channels visualBrushToChannelsMap[visualBrush] = visualBrushToChannelsMap[visualBrush] - 1; } // Decrease the number of VisualBrushes using the visual as root on this channel Dictionary channelsToVisualBrushMap = ChannelsToVisualBrushMapField.GetValue(this); Debug.Assert(channelsToVisualBrushMap != null); Debug.Assert(channelsToVisualBrushMap.ContainsKey(channel)); Debug.Assert(channelsToVisualBrushMap[channel] > 0); channelsToVisualBrushMap[channel] = channelsToVisualBrushMap[channel] - 1; // // If on this channel, there are no more VisualBrushes using this visual as // a root then we need to remove the flag syaing that the visual is a visual // brush root and make sure that the dependant resources are released in // case we are no longer connected to the visual tree. // if (channelsToVisualBrushMap[channel] == 0) { channelsToVisualBrushMap.Remove(channel); SetFlags(false, VisualFlags.NodeIsVisualBrushRoot); PropagateFlags( this, VisualFlags.NodeNeedsBitmapEffectUpdate, VisualProxyFlags.IsSubtreeDirtyForRender); // // If we do not have a parent or we have already disconnected from // the parent and we are also not the root then we need to clear out // the tree. // if ( (_parent == null || !CheckFlagsAnd(channel, VisualProxyFlags.IsConnectedToParent)) && !IsRootElement) { // // We release the Visual directly on channel instead of using the delayed release // mechanism because the VisualBrush is the last reference holding on to it. // We can get here if:- // 1. VisualBrush is being released on channel (already in delayed release walk). // 2. VisualBrush's Visual is being changed (from VisualPropertyChanged). // ((DUCE.IResource)this).ReleaseOnChannel(channel); } } } #endregion Resource Marshalling and Unmarshalling // -------------------------------------------------------------------- // // Access Verification // // -------------------------------------------------------------------- #region Access Verification /// /// Applies various API checks /// internal void VerifyAPIReadOnly() { // Verify that we are executing on the right context VerifyAccess(); } ////// Applies various API checks /// internal void VerifyAPIReadOnly(DependencyObject value) { VerifyAPIReadOnly(); // Make sure the value is on the same context as the visual. // AssertSameContext handles null and Dispatcher-free values. MediaSystem.AssertSameContext(this, value); } ////// Applies various API checks for read/write /// internal void VerifyAPIReadWrite() { VerifyAPIReadOnly(); // Verify the correct access permissions MediaContext.From(this.Dispatcher).VerifyWriteAccess(); } ////// Applies various API checks /// internal void VerifyAPIReadWrite(DependencyObject value) { VerifyAPIReadWrite(); // Make sure the value is on the same context as the visual. // AssertSameContext handles null and Dispatcher-free values. MediaSystem.AssertSameContext(this, value); } #endregion Access Verification // ------------------------------------------------------------------- // // Pre-compute / render / realization passes // // -------------------------------------------------------------------- #region Pre-compute / render / realization passes internal void Precompute() { if (CheckFlagsAnd(VisualFlags.IsSubtreeDirtyForPrecompute)) { // Disable processing of the queue during blocking operations to prevent unrelated reentrancy. using(Dispatcher.DisableProcessing()) { MediaContext mediaContext = MediaContext.From(Dispatcher); try { mediaContext.PushReadOnlyAccess(); Rect bboxSubgraph; PrecomputeRecursive(out bboxSubgraph); } finally { mediaContext.PopReadOnlyAccess(); } } } } ////// Derived class can do precomputations on their content by overriding this method. /// Derived classes must call the base class. /// internal virtual void PrecomputeContent() { _bboxSubgraph = GetHitTestBounds(); // If bounding box has NaN, then we set the bounding box to infinity. if (DoubleUtil.RectHasNaN(_bboxSubgraph)) { _bboxSubgraph.X = Double.NegativeInfinity; _bboxSubgraph.Y = Double.NegativeInfinity; _bboxSubgraph.Width = Double.PositiveInfinity; _bboxSubgraph.Height = Double.PositiveInfinity; } } internal void PrecomputeRecursive(out Rect bboxSubgraph) { // Simple loop detection to avoid stack overflow in cyclic Visual // scenarios. This fix is only aimed at mitigating a very common // VisualBrush scenario. bool canEnter = Enter(); if (canEnter) { try { if (CheckFlagsAnd(VisualFlags.IsSubtreeDirtyForPrecompute)) { // // We need to check which children need realization // updates and/or ink tracking. // bool subTreeUsesRealizationCaches = false; bool subTreeNeedsNewRealization = false; // Reset graphness flag, PrecomputeContent() may modify it SetFlags(false, VisualFlags.NodeOrDescendantIntroducesGraphness); PrecomputeContent(); // // If we have a bitmap effect, we need full realizations // if (VisualBitmapEffect != null) { SetFlags(true, VisualFlags.NodeOrDescendantIntroducesGraphness); } // // VisualFlags.NodeOrDescendantIntroducesGraphness will now be // true if this node has its own graphness inducing content. We // also need to check the children to see if they have graphness // inducing content // bool nodeOrDescendantIntroducesGraphness = CheckFlagsAnd(VisualFlags.NodeOrDescendantIntroducesGraphness); int childCount = VisualChildrenCount; for (int i = 0; i < childCount; i++) { Visual child = GetVisualChild(i); if (child != null) { Rect bboxSubgraphChild; child.PrecomputeRecursive(out bboxSubgraphChild); _bboxSubgraph.Union(bboxSubgraphChild); // // Does this subtree require realization updates // and/or ink tracking? // subTreeUsesRealizationCaches |= child.CheckFlagsAnd(VisualFlags.NodeInSubtreeUsesRealizationCaches); subTreeNeedsNewRealization |= child.CheckFlagsAnd(VisualFlags.NodeInSubtreeRequiresNewRealization); nodeOrDescendantIntroducesGraphness |= child.CheckFlagsAnd(VisualFlags.NodeOrDescendantIntroducesGraphness); } } if (CheckFlagsAnd(VisualFlags.NodeHasBitmapEffect)) { // // Special case for bitmap effects -- expand the bounding box // and always require realization updates. // _bboxSubgraph.Union(BitmapEffectStateField.GetValue(this).GetBounds(_bboxSubgraph)); SetFlags(true, VisualFlags.NodeUsesRealizationCaches); } // Propagate graphness flag SetFlags(nodeOrDescendantIntroducesGraphness, VisualFlags.NodeOrDescendantIntroducesGraphness); // // Update the per-instance visual flags with the new information // about the need to perform realization updates and track ink. // subTreeUsesRealizationCaches |= CheckFlagsAnd(VisualFlags.NodeUsesRealizationCaches); SetFlags(subTreeUsesRealizationCaches, VisualFlags.NodeInSubtreeUsesRealizationCaches); // // Having recursed into the subgraph, we now know definitively whether this node // or its children contain realizations. We need to set the NodeInSubtreeRequiresNewRealization // flags appropriately to allow the MarkVisibleRealization walk to follow the path to the // nodes which require new realizations (NodeRequiresNewRealization, which was set when an // operation requiring new realizations occurred). // if (!CheckFlagsOr(VisualFlags.NodeUsesRealizationCaches | VisualFlags.NodeInSubtreeUsesRealizationCaches)) { SetFlags(false, VisualFlags.NodeRequiresNewRealization); } SetFlags((subTreeNeedsNewRealization && subTreeUsesRealizationCaches) || CheckFlagsAnd(VisualFlags.NodeRequiresNewRealization), VisualFlags.NodeInSubtreeRequiresNewRealization); SetFlags(false, VisualFlags.IsSubtreeDirtyForPrecompute); } // Bounding boxes are cached in inner space (below offset, transform, and clip). // Before returning them we need // to transform them into outer space. bboxSubgraph = _bboxSubgraph; Geometry clip = ClipField.GetValue(this); if (clip != null) { bboxSubgraph.Intersect(clip.Bounds); } Transform transform = TransformField.GetValue(this); if ((transform != null) && (!transform.IsIdentity)) { Matrix m = transform.Value; MatrixUtil.TransformRect(ref bboxSubgraph, ref m); } if (!bboxSubgraph.IsEmpty) { bboxSubgraph.X += _offset.X; bboxSubgraph.Y += _offset.Y; } // If child's bounding box has NaN, then we set the bounding box to infinity. if (DoubleUtil.RectHasNaN(bboxSubgraph)) { bboxSubgraph.X = Double.NegativeInfinity; bboxSubgraph.Y = Double.NegativeInfinity; bboxSubgraph.Width = Double.PositiveInfinity; bboxSubgraph.Height = Double.PositiveInfinity; } } finally { Exit(); } } else { bboxSubgraph = new Rect(); } } internal void Render(RenderContext ctx, UInt32 childIndex) { DUCE.Channel channel = ctx.Channel; // // Currently everything is sent to the compositor. IsSubtreeDirtyForRender // indicates that something in the sub-graph of this Visual needs to have an update // sent to the compositor. Hence traverse if this bit is set. Also traverse when the // sub-graph has not yet been sent to the compositor. // if (CheckFlagsAnd(channel, VisualProxyFlags.IsSubtreeDirtyForRender) || !IsOnChannel(channel)) { RenderRecursive(ctx); } // // Connect the root visual to the composition root if necessary. // if (IsOnChannel(channel) && !CheckFlagsAnd(channel, VisualProxyFlags.IsConnectedToParent) && !ctx.Root.IsNull) { DUCE.CompositionNode.InsertChildAt( ctx.Root, _proxy.GetHandle(channel), childIndex, channel); SetFlags( channel, true, VisualProxyFlags.IsConnectedToParent); } } ////// This is only called during the realization pass if the visual has /// a bitmap effect. It only renders opacity, opacity mask, content and /// children of the visual. The effect is applied below transform, /// clip, offset and guidelines. Those properties are updated during /// the render pass /// /// /// internal void RenderForBitmapEffect(RenderContext ctx, UInt32 childIndex) { DUCE.Channel channel = ctx.Channel; // // See if this visual is already on that channel // bool isOnChannel = IsOnChannel(channel); // // Currently everything is sent to the compositor. IsSubtreeDirtyForRender // indicates that something in the sub-graph of this Visual needs to have an update // sent to the compositor. Hence traverse if this bit is set. Also traverse when the // sub-graph has not yet been sent to the compositor. // if (CheckFlagsAnd(channel, VisualProxyFlags.IsSubtreeDirtyForRender) || !isOnChannel) { // // Ensure that the visual resource for this Visual // is being sent to our current channel. // DUCE.ResourceHandle handle = isOnChannel ? _proxy.GetHandle(channel) : ((DUCE.IResource)this).AddRefOnChannel(channel); VisualProxyFlags flags = isOnChannel ? _proxy.GetFlags(channel) : c_ProxyFlagsDirtyMask; if (!isOnChannel) { // we need to set the Viewport3D flags, if the visual is not // on channel so that the viewport sends all its resources // to the compositor SetFlags(channel, true, c_Viewport3DProxyFlagsDirtyMask); } // Do the updates UpdateContent(ctx, flags, isOnChannel); UpdateOpacity(channel, handle, flags, isOnChannel); UpdateOpacityMask(channel, handle, flags, isOnChannel); UpdateChildren(ctx, handle); // // Finally, reset the dirty flags for this visual (at this point, // we have handled them all). // SetFlags(channel, false, VisualProxyFlags.IsSubtreeDirtyForRender); } // // Connect the root visual to the composition root if necessary. // if (IsOnChannel(channel) && !CheckFlagsAnd(channel, VisualProxyFlags.IsConnectedToParent) && !ctx.Root.IsNull) { DUCE.CompositionNode.InsertChildAt( ctx.Root, _proxy.GetHandle(channel), childIndex, channel); SetFlags( channel, true, VisualProxyFlags.IsConnectedToParent); } } internal virtual void RenderRecursive( RenderContext ctx) { // Simple loop detection to avoid stack overflow in cyclic Visual // scenarios. This fix is only aimed at mitigating a very common // VisualBrush scenario. bool canEnter = Enter(); if (canEnter) { try { DUCE.Channel channel = ctx.Channel; DUCE.ResourceHandle handle = DUCE.ResourceHandle.Null; VisualProxyFlags flags = VisualProxyFlags.None; // // See if this visual is already on that channel // bool isOnChannel = IsOnChannel(channel); // // Ensure that the visual resource for this Visual // is being sent to our current channel. // if (isOnChannel) { // // Good, we're already on channel. Get the handle and flags. // handle = _proxy.GetHandle(channel); flags = _proxy.GetFlags(channel); } else { // // Create the visual resource on the current channel. // // Need to update all set properties. // handle = ((DUCE.IResource)this).AddRefOnChannel(channel); // we need to set the Viewport3D flags, if the visual is not // on channel so that the viewport sends all its resources // to the compositor. we need the explicit set, because // the update happens during RenderContent and we have no // other way to pass the flags // // We do that for all visuals. the flags will be ignored // if the visual is not a Viewport3D visual SetFlags(channel, true, c_Viewport3DProxyFlagsDirtyMask); flags = c_ProxyFlagsDirtyMask; } UpdateTransform(channel, handle, flags, isOnChannel); UpdateClip(channel, handle, flags, isOnChannel); UpdateOffset(channel, handle, flags, isOnChannel); UpdateGuidelines(channel, handle, flags, isOnChannel); // if we have a bitmap effect, mark the effect as dirty // we will render the rest of the visual during // the realization pass if (CheckFlagsAnd(VisualFlags.NodeHasBitmapEffect)) { // if the visual content is dirty or the subtree is dirty for render // or the opacity or opacity mask changed, // we would want to re-apply the effect during the realisation pass if (((flags & c_BitmapEffectDirtyMask) != 0) || CheckFlagsAnd(VisualFlags.NodeNeedsBitmapEffectUpdate)) { SetFlags(channel, true, VisualProxyFlags.IsBitmapEffectDirty); } } else { UpdateContent(ctx, flags, isOnChannel); UpdateOpacity(channel, handle, flags, isOnChannel); UpdateOpacityMask(channel, handle, flags, isOnChannel); UpdateRenderOptions(channel, handle, flags, isOnChannel); UpdateChildren(ctx, handle); } // // Finally, reset the dirty flags for this visual (at this point, // we have handled them all). SetFlags(channel, false, VisualProxyFlags.IsSubtreeDirtyForRender); SetFlags(false, VisualFlags.NodeNeedsBitmapEffectUpdate); } finally { Exit(); } } } ////// Enter is used for simple cycle detection in Visual. If the method returns false /// the Visual has already been entered and cannot be entered again. Matching invocation of Exit /// must be skipped if Enter returns false. /// internal bool Enter() { if (CheckFlagsAnd(VisualFlags.ReentrancyFlag)) { return false; } else { SetFlags(true, VisualFlags.ReentrancyFlag); return true; } } ////// Exits the Visual. For more details see Enter method. /// internal void Exit() { Debug.Assert(CheckFlagsAnd(VisualFlags.ReentrancyFlag)); // Exit must be matched with Enter. See Enter comments. SetFlags(false, VisualFlags.ReentrancyFlag); } ////// Update opacity /// /// /// /// /// private void UpdateOpacity(DUCE.Channel channel, DUCE.ResourceHandle handle, VisualProxyFlags flags, bool isOnChannel) { // Opacity --------------------------------------------------------------------------- if ((flags & VisualProxyFlags.IsOpacityDirty) != 0) { double opacity = OpacityCache; if (isOnChannel || !(opacity >= 1.0)) { // // Opacity is 1.0 by default -- do not send it for new visuals. // DUCE.CompositionNode.SetAlpha( handle, opacity, channel); } SetFlags(channel, false, VisualProxyFlags.IsOpacityDirty); } } ////// Update OpacityMask /// /// /// /// /// The Visual exists on channel. private void UpdateOpacityMask(DUCE.Channel channel, DUCE.ResourceHandle handle, VisualProxyFlags flags, bool isOnChannel) { // Opacity Mask --------------------------------------------------------------------------- if ((flags & VisualProxyFlags.IsOpacityMaskDirty) != 0) { Brush opacityMask = OpacityMaskField.GetValue(this); if (opacityMask != null) { // // Set the new opacity mask resource on the visual. // If opacityMask is null we don't need to do this. // Also note that the old opacity mask was disconnected // in the OpacityMask property setter. // DUCE.CompositionNode.SetAlphaMask( handle, ((DUCE.IResource)opacityMask).AddRefOnChannel(channel), channel); } else if (isOnChannel) /* opacityMask == null */ { DUCE.CompositionNode.SetAlphaMask( handle, DUCE.ResourceHandle.Null, channel); } SetFlags(channel, false, VisualProxyFlags.IsOpacityMaskDirty); } } ////// Update transform /// /// /// /// /// The Visual exists on channel. private void UpdateTransform(DUCE.Channel channel, DUCE.ResourceHandle handle, VisualProxyFlags flags, bool isOnChannel) { // Transform ------------------------------------------------------------------------------- if ((flags & VisualProxyFlags.IsTransformDirty) != 0) { Transform transform = TransformField.GetValue(this); if (transform != null) { // // Set the new transform resource on the visual. // If transform is null we don't need to do this. // Also note that the old transform was disconnected // in the Transform property setter. // DUCE.CompositionNode.SetTransform( handle, ((DUCE.IResource)transform).AddRefOnChannel(channel), channel); } else if (isOnChannel) /* transform == null */ { DUCE.CompositionNode.SetTransform( handle, DUCE.ResourceHandle.Null, channel); } SetFlags(channel, false, VisualProxyFlags.IsTransformDirty); } } ////// Update clip /// /// /// /// /// The Visual exists on channel. private void UpdateClip(DUCE.Channel channel, DUCE.ResourceHandle handle, VisualProxyFlags flags, bool isOnChannel) { // Clip ------------------------------------------------------------------------------------ if ((flags & VisualProxyFlags.IsClipDirty) != 0) { Geometry clip = ClipField.GetValue(this); if (clip != null) { // // Set the new clip resource on the composition node. // If clip is null we don't need to do this. Also note // that the old clip was disconnected in the Clip // property setter. // DUCE.CompositionNode.SetClip( handle, ((DUCE.IResource)clip).AddRefOnChannel(channel), channel); } else if (isOnChannel) /* clip == null */ { DUCE.CompositionNode.SetClip( handle, DUCE.ResourceHandle.Null, channel); } SetFlags(channel, false, VisualProxyFlags.IsClipDirty); } } ////// Update offset /// /// /// /// /// private void UpdateOffset(DUCE.Channel channel, DUCE.ResourceHandle handle, VisualProxyFlags flags, bool isOnChannel) { // Offset ------------------------------------------------------------------------------------------- if ((flags & VisualProxyFlags.IsOffsetDirty) != 0) { if (isOnChannel || _offset != new Vector()) { // // Offset is (0, 0) by default so do not update it for new visuals. // DUCE.CompositionNode.SetOffset( handle, _offset.X, _offset.Y, channel); } SetFlags(channel, false, VisualProxyFlags.IsOffsetDirty); } } ////// Update guidelines /// /// /// /// /// private void UpdateGuidelines(DUCE.Channel channel, DUCE.ResourceHandle handle, VisualProxyFlags flags, bool isOnChannel) { // Guidelines -------------------------------------------------------------------- if ((flags & VisualProxyFlags.IsGuidelineCollectionDirty) != 0) { DoubleCollection guidelinesX = GuidelinesXField.GetValue(this); DoubleCollection guidelinesY = GuidelinesYField.GetValue(this); if (isOnChannel || (guidelinesX != null || guidelinesY != null)) { // // Guidelines are null by default, so do not update them for new visuals. // DUCE.CompositionNode.SetGuidelineCollection( handle, guidelinesX, guidelinesY, channel); } SetFlags(channel, false, VisualProxyFlags.IsGuidelineCollectionDirty); } } ////// Update EdgeMode /// /// /// /// /// private void UpdateRenderOptions(DUCE.Channel channel, DUCE.ResourceHandle handle, VisualProxyFlags flags, bool isOnChannel) { if (((flags & VisualProxyFlags.IsEdgeModeDirty) != 0) || ((flags & VisualProxyFlags.IsBitmapScalingModeDirty) != 0)) { MILRenderOptions renderOptions = new MILRenderOptions(); // EdgeMode ---------------------------------------------------------------------------- // "isOnChannel" (if true) indicates that this Visual was on channel // previous to this update. If this is the case, all changes to the EdgeMode // must be reflected in the composition node. If "isOnChannel" is false it means // that this Visual has just been added to a channel. In this case, we can // skip an EdgeMode update if the EdgeMode is Unspecified, as this is the default // behavior. if (isOnChannel || (EdgeModeCache != EdgeMode.Unspecified)) { renderOptions.Flags |= MILRenderOptionFlags.EdgeMode; renderOptions.EdgeMode = EdgeModeCache; } // ImageScalingMode --------------------------------------------------------------------------- if (isOnChannel || (BitmapScalingModeCache != BitmapScalingMode.Unspecified)) { renderOptions.Flags |= MILRenderOptionFlags.BitmapScalingMode; renderOptions.BitmapScalingMode = BitmapScalingModeCache; } if (renderOptions.Flags != 0) { DUCE.CompositionNode.SetRenderOptions( handle, renderOptions, channel); } SetFlags(channel, false, VisualProxyFlags.IsEdgeModeDirty | VisualProxyFlags.IsBitmapScalingModeDirty); } } ////// Update content /// /// /// /// The Visual exists on channel. private void UpdateContent(RenderContext ctx, VisualProxyFlags flags, bool isOnChannel) { // // Hookup content to the Visual // if ((flags & VisualProxyFlags.IsContentDirty) != 0) { RenderContent(ctx, isOnChannel); SetFlags(ctx.Channel, false, VisualProxyFlags.IsContentDirty); } } ////// Update children /// /// /// private void UpdateChildren(RenderContext ctx, DUCE.ResourceHandle handle) { DUCE.Channel channel = ctx.Channel; // // Visit children of this visual. // // // If content node is connected child node indicies need to be offset by one. // UInt32 connectedChildIndex = CheckFlagsAnd(channel, VisualProxyFlags.IsContentNodeConnected) ? (UInt32)1 : 0; int childCount = VisualChildrenCount; for (int i = 0; i < childCount; i++) { Visual child = GetVisualChild(i); if (child != null) { // // Recurse if the child visual is dirty // or it has not been marshalled yet. // if (child.CheckFlagsAnd(channel, VisualProxyFlags.IsSubtreeDirtyForRender) || !(child.IsOnChannel(channel))) { child.RenderRecursive(ctx); } // // Make sure that all the marshaled children are // connected to the parent visual. // if (child.IsOnChannel(channel)) { if (!child.CheckFlagsAnd(channel, VisualProxyFlags.IsConnectedToParent)) { DUCE.CompositionNode.InsertChildAt( handle, ((DUCE.IResource)child).GetHandle(channel), connectedChildIndex, channel); child.SetFlags( channel, true, VisualProxyFlags.IsConnectedToParent); } connectedChildIndex++; } } } } ////// Marks the visible realizations in this visual graph. /// /// The realization context. ////// internal void MarkVisibleRealizations(RealizationContext ctx) { Debug.Assert(IsOnChannels(ctx.Channels)); if ( CheckFlagsAnd(VisualFlags.NodeInSubtreeUsesRealizationCaches) || CheckFlagsAnd(VisualFlags.NodeHasBitmapEffect)) { MarkVisibleRealizationsRecursive(ctx); } } internal bool RequiresRealizationUpdates { get { return CheckFlagsAnd(VisualFlags.NodeInSubtreeUsesRealizationCaches); } } /// /// Marks the visible realizations in this visual graph. /// This is only called during the realization pass if the visual has /// a bitmap effect. /// /// The realization context. ////// internal void MarkVisibleRealizationsForBitmapEffect(RealizationContext ctx) { // ---------------------------------------------------------------- // Update realizations. if (CheckFlagsAnd(VisualFlags.NodeUsesRealizationCaches)) { UpdateRealizations(ctx); } // --------------------------------------------------------------- // Recurse into the visible part of the subgraph. MarkVisibleRealizationsForChildren(ctx); } /// /// Performs a recursive walk of the visible part of this visual graph, /// marking the realization nodes to be created or updated. /// ////// Do not call this method directly. /// /// The realization context. ///private void MarkVisibleRealizationsRecursive(RealizationContext ctx) { // Simple loop detection to avoid stack overflow in cyclic Visual // scenarios. This fix is only aimed at mitigating a very common // VisualBrush scenario. bool canEnter = Enter(); if (canEnter) { try { #if TRACE MarkVisibleRealizationsCount++; #endif // --------------------------------------------------------------- // Update the transform stack (needed by UpdateRealizations). //push offset if (_offset != new Vector()) { ctx.TransformStack.Push(_offset, true); } Transform transform = TransformField.GetValue(this); if (transform != null) { ctx.TransformStack.Push(transform, true); } if (CheckFlagsAnd(VisualFlags.NodeHasBitmapEffect)) { // if the visual has a bitmap effect, update the realizations. UpdateBitmapEffectRealizations(ctx); } else { bool nodeRequiresNewRealization = CheckFlagsAnd(VisualFlags.NodeRequiresNewRealization); // --------------------------------------------------------------- // Update realizations if NodeUsesRealizationCaches is true and // we're either doing a full walk (!ctx.IncrementalWalk) or // we're doing an incremental walk and the node actually requires // new realizations (nodeRequiresNewRealization) if ( CheckFlagsAnd(VisualFlags.NodeUsesRealizationCaches) && (!ctx.IncrementalWalk || nodeRequiresNewRealization)) { UpdateRealizations(ctx); ctx.CheckVisualRequiresNextFrameRealizations(this); } if (nodeRequiresNewRealization) { // Need to walk all children containing realizations since // this node has had a realizations impacting change ctx.DisableIncrementalWalk(); } MarkVisibleRealizationsForChildren(ctx); if (nodeRequiresNewRealization) { ctx.EnableIncrementalWalk(); } } // ---------------------------------------------------------------- // Clean up. if (transform != null) { ctx.TransformStack.Pop(); } // pop offset if (_offset != new Vector()) { ctx.TransformStack.Pop(); } } finally { // Normally, we reset the flags for node realization to avoid redundant processing of realizations. // However, in the case of a bitmap render (RenderTargetBitmap), don't reset the flags here, since // we would still need to apply the realizations when a regular render occurs later. if (!ctx.WalkForBitmapRenderTarget) { SetFlags(false, VisualFlags.NodeRequiresNewRealization | VisualFlags.NodeInSubtreeRequiresNewRealization); } Exit(); } } } /// /// MarkVisibleRealizations for all the children /// /// private void MarkVisibleRealizationsForChildren(RealizationContext ctx) { // --------------------------------------------------------------- // Recurse into the visible part of the subgraph. int childrenCount = this.VisualChildrenCount; for (int i = 0; i < childrenCount; i++) { Visual child = this.GetVisualChild(i); // Skip this node if currently not on the channel or // if there is nothing to be updated on its subtree. // If this walk is incremental, then walk child // only if subtree has a node requiring a new realization if (child != null && child.CheckFlagsAnd(VisualFlags.NodeInSubtreeUsesRealizationCaches) && (!ctx.IncrementalWalk || child.CheckFlagsOr(VisualFlags.NodeInSubtreeRequiresNewRealization))) { Debug.Assert(child.IsOnChannels(ctx.Channels)); child.MarkVisibleRealizationsRecursive(ctx); } } } #endregion Pre-compute / render / realization passes // -------------------------------------------------------------------- // // Hit Testing // // -------------------------------------------------------------------- #region Hit Testing internal class TopMostHitResult { internal HitTestResult _hitResult = null; internal HitTestResultBehavior HitTestResult(HitTestResult result) { _hitResult = result; return HitTestResultBehavior.Stop; } internal HitTestFilterBehavior NoNested2DFilter(DependencyObject potentialHitTestTarget) { if (potentialHitTestTarget is Viewport2DVisual3D) { return HitTestFilterBehavior.ContinueSkipChildren; } return HitTestFilterBehavior.Continue; } } ////// Used by derived classes to invalidate their hit-test bounds. /// internal void InvalidateHitTestBounds() { VerifyAPIReadWrite(); PropagateFlags( this, VisualFlags.IsSubtreeDirtyForPrecompute, VisualProxyFlags.None); } ////// Derived classes return the hit-test bounding box from the /// GetHitTestBounds virtual. Visual uses the bounds to optimize /// hit-testing. /// internal virtual Rect GetHitTestBounds() { return GetContentBounds(); } ////// Return top most visual of a hit test. /// internal HitTestResult HitTest(Point point) { return HitTest(point, true); } ////// Return top most visual of a hit test. If include2DOn3D is true we will /// hit test in to 2D on 3D children, otherwise we will ignore that part of /// the tree. /// internal HitTestResult HitTest(Point point, bool include2DOn3D) { // TopMostHitResult result = new TopMostHitResult(); VisualTreeHelper.HitTest( this, include2DOn3D? null : new HitTestFilterCallback(result.NoNested2DFilter), new HitTestResultCallback(result.HitTestResult), new PointHitTestParameters(point)); return result._hitResult; } ////// Initiate a hit test using delegates. /// internal void HitTest( HitTestFilterCallback filterCallback, HitTestResultCallback resultCallback, HitTestParameters hitTestParameters) { if (resultCallback == null) { throw new ArgumentNullException("resultCallback"); } if (hitTestParameters == null) { throw new ArgumentNullException("hitTestParameters"); } VerifyAPIReadWrite(); Precompute(); PointHitTestParameters pointParams = hitTestParameters as PointHitTestParameters; if (pointParams != null) { // Because we call dynamic code during the hit testing walk we need to back up // the original hit point in case the user's delegate throws an exception so that // we can restore it. Point backupHitPoint = pointParams.HitPoint; try { HitTestPoint(filterCallback, resultCallback, pointParams); } catch { // If an exception occured, restore the user's hit point and rethrow. pointParams.SetHitPoint(backupHitPoint); throw; } finally { Debug.Assert(Point.Equals(pointParams.HitPoint, backupHitPoint), "Failed to restore user's hit point back to the original coordinate system."); } } else { GeometryHitTestParameters geometryParams = hitTestParameters as GeometryHitTestParameters; if (geometryParams != null) { // Because we call dynamic code during the hit testing walk we need to ensure // that if the user's delegate throws an exception we restore the original // transform on the hit test geometry. #if DEBUG // Internally we replace the hit geometry with a copy which is guaranteed to have // a MatrixTransform so we do not need to worry about null dereferences here. Matrix originalMatrix = geometryParams.InternalHitGeometry.Transform.Value; #endif // DEBUG try { HitTestGeometry(filterCallback, resultCallback, geometryParams); } catch { geometryParams.EmergencyRestoreOriginalTransform(); throw; } #if DEBUG finally { Debug.Assert(Matrix.Equals(geometryParams.InternalHitGeometry.Transform.Value, originalMatrix), "Failed to restore user's hit geometry back to the original coordinate system."); } #endif // DEBUG } else { // This should never happen, users can not extend the abstract HitTestParameters class. Invariant.Assert(false, String.Format(System.Globalization.CultureInfo.InvariantCulture, "'{0}' HitTestParameters are not supported on {1}.", hitTestParameters.GetType().Name, this.GetType().Name)); } } } internal HitTestResultBehavior HitTestPoint( HitTestFilterCallback filterCallback, HitTestResultCallback resultCallback, PointHitTestParameters pointParams) { // we do not need parameter checks because they are done in HitTest() Geometry clip = VisualClip; // Before we continue hit-testing we check against the hit-test bounds for the sub-graph. // If the point is not with-in the hit-test bounds, the sub-graph can be skipped. if (_bboxSubgraph.Contains(pointParams.HitPoint) && ((null == clip) || clip.FillContains(pointParams.HitPoint))) // Check that the hit-point is with-in the clip. { // // Determine if there is a special filter behavior defined for this // Visual. // HitTestFilterBehavior filter = HitTestFilterBehavior.Continue; if (filterCallback != null) { filter = filterCallback(this); if (filter == HitTestFilterBehavior.ContinueSkipSelfAndChildren) { return HitTestResultBehavior.Continue; } if (filter == HitTestFilterBehavior.Stop) { return HitTestResultBehavior.Stop; } } // if there is a bitmap effect transform the point // Backup the hit point so that we can restore it later on. Point originalHitPoint = pointParams.HitPoint; Point hitPoint = originalHitPoint; if (CheckFlagsAnd(VisualFlags.NodeHasBitmapEffect)) { if (BitmapEffectStateField.GetValue(this).TransformHitPoint( _bboxSubgraph, originalHitPoint, out hitPoint) == false) { return HitTestResultBehavior.Continue; } } // // Hit test against the children. // if (filter != HitTestFilterBehavior.ContinueSkipChildren) { int childCount = VisualChildrenCount; for (int i=childCount-1; i>=0; i--) { Visual child = GetVisualChild(i); if (child != null) { // // Transform the hit-test point below offset and transform. // Point newHitPoint = hitPoint; // Apply the offset. newHitPoint = newHitPoint - child._offset; // If we have a transform, apply the transform. Transform childTransform = TransformField.GetValue(child); if (childTransform != null) { Matrix inv = childTransform.Value; // If we can't invert the transform, the child is not hitable. This makes sense since // the node's rendered content is degenerate, i.e. does not really take up any space. // Skip the child by continuing in the loop. if (!inv.HasInverse) { continue; } inv.Invert(); newHitPoint = newHitPoint * inv; } // Set the new hittesting point into the hittest params. pointParams.SetHitPoint(newHitPoint); // Perform the hit-test against the child. HitTestResultBehavior result = child.HitTestPoint(filterCallback, resultCallback, pointParams); // Restore the hit-test point. pointParams.SetHitPoint(originalHitPoint); if (result == HitTestResultBehavior.Stop) { return HitTestResultBehavior.Stop; } } } } // // Hit test against the content of this Visual. // if (filter != HitTestFilterBehavior.ContinueSkipSelf) { // set the transformed hit point pointParams.SetHitPoint(hitPoint); HitTestResultBehavior result = HitTestPointInternal(filterCallback, resultCallback, pointParams); // restore the hit point back to its original pointParams.SetHitPoint(originalHitPoint); if (result == HitTestResultBehavior.Stop) { return HitTestResultBehavior.Stop; } } } return HitTestResultBehavior.Continue; } // provides a transform that goes between the Visual's coordinate space // and that after applying the transforms that bring it to outer space. internal GeneralTransform TransformToOuterSpace() { Matrix m = Matrix.Identity; GeneralTransformGroup group = null; GeneralTransform result = null; if (CheckFlagsAnd(VisualFlags.NodeHasBitmapEffect)) { BitmapEffectVisualState bitmapEffectState = BitmapEffectStateField.GetValue(this); BitmapEffect effect = bitmapEffectState.BitmapEffect; // if the BitmapEffect has an affine transformation // add code to just multiply the matrix here to the effects matrix if (effect.IsAffineTransform) { Matrix cm = effect.GetAffineMatrix(); MatrixUtil.MultiplyMatrix(ref m, ref cm); } else { group = new GeneralTransformGroup(); group.Children.Add(new BitmapEffectGeneralTransform(bitmapEffectState.BitmapEffect, bitmapEffectState.BitmapEffectInput, false, VisualContentBounds)); } } Transform transform = TransformField.GetValue(this); if (transform != null) { Matrix cm = transform.Value; MatrixUtil.MultiplyMatrix(ref m, ref cm); } m.Translate(_offset.X, _offset.Y); // Consider having a bit that indicates that we have a non-null offset. if (group == null) { result = new MatrixTransform(m); } else { group.Children.Add(new MatrixTransform(m)); result = group; } result.Freeze(); return result; } internal HitTestResultBehavior HitTestGeometry( HitTestFilterCallback filterCallback, HitTestResultCallback resultCallback, GeometryHitTestParameters geometryParams) { // we do not need parameter checks because they are done in HitTest() // Check against the clip before checking against geometry IntersectionDetail intersectionDetail; Geometry clip = VisualClip; if (clip != null) { // HitTest with a Geometry and a clip should hit test with // the intersection of the geometry and the clip, not the entire geometry intersectionDetail = clip.FillContainsWithDetail(geometryParams.InternalHitGeometry); Debug.Assert(intersectionDetail != IntersectionDetail.NotCalculated); if (intersectionDetail == IntersectionDetail.Empty) { // bail out if there is a clip and this region is not inside return HitTestResultBehavior.Continue; } } // // Check if the geometry intersects with our hittest bounds. // If not, the Visual is not hit-testable at all. if (_bboxSubgraph.IntersectsWith(geometryParams.Bounds)) { // // Determine if there is a special filter behavior defined for this // Visual. // HitTestFilterBehavior filter = HitTestFilterBehavior.Continue; if (filterCallback != null) { filter = filterCallback(this); if (filter == HitTestFilterBehavior.ContinueSkipSelfAndChildren) { return HitTestResultBehavior.Continue; } if (filter == HitTestFilterBehavior.Stop) { return HitTestResultBehavior.Stop; } } // // Hit-test against the children. // int childCount = VisualChildrenCount; if (filter != HitTestFilterBehavior.ContinueSkipChildren) { for (int i=childCount-1; i>=0; i--) { Visual child = GetVisualChild(i); if (child != null) { // Transform the geometry below offset and transform. Matrix inv = Matrix.Identity; inv.Translate(-child._offset.X, -child._offset.Y); Transform childTransform = TransformField.GetValue(child); if (childTransform != null) { Matrix m = childTransform.Value; // If we can't invert the transform, the child is not hitable. This makes sense since // the node's rendered content is degnerated, i.e. does not really take up any space. // Skipping the child by continuing the loop. if (!m.HasInverse) { continue; } // Inverse the transform. m.Invert(); // Multiply the inverse and the offset together. // inv = inv * m; MatrixUtil.MultiplyMatrix(ref inv, ref m); } // Push the transform on the geometry params. geometryParams.PushMatrix(ref inv); // Hit-Test against the children. HitTestResultBehavior result = child.HitTestGeometry(filterCallback, resultCallback, geometryParams); // Pop the transform from the geometry params. geometryParams.PopMatrix(); // Process the result. if (result == HitTestResultBehavior.Stop) { return HitTestResultBehavior.Stop; } } } } // // Hit-test against the content of the Visual. // if (filter != HitTestFilterBehavior.ContinueSkipSelf) { GeometryHitTestResult hitResult = HitTestCore(geometryParams); if (hitResult != null) { Debug.Assert(resultCallback != null); return resultCallback(hitResult); } } } return HitTestResultBehavior.Continue; } ////// This method provides an internal extension point for Viewport3DVisual /// to grab the HitTestFilterCallback and ResultDelegate before it gets lost in the /// forward to HitTestCore. /// internal virtual HitTestResultBehavior HitTestPointInternal( HitTestFilterCallback filterCallback, HitTestResultCallback resultCallback, PointHitTestParameters hitTestParameters) { HitTestResult hitResult = HitTestCore(hitTestParameters); if (hitResult != null) { return resultCallback(hitResult); } return HitTestResultBehavior.Continue; } ////// HitTestCore implements whether we have hit the bounds of this visual. /// protected virtual HitTestResult HitTestCore(PointHitTestParameters hitTestParameters) { if (hitTestParameters == null) { throw new ArgumentNullException("hitTestParameters"); } // If we don't have a clip, or if the clip contains the point, keep going. if (GetHitTestBounds().Contains(hitTestParameters.HitPoint)) { return new PointHitTestResult(this, hitTestParameters.HitPoint); } else { return null; } } ////// HitTestCore implements whether we have hit the bounds of this visual. /// protected virtual GeometryHitTestResult HitTestCore(GeometryHitTestParameters hitTestParameters) { if (hitTestParameters == null) { throw new ArgumentNullException("hitTestParameters"); } IntersectionDetail intersectionDetail; RectangleGeometry contentGeometry = new RectangleGeometry(GetHitTestBounds()); intersectionDetail = contentGeometry.FillContainsWithDetail(hitTestParameters.InternalHitGeometry); Debug.Assert(intersectionDetail != IntersectionDetail.NotCalculated); if (intersectionDetail != IntersectionDetail.Empty) { return new GeometryHitTestResult(this, intersectionDetail); } return null; } #endregion Hit Testing // ------------------------------------------------------------------- // // Visual Operations API // // -------------------------------------------------------------------- #region VisualChildren ////// Derived classes override this property to enable the Visual code to enumerate /// the Visual children. Derived classes need to return the number of children /// from this method. /// /// By default a Visual does not have any children. /// /// Remark: During this virtual method the Visual tree must not be modified. /// protected virtual int VisualChildrenCount { get { return 0; } } ////// Returns the number of 2D children. This returns 0 for visuals /// whose children are Visual3Ds. /// internal int InternalVisualChildrenCount { get { // Call the right virtual method. return VisualChildrenCount; } } ////// Returns the number of children of this object (in most cases this will be /// the number of Visuals, but it some cases, Viewport3DVisual for instance, /// this is the number of Visual3Ds). /// /// Used only by VisualTreeHelper. /// internal virtual int InternalVisual2DOr3DChildrenCount { get { // Call the right virtual method. return VisualChildrenCount; } } //////Flag to check if this visual has any children /// internal bool HasVisualChildren { get { return ((_flags & VisualFlags.HasChildren) != 0); } } ////// Derived class must implement to support Visual children. The method must return /// the child at the specified index. Index must be between 0 and GetVisualChildrenCount-1. /// /// By default a Visual does not have any children. /// /// Remark: /// Need to lock down Visual tree during the callbacks. /// During this virtual call it is not valid to modify the Visual tree. /// /// It is okay to type this protected API to the 2D Visual. The only 2D Visual with /// 3D childern is the Viewport3DVisual which is sealed. -- [....] 01/17/06 /// protected virtual Visual GetVisualChild(int index) { throw new ArgumentOutOfRangeException("index", index, SR.Get(SRID.Visual_ArgumentOutOfRange)); } ////// Returns the 2D child at index "index". This will fail for Visuals /// whose children are Visual3Ds. /// internal Visual InternalGetVisualChild(int index) { // Call the right virtual method. return GetVisualChild(index); } ////// Returns the child at index "index" (in most cases this will be /// a Visual, but it some cases, Viewport3DVisual for instance, /// this is a Visual3D). /// /// Used only by VisualTreeHelper. /// internal virtual DependencyObject InternalGet2DOr3DVisualChild(int index) { // Call the right virtual method. return GetVisualChild(index); } ////// Helper method to provide access to AddVisualChild for the VisualCollection. /// internal void InternalAddVisualChild(Visual child) { this.AddVisualChild(child); } ////// Helper method to provide access to RemoveVisualChild for the VisualCollection. /// internal void InternalRemoveVisualChild(Visual child) { this.RemoveVisualChild(child); } ////// AttachChild /// /// Derived classes must call this method to notify the Visual layer that a new /// child appeard in the children collection. The Visual layer will then call the GetVisualChild /// method to find out where the child was added. /// /// Remark: To move a Visual child in a collection it must be first disconnected and then connected /// again. (Moving forward we might want to add a special optimization there so that we do not /// unmarshal our composition resources). /// /// It is okay to type this protected API to the 2D Visual. The only 2D Visual with /// 3D childern is the Viewport3DVisual which is sealed. -- [....] 01/17/06 /// protected void AddVisualChild(Visual child) { if (child == null) { return; } if (child._parent != null) { throw new ArgumentException(SR.Get(SRID.Visual_HasParent)); } SetFlags(true, VisualFlags.HasChildren); // Set the parent pointer. child._parent = this; // // The child might be dirty. Hence we need to propagate dirty information // from the parent and from the child. // Visual.PropagateFlags( this, VisualFlags.IsSubtreeDirtyForPrecompute | VisualFlags.NodeNeedsBitmapEffectUpdate, VisualProxyFlags.IsSubtreeDirtyForRender); // // If child has realizations, we need to set NodeRequiresRealizationUpdate flag, and propagate // corresponding subtree flag // child.SetFlags(true, VisualFlags.NodeRequiresNewRealization); Visual.PropagateFlags( child, VisualFlags.IsSubtreeDirtyForPrecompute | VisualFlags.NodeNeedsBitmapEffectUpdate | VisualFlags.NodeInSubtreeRequiresNewRealization, VisualProxyFlags.IsSubtreeDirtyForRender); // // Resume layout. // UIElement.PropagateResumeLayout(this, child); // Fire notifications this.OnVisualChildrenChanged(child, null /* no removed child */); child.FireOnVisualParentChanged(null); } ////// DisconnectChild /// /// Derived classes must call this method to notify the Visual layer that a /// child was removed from the children collection. The Visual layer will then call /// GetChildren to find out which child has been removed. /// /// protected void RemoveVisualChild(Visual child) { if (child == null || child._parent == null) { return; } if (child._parent != this) { throw new ArgumentException(SR.Get(SRID.Visual_NotChild)); } if(InternalVisual2DOr3DChildrenCount == 0) { SetFlags(false, VisualFlags.HasChildren); } // // Remove the child on all channels its current parent is marshalled to. // for (int i = 0; i < _proxy.Count; i++) { DUCE.Channel channel = _proxy.GetChannel(i); if (child.CheckFlagsAnd(channel, VisualProxyFlags.IsConnectedToParent)) { channel.AddToRemoveAndReleaseQueue( this, child); child.SetFlags(channel, false, VisualProxyFlags.IsConnectedToParent); } } // Set the parent pointer to null. child._parent = null; Visual.PropagateFlags( this, VisualFlags.IsSubtreeDirtyForPrecompute | VisualFlags.NodeNeedsBitmapEffectUpdate, VisualProxyFlags.IsSubtreeDirtyForRender); UIElement.PropagateSuspendLayout(child); // Fire notifications child.FireOnVisualParentChanged(this); OnVisualChildrenChanged(null /* no child added */, child); } ////// InvalidateZOrder /// Note: must do invalidation without removing / adding /// to avoid loosing focused element by input system /// [FriendAccessAllowed] internal void InvalidateZOrder() { // if we don't have any children, there is nothing to do if (VisualChildrenCount == 0) return; SetFlags(true, VisualFlags.NodeRequiresNewRealization); Visual.PropagateFlags( this, VisualFlags.IsSubtreeDirtyForPrecompute | VisualFlags.NodeNeedsBitmapEffectUpdate | VisualFlags.NodeInSubtreeRequiresNewRealization, VisualProxyFlags.IsSubtreeDirtyForRender); // Disconnect the children of the visual on all channels // it is being marshalled to. DisconnectChildrenOnAllChannels(); // System.Windows.Input.InputManager.SafeCurrentNotifyHitTestInvalidated(); } //This is used by LayoutManager as a perf optimization for layout updates. //During layout updates, LM needs to find which areas of the visual tree //are higher in the tree - they have to be processed first to avoid multiple //updates of lower descendants. The tree level counter is maintained by //UIElement.PropagateResume/SuspendLayout methods and uses 8 bits in VisualFlags to //keep the count. internal uint TreeLevel { get { return ((uint)_flags & 0xFF000000) >> 24; } set { if(value > 0xFF) { throw new InvalidOperationException(SR.Get(SRID.LayoutManager_DeepRecursion, 255)); } _flags = (VisualFlags)(((uint)_flags & 0x00FFFFFF) | (value << 24)); } } #endregion VisualChildren #region VisualParent ////// Returns the parent of this Visual. Parent may be either a Visual or Visual3D. /// protected DependencyObject VisualParent { get { VerifyAPIReadOnly(); return InternalVisualParent; } } ////// Identical to VisualParent, except that skips verify access for perf. /// internal DependencyObject InternalVisualParent { get { return _parent; } } #endregion VisualParent // These 2 method will be REMOVED once Hamid is back and can // explain why Window needs to Bypass layout for setting Flow Direction. // These methods are only called from InternalSetLayoutTransform which is called only from Window [FriendAccessAllowed] internal void InternalSetOffsetWorkaround(Vector offset) { VisualOffset = offset; } [FriendAccessAllowed] internal void InternalSetTransformWorkaround(Transform transform) { VisualTransform = transform; } // ------------------------------------------------------------------- // // Visual Properties // // ------------------------------------------------------------------- #region Visual Properties ////// Gets or sets the transform of this Visual. /// protected internal Transform VisualTransform { get { VerifyAPIReadOnly(); return TransformField.GetValue(this); } protected set { VerifyAPIReadWrite(value); Transform transform = TransformField.GetValue(this); if (transform == value) { return; } Transform newTransform = value; // Add changed notifications for the new transform if necessary. if (newTransform != null && !newTransform.IsFrozen) { newTransform.Changed += TransformChangedHandler; } if (transform != null) { // // Remove changed notifications for the old transform if necessary. // if (!transform.IsFrozen) { transform.Changed -= TransformChangedHandler; } // // Disconnect the transform from this visual. // DisconnectAttachedResource( VisualProxyFlags.IsTransformDirty, ((DUCE.IResource)transform)); } // // Set the new clip and mark it dirty // TransformField.SetValue(this, newTransform); SetFlagsOnAllChannels(true, VisualProxyFlags.IsTransformDirty); TransformChanged(/* sender */ null, /* args */ null); } } ////// BitmapEffect Property - /// Gets or sets the optional BitmapEffect. If set, the BitmapEffect will /// be applied Visual's rendered content, after which the OpacityMask and/or Opacity /// will be applied (if present). /// protected internal BitmapEffect VisualBitmapEffect { get { VerifyAPIReadOnly(); if (CheckFlagsAnd(VisualFlags.NodeHasBitmapEffect)) return BitmapEffectStateField.GetValue(this).BitmapEffect; return null; } protected set { VerifyAPIReadWrite(value); BitmapEffectVisualState bitmapEffectState = BitmapEffectStateField.GetValue(this); BitmapEffect bitmapEffect = (bitmapEffectState == null) ? null : bitmapEffectState.BitmapEffect; if (bitmapEffect == value) { return; } BitmapEffect newBitmapEffect = value; if (newBitmapEffect == null) { Debug.Assert(bitmapEffectState != null); for (int i = 0; i < _proxy.Count; i++) { DUCE.Channel channel = _proxy.GetChannel(i); bitmapEffectState.FreeContent(this, channel); } BitmapEffectStateField.SetValue(this, null); SetFlags(false, VisualFlags.NodeHasBitmapEffect); SetFlagsOnAllChannels(false, VisualProxyFlags.IsContentConnected); SetFlagsOnAllChannels(true, VisualProxyFlags.IsContentDirty | VisualProxyFlags.IsOpacityDirty | VisualProxyFlags.IsOpacityMaskDirty | VisualProxyFlags.IsEdgeModeDirty); } else { // if we are adding a bitmap effect to the visual, // we want to disconnect all its children, edgemode and // opacity/opacity mask from all channels // The visual's children will no longer be rendered on the // compositor. We render them to a bitmap and apply the // effect to the bitmap and then set the new bitmap as the // content of the visual. if (bitmapEffectState == null) { DisconnectBitmapEffectPropertiesOnAllChannels(); bitmapEffectState = new BitmapEffectVisualState(); BitmapEffectStateField.SetValue(this, bitmapEffectState); } bitmapEffectState.BitmapEffect = newBitmapEffect; SetFlags(true, VisualFlags.NodeHasBitmapEffect); } // Enable new image effect if necessary. if (newBitmapEffect != null && !newBitmapEffect.IsFrozen) { newBitmapEffect.Changed += BitmapEffectChangedHandler; } // Disable old image effect if necessary. if (bitmapEffect != null && !bitmapEffect.IsFrozen) { bitmapEffect.Changed -= BitmapEffectChangedHandler; } //Propagate flags -- the handler doesn't care about the arguments, so we can pass in nulls BitmapEffectChanged(/* sender */ null, /* args */ null); } } ////// BitmapEffectInput Property - /// Gets or sets the optional BitmapEffectInput. If set, the BitmapEffectInput will /// be applied Visual's rendered content, after which the OpacityMask and/or Opacity /// will be applied (if present). /// protected internal BitmapEffectInput VisualBitmapEffectInput { get { VerifyAPIReadOnly(); BitmapEffectVisualState bitmapEffectState = BitmapEffectStateField.GetValue(this); if (bitmapEffectState != null) return bitmapEffectState.BitmapEffectInput; return null; } protected set { VerifyAPIReadWrite(); BitmapEffectVisualState bitmapEffectState = BitmapEffectStateField.GetValue(this); BitmapEffectInput bitmapEffectInput = (bitmapEffectState == null) ? null : bitmapEffectState.BitmapEffectInput; if (bitmapEffectInput == value) { return; } BitmapEffectInput newBitmapEffectInput = value; if (bitmapEffectState == null) { bitmapEffectState = new BitmapEffectVisualState(); BitmapEffectStateField.SetValue(this, bitmapEffectState); } // Enable new image effect if necessary. if (newBitmapEffectInput != null && !newBitmapEffectInput.IsFrozen) { newBitmapEffectInput.Changed += BitmapEffectInputChangedHandler; } // Disable old image effect if necessary. if (bitmapEffectInput != null && !bitmapEffectInput.IsFrozen) { bitmapEffectInput.Changed -= BitmapEffectInputChangedHandler; } bitmapEffectState.BitmapEffectInput = newBitmapEffectInput; //Propagate flags -- the handler doesn't care about the arguments, so we can pass in nulls BitmapEffectInputChanged(/* sender */ null, /* args */ null); } } ////// Gets or sets the clip of this Visual. /// protected internal Geometry VisualClip { get { VerifyAPIReadOnly(); return ClipField.GetValue(this); } protected set { ChangeVisualClip(value, false /* dontSetWhenClose */); } } ////// Processes changing the clip from the old clip to the new clip. /// Called from Visual.set_VisualClip and from places that want /// to optimize setting a new clip (like UIElement.ensureClip). /// internal void ChangeVisualClip(Geometry newClip, bool dontSetWhenClose) { VerifyAPIReadWrite(newClip); Geometry oldClip = ClipField.GetValue(this); if ((oldClip == newClip) || (dontSetWhenClose && (oldClip != null) && (newClip != null) && oldClip.AreClose(newClip))) { return; } // Add changed notifications for the new clip if necessary. if (newClip != null && !newClip.IsFrozen) { newClip.Changed += ClipChangedHandler; } if (oldClip != null) { // // Remove changed notifications for the old clip if necessary. // if (!oldClip.IsFrozen) { oldClip.Changed -= ClipChangedHandler; } // // Disconnect the clip from this visual. // DisconnectAttachedResource( VisualProxyFlags.IsClipDirty, ((DUCE.IResource)oldClip)); } // // Set the new clip and mark it dirty // ClipField.SetValue(this, newClip); SetFlagsOnAllChannels(true, VisualProxyFlags.IsClipDirty); ClipChanged(/* sender */ null, /* args */ null); } ////// Gets and sets the offset. /// protected internal Vector VisualOffset { get { // VerifyAPIReadOnly(); // Intentionally removed for performance reasons. return _offset; } protected set { VerifyAPIReadWrite(); if (value != _offset) // Fuzzy comparison might be better here. { VisualFlags flags; _offset = value; SetFlagsOnAllChannels(true, VisualProxyFlags.IsOffsetDirty); flags = VisualFlags.IsSubtreeDirtyForPrecompute | VisualFlags.NodeNeedsBitmapEffectUpdate; // // if (CheckFlagsOr(VisualFlags.NodeUsesRealizationCaches | VisualFlags.NodeInSubtreeUsesRealizationCaches) && MediaContext.From(Dispatcher).BitmapEffectsUsed) { SetFlags(true, VisualFlags.NodeRequiresNewRealization); flags |= VisualFlags.NodeInSubtreeRequiresNewRealization; } PropagateFlags( this, flags, VisualProxyFlags.IsSubtreeDirtyForRender); } } } ////// Gets or sets the opacity of the Visual. /// protected internal double VisualOpacity { get { VerifyAPIReadOnly(); return OpacityCache; } protected set { VerifyAPIReadWrite(); if (OpacityCache == value) { return; } OpacityField.SetValue(this, value); // [....]: We need to do more here for animated opacity. SetFlagsOnAllChannels(true, VisualProxyFlags.IsOpacityDirty); PropagateFlags( this, VisualFlags.NodeNeedsBitmapEffectUpdate, VisualProxyFlags.IsSubtreeDirtyForRender); } } ////// Gets or sets the EdgeMode of the Visual. /// protected internal EdgeMode VisualEdgeMode { get { VerifyAPIReadOnly(); return EdgeModeCache; } protected set { VerifyAPIReadWrite(); if (EdgeModeCache == value) { return; } EdgeModeField.SetValue(this, value); SetFlagsOnAllChannels(true, VisualProxyFlags.IsEdgeModeDirty); PropagateFlags( this, VisualFlags.NodeNeedsBitmapEffectUpdate, VisualProxyFlags.IsSubtreeDirtyForRender); } } private EdgeMode EdgeModeCache { get { object edgeMode = EdgeModeField.GetValue(this); if (edgeMode == null) { return EdgeMode.Unspecified; } return (EdgeMode)edgeMode; } } ////// Gets or sets the ImageScalingMode of the Visual. /// protected internal BitmapScalingMode VisualBitmapScalingMode { get { VerifyAPIReadOnly(); return BitmapScalingModeCache; } protected set { VerifyAPIReadWrite(); if (BitmapScalingModeCache == value) { return; } BitmapScalingModeField.SetValue(this, value); SetFlagsOnAllChannels(true, VisualProxyFlags.IsBitmapScalingModeDirty); PropagateFlags( this, VisualFlags.NodeNeedsBitmapEffectUpdate, VisualProxyFlags.IsSubtreeDirtyForRender); } } private BitmapScalingMode BitmapScalingModeCache { get { object bitmapScalingMode = BitmapScalingModeField.GetValue(this); if (bitmapScalingMode == null) { return BitmapScalingMode.Unspecified; } return (BitmapScalingMode) bitmapScalingMode; } } ////// OpacityMask Property - /// Gets or sets the optional OpacityMask. If set, the Brush's opacity will /// be combined multiplicitively with the Visual's rendered content. /// protected internal Brush VisualOpacityMask { get { VerifyAPIReadOnly(); return OpacityMaskField.GetValue(this); } protected set { VerifyAPIReadWrite(value); Brush opacityMask = OpacityMaskField.GetValue(this); if (opacityMask == value) { return; } Brush newOpacityMask = value; // Add changed notifications for the new opacity mask if necessary. if (newOpacityMask != null && !newOpacityMask.IsFrozen) { newOpacityMask.Changed += OpacityMaskChangedHandler; } if (opacityMask != null) { // // Remove changed notifications for the old opacity mask if necessary. // if (!opacityMask.IsFrozen) { opacityMask.Changed -= OpacityMaskChangedHandler; } // // Disconnect the opacity mask from this visual. // If the visual has a bitmap effect, the opacity mask is not // connected if (CheckFlagsAnd(VisualFlags.NodeHasBitmapEffect) == false) { DisconnectAttachedResource( VisualProxyFlags.IsOpacityMaskDirty, ((DUCE.IResource)opacityMask)); } } // // Set the new opacity mask and mark it dirty // OpacityMaskField.SetValue(this, newOpacityMask); SetFlagsOnAllChannels(true, VisualProxyFlags.IsOpacityMaskDirty); OpacityMaskChanged(/* sender */ null, /* args */ null); } } ////// Gets or sets X- (vertical) guidelines on this Visual. /// protected internal DoubleCollection VisualXSnappingGuidelines { get { VerifyAPIReadOnly(); return GuidelinesXField.GetValue(this); } protected set { VerifyAPIReadWrite(value); DoubleCollection guidelines = GuidelinesXField.GetValue(this); if (guidelines == value) { return; } DoubleCollection newGuidelines = value; // Add changed notifications for the new guidelines if necessary. if (newGuidelines != null && !newGuidelines.IsFrozen) { newGuidelines.Changed += GuidelinesChangedHandler; } // Remove changed notifications for the old guidelines if necessary. if (guidelines != null && !guidelines.IsFrozen) { guidelines.Changed -= GuidelinesChangedHandler; } GuidelinesXField.SetValue(this, newGuidelines); GuidelinesChanged(/* sender */ null, /* args */ null); } } ////// Gets or sets Y- (horizontal) guidelines of this Visual. /// protected internal DoubleCollection VisualYSnappingGuidelines { get { VerifyAPIReadOnly(); return GuidelinesYField.GetValue(this); } protected set { VerifyAPIReadWrite(value); DoubleCollection guidelines = GuidelinesYField.GetValue(this); if (guidelines == value) { return; } DoubleCollection newGuidelines = value; // Add changed notifications for the new guidelines if necessary. if (newGuidelines != null && !newGuidelines.IsFrozen) { newGuidelines.Changed += GuidelinesChangedHandler; } // Remove changed notifications for the old guidelines if necessary. if (guidelines != null && !guidelines.IsFrozen) { guidelines.Changed -= GuidelinesChangedHandler; } GuidelinesYField.SetValue(this, newGuidelines); GuidelinesChanged(/* sender */ null, /* args */ null); } } #endregion Visual Properties private double OpacityCache { get { object opacity = OpacityField.GetValue(this); if (opacity == null) { return 1.0; } return (double)opacity; } } ////// Disconnects a resource attached to this visual. /// internal void DisconnectAttachedResource( VisualProxyFlags correspondingFlag, DUCE.IResource attachedResource) { // // We need a special case for the content (corresponding // to the IsContentConnected flag). // bool needToReleaseContent = correspondingFlag == VisualProxyFlags.IsContentConnected; // // Iterate over the channels this visual is being marshaled to // for (int i = 0; i < _proxy.Count; i++) { DUCE.Channel channel = _proxy.GetChannel(i); VisualProxyFlags flags = _proxy.GetFlags(i); // // See if the corresponding flag is set... // bool correspondingFlagSet = (flags & correspondingFlag) != 0; // // We want to perform an action if IsContentConnected // flag is set or a Is*Dirty flag is not set: // if (correspondingFlagSet == needToReleaseContent) { // // Set the flag so that during render we send // update to the compositor. // SetFlags(channel, true, correspondingFlag); // // Add this resource to the queue for delayed-removal. // channel.AddToRemoveAndReleaseQueue( null, attachedResource); if (needToReleaseContent) { // // Mark the content of this visual as disconnected. // _proxy.SetFlags(i, false, VisualProxyFlags.IsContentConnected); } } } } ////// GetDrawing - Returns the Drawing content of this Visual /// internal virtual DrawingGroup GetDrawing() { VerifyAPIReadOnly(); // Default implementation returns null for Visual's that // don't have drawings return null; } // ------------------------------------------------------------------- // // Visual Ancestry Relations // // -------------------------------------------------------------------- #region Visual Ancestry Relations ////// This is called when the parent link of the Visual is changed. /// This method executes important base functionality before calling the /// overridable virtual. /// /// Old parent or null if the Visual did not have a parent before. internal virtual void FireOnVisualParentChanged(DependencyObject oldParent) { // Call the ParentChanged virtual before firing the Ancestor Changed Event OnVisualParentChanged(oldParent); // Clean up bits when the tree is Cut or Pasted. // If we are attaching to a tree then // send the bit up if we need to. if(oldParent == null) { Debug.Assert(_parent != null, "If oldParent is null, current parent should != null."); if(CheckFlagsAnd(VisualFlags.SubTreeHoldsAncestorChanged)) { SetTreeBits( _parent, VisualFlags.SubTreeHoldsAncestorChanged, VisualFlags.RegisteredForAncestorChanged); } } // If we are cutting a sub tree off then // clear the bit in the main tree above if we need to. else { if(CheckFlagsAnd(VisualFlags.SubTreeHoldsAncestorChanged)) { ClearTreeBits( oldParent, VisualFlags.SubTreeHoldsAncestorChanged, VisualFlags.RegisteredForAncestorChanged); } } // Fire the Ancestor changed Event on the nodes. AncestorChangedEventArgs args = new AncestorChangedEventArgs(this, oldParent); ProcessAncestorChangedNotificationRecursive(this, args); } ////// OnVisualParentChanged is called when the parent of the Visual is changed. /// /// Old parent or null if the Visual did not have a parent before. protected internal virtual void OnVisualParentChanged(DependencyObject oldParent) { } ////// OnVisualChildrenChanged is called when the VisualCollection of the Visual is edited. /// protected internal virtual void OnVisualChildrenChanged( DependencyObject visualAdded, DependencyObject visualRemoved) { } ////// Add removed delegates to the VisualAncenstorChanged Event. /// ////// This also sets/clears the tree-searching bit up the tree /// internal event AncestorChangedEventHandler VisualAncestorChanged { add { AncestorChangedEventHandler newHandler = AncestorChangedEventField.GetValue(this); if (newHandler == null) { newHandler = value; } else { newHandler += value; } AncestorChangedEventField.SetValue(this, newHandler); SetTreeBits( this, VisualFlags.SubTreeHoldsAncestorChanged, VisualFlags.RegisteredForAncestorChanged); } remove { // check that we are Disabling a node that was previously Enabled if(CheckFlagsAnd(VisualFlags.SubTreeHoldsAncestorChanged)) { ClearTreeBits( this, VisualFlags.SubTreeHoldsAncestorChanged, VisualFlags.RegisteredForAncestorChanged); } // if we are Disabling a Visual that was not Enabled then this // search should fail. But it is safe to check. AncestorChangedEventHandler newHandler = AncestorChangedEventField.GetValue(this); if (newHandler != null) { newHandler -= value; if(newHandler == null) { AncestorChangedEventField.ClearValue(this); } else { AncestorChangedEventField.SetValue(this, newHandler); } } } } ////// Walks down in the tree for nodes that have AncestorChanged Handlers /// registered and calls them. /// It uses Flag bits that help it prune the walk. This should go /// straight to the relevent nodes. /// internal static void ProcessAncestorChangedNotificationRecursive(DependencyObject e, AncestorChangedEventArgs args) { if (e is Visual3D) { Visual3D.ProcessAncestorChangedNotificationRecursive(e, args); } else { Visual eAsVisual = e as Visual; // If the flag is not set, then we are Done. if(!eAsVisual.CheckFlagsAnd(VisualFlags.SubTreeHoldsAncestorChanged)) { return; } // If there is a handler on this node, then fire it. AncestorChangedEventHandler handler = AncestorChangedEventField.GetValue(eAsVisual); if(handler != null) { handler(eAsVisual, args); } // Decend into the children. int count = eAsVisual.InternalVisual2DOr3DChildrenCount; for (int i = 0; i < count; i++) { DependencyObject childVisual = eAsVisual.InternalGet2DOr3DVisualChild(i); if (childVisual != null) { ProcessAncestorChangedNotificationRecursive(childVisual, args); } } } } ////// Returns true if the specified ancestor (this) is really the ancestor of the /// given descendant (argument). /// public bool IsAncestorOf(DependencyObject descendant) { Visual visual; Visual3D visual3D; VisualTreeUtils.AsNonNullVisual(descendant, out visual, out visual3D); // x86 branch prediction skips the branch on first encounter. We favor 2D. if(visual3D != null) { return visual3D.IsDescendantOf(this); } return visual.IsDescendantOf(this); } ////// Returns true if the refernece Visual (this) is a descendant of the argument Visual. /// public bool IsDescendantOf(DependencyObject ancestor) { if (ancestor == null) { throw new ArgumentNullException("ancestor"); } VisualTreeUtils.EnsureVisual(ancestor); // Walk up the parent chain of the descendant until we run out // of 2D parents or we find the ancestor. DependencyObject current = this; while ((current != null) && (current != ancestor)) { Visual currentAsVisual = current as Visual; if (currentAsVisual != null) { current = currentAsVisual._parent; } else { Visual3D currentAsVisual3D = current as Visual3D; if (currentAsVisual3D != null) { current = currentAsVisual3D.InternalVisualParent; } else { current = null; } } } return current == ancestor; } ////// Walks up the Visual tree setting or clearing the given flags. Unlike /// PropagateFlags this does not terminate when it reaches node with /// the flags already set. It always walks all the way to the root. /// internal void SetFlagsToRoot(bool value, VisualFlags flag) { Visual current = this; do { current.SetFlags(value, flag); Visual currentParent = current._parent as Visual; // if the cast to currentParent failed and yet current._parent is not null then // we have a 3D element. Call SetFlagsToRoot on it instead. if (current._parent != null && currentParent == null) { ((Visual3D)current._parent).SetFlagsToRoot(value, flag); return; } current = currentParent; } while (current != null); } ////// Finds the first ancestor of the given element which has the given /// flags set. /// internal DependencyObject FindFirstAncestorWithFlagsAnd(VisualFlags flag) { Visual current = this; do { if (current.CheckFlagsAnd(flag)) { // The other Visual crossed through this Visual's parent chain. Hence this is our // common ancestor. return current; } DependencyObject parent = current._parent; // first attempt to see if parent is a Visual, in which case we continue the loop. // Otherwise see if it's a Visual3D, and call the similar method on it. current = parent as Visual; if (current == null) { Visual3D parentAsVisual3D = parent as Visual3D; if (parentAsVisual3D != null) { return parentAsVisual3D.FindFirstAncestorWithFlagsAnd(flag); } } } while (current != null); return null; } ////// Finds the common ancestor of two Visuals. /// ///Returns the common ancestor if the Visuals have one or otherwise null. ///If the argument is null. public DependencyObject FindCommonVisualAncestor(DependencyObject otherVisual) { VerifyAPIReadOnly(otherVisual); if (otherVisual == null) { throw new System.ArgumentNullException("otherVisual"); } // Since we can't rely on code running in the CLR, we need to first make sure // that the FindCommonAncestor flag is not set. It is enought to ensure this // on one path to the root Visual. // SetFlagsToRoot(false, VisualFlags.FindCommonAncestor); // Walk up the other visual's parent chain and set the FindCommonAncestor flag. VisualTreeUtils.SetFlagsToRoot(otherVisual, true, VisualFlags.FindCommonAncestor); // Now see if the other Visual's parent chain crosses our parent chain. return FindFirstAncestorWithFlagsAnd(VisualFlags.FindCommonAncestor); } #endregion Visual Ancestry Relations // ------------------------------------------------------------------- // // Visual-to-Visual Transforms // // -------------------------------------------------------------------- #region Visual-to-Visual Transforms ////// Returns a transform that can be used to transform coordinate from this /// node to the specified ancestor. It allows 3D to be between the 2D nodes. /// ////// If ancestor is null. /// ////// If the ancestor Visual is not a ancestor of Visual. /// ///If the Visuals are not connected. public GeneralTransform TransformToAncestor( Visual ancestor) { if (ancestor == null) { throw new ArgumentNullException("ancestor"); } VerifyAPIReadOnly(ancestor); return InternalTransformToAncestor(ancestor, false); } ////// Returns a transform that can be used to transform coordinate from this /// node to the specified ancestor. /// ////// If ancestor is null. /// ////// If the ancestor Visual3D is not a ancestor of Visual. /// ///If the Visuals are not connected. public GeneralTransform2DTo3D TransformToAncestor(Visual3D ancestor) { if (ancestor == null) { throw new ArgumentNullException("ancestor"); } VerifyAPIReadOnly(ancestor); return InternalTransformToAncestor(ancestor, false); } ////// Returns a transform that can be used to transform coordinates from this /// node to the specified descendant, or null if the transform from descendant to "this" /// is non-invertible. It allows 3D to be between the 2D nodes. /// ////// If the reference Visual is not a ancestor of the descendant Visual. /// ///If the descendant argument is null. ///If the Visuals are not connected. public GeneralTransform TransformToDescendant(Visual descendant) { if (descendant == null) { throw new ArgumentNullException("descendant"); } VerifyAPIReadOnly(descendant); return descendant.InternalTransformToAncestor(this, true); } ////// The returned matrix can be used to transform coordinates from this Visual to /// the specified Visual. /// Returns null if no such transform exists due to a non-invertible Transform. /// ///If visual is null. ///If the Visuals are not connected. public GeneralTransform TransformToVisual(Visual visual) { DependencyObject ancestor = FindCommonVisualAncestor(visual); Visual ancestorAsVisual = ancestor as Visual; if (ancestorAsVisual == null) { throw new System.InvalidOperationException(SR.Get(SRID.Visual_NoCommonAncestor)); } GeneralTransform g0; Matrix m0; bool isSimple0 = this.TrySimpleTransformToAncestor(ancestorAsVisual, false, out g0, out m0); GeneralTransform g1; Matrix m1; bool isSimple1 = visual.TrySimpleTransformToAncestor(ancestorAsVisual, true, out g1, out m1); // combine the transforms // if both transforms are simple Matrix transforms, just multiply them and // return the result. if (isSimple0 && isSimple1) { MatrixUtil.MultiplyMatrix(ref m0, ref m1); MatrixTransform m = new MatrixTransform(m0); m.Freeze(); return m; } // Handle the case where 0 is simple and 1 is complex. if (isSimple0) { g0 = new MatrixTransform(m0); g0.Freeze(); } else if (isSimple1) { g1 = new MatrixTransform(m1); g1.Freeze(); } // If inverse was requested, TrySimpleTransformToAncestor can return null // add the transform only if it is not null if (g1 != null) { GeneralTransformGroup group = new GeneralTransformGroup(); group.Children.Add(g0); group.Children.Add(g1); group.Freeze(); return group; } return g0; } ////// Returns the transform or the inverse transform between this visual and the specified ancestor. /// If inverse is requested but does not exist (if the transform is not invertible), null is returned. /// /// Ancestor visual. /// Returns inverse if this argument is true. private GeneralTransform InternalTransformToAncestor(Visual ancestor, bool inverse) { GeneralTransform generalTransform; Matrix simpleTransform; bool isSimple = TrySimpleTransformToAncestor(ancestor, inverse, out generalTransform, out simpleTransform); if (isSimple) { MatrixTransform matrixTransform = new MatrixTransform(simpleTransform); matrixTransform.Freeze(); return matrixTransform; } else { return generalTransform; } } ////// Provides the transform or the inverse transform between this visual and the specified ancestor. /// Returns true if the transform is "simple" - in which case the GeneralTransform is null /// and the caller should use the Matrix. /// Otherwise, returns false - use the GeneralTransform and ignore the Matrix. /// If inverse is requested but not available (if the transform is not invertible), false is /// returned and the GeneralTransform is null. /// /// Ancestor visual. /// Returns inverse if this argument is true. /// The GeneralTransform if this method returns false. /// The Matrix if this method returns true. internal bool TrySimpleTransformToAncestor(Visual ancestor, bool inverse, out GeneralTransform generalTransform, out Matrix simpleTransform) { Debug.Assert(ancestor != null); // flag to indicate if we have a case where we do multile 2D->3D->2D transitions bool embedded2Don3D = false; DependencyObject g = this; Matrix m = Matrix.Identity; // Keep this null until it's needed GeneralTransformGroup group = null; // This while loop will walk up the visual tree until we encounter the ancestor. // As it does so, it will accumulate the descendent->ancestor transform. // In most cases, this is simply a matrix, though if we encounter a bitmap effect we // will need to use a general transform group to store the transform. // We will accumulate the current transform in a matrix until we encounter a bitmap effect, // at which point we will add the matrix's current value and the bitmap effect's transforms // to the GeneralTransformGroup and continue to accumulate further transforms in the matrix again. // At the end of this loop, we will have 0 or more transforms in the GeneralTransformGroup // and the matrix which, if not identity, should be appended to the GeneralTransformGroup. // If, as is commonly the case, this loop terminates without encountering a bitmap effect // we will simply use the Matrix. while ((VisualTreeHelper.GetParent(g) != null) && (g != ancestor)) { Visual gAsVisual = g as Visual; if (gAsVisual != null) { if (CheckFlagsAnd(VisualFlags.NodeHasBitmapEffect)) { BitmapEffectVisualState bitmapEffectState = BitmapEffectStateField.GetValue(this); BitmapEffect effect = bitmapEffectState.BitmapEffect; // if the BitmapEffect has an affine transformation // add code to just multiply the matrix here to the effects matrix if (effect.IsAffineTransform) { Matrix cm = effect.GetAffineMatrix(); MatrixUtil.MultiplyMatrix(ref m, ref cm); } else { if (group == null) { group = new GeneralTransformGroup(); } group.Children.Add(new MatrixTransform(m)); m = Matrix.Identity; group.Children.Add(new BitmapEffectGeneralTransform(bitmapEffectState.BitmapEffect, bitmapEffectState.BitmapEffectInput, false, VisualContentBounds)); } } Transform transform = TransformField.GetValue(gAsVisual); if (transform != null) { Matrix cm = transform.Value; MatrixUtil.MultiplyMatrix(ref m, ref cm); } m.Translate(gAsVisual._offset.X, gAsVisual._offset.Y); // Consider having a bit that indicates that we have a non-null offset. g = gAsVisual._parent; } else { // we just hit a Visual3D - use a GeneralTransform to go from 2D -> 3D -> 2D // and then return to the tree using the 2D parent - the general transform will deal with the // actual transformation. This Visual3D also must be a Viewport2DVisual3D since this is the only // Visual3D that can have a 2D child. Viewport2DVisual3D gAsVisual3D = g as Viewport2DVisual3D; if (group == null) { group = new GeneralTransformGroup(); } group.Children.Add(new MatrixTransform(m)); m = Matrix.Identity; Visual visualForGenTransform = null; if (embedded2Don3D) { visualForGenTransform = gAsVisual3D.Visual; } else { visualForGenTransform = this; embedded2Don3D = true; } group.Children.Add(new GeneralTransform2DTo3DTo2D(gAsVisual3D, visualForGenTransform)); g = VisualTreeHelper.GetContainingVisual2D(gAsVisual3D); } } if (g != ancestor) { throw new System.InvalidOperationException(SR.Get(inverse ? SRID.Visual_NotADescendant : SRID.Visual_NotAnAncestor)); } // At this point, we will have 0 or more transforms in the GeneralTransformGroup // and the matrix which, if not identity, should be appended to the GeneralTransformGroup. // If, as is commonly the case, this loop terminates without encountering a bitmap effect // we will simply use the Matrix. // Assert that a non-null group implies at least one child Debug.Assert((group == null) || (group.Children.Count > 0)); // Do we have a group? if (group != null) { if (!m.IsIdentity) { group.Children.Add(new MatrixTransform(m)); } if (inverse) { group = (GeneralTransformGroup)group.Inverse; } // group can be null if it does not have an inverse if (group != null) { group.Freeze(); } // Initialize out params generalTransform = group; simpleTransform = new Matrix(); return false; // simple transform failed } // If not, the entire transform is stored in the matrix else { // Initialize out params generalTransform = null; if (inverse) { if (!m.HasInverse) { simpleTransform = new Matrix(); return false; // inversion failed, so simple transform failed. } m.Invert(); } simpleTransform = m; return true; // simple transform succeeded } } ////// Returns the transform or the inverse transform between this visual and the specified ancestor. /// If inverse is requested but does not exist (if the transform is not invertible), null is returned. /// /// Ancestor visual. /// Returns inverse if this argument is true. private GeneralTransform2DTo3D InternalTransformToAncestor(Visual3D ancestor, bool inverse) { GeneralTransform2DTo3D transformTo3D = null; if (TrySimpleTransformToAncestor(ancestor, out transformTo3D)) { transformTo3D.Freeze(); return transformTo3D; } else { return null; } } ////// Provides the transform to go from 2D to 3D. /// /// Ancestor visual. /// The transform to use to go to 3D internal bool TrySimpleTransformToAncestor(Visual3D ancestor, out GeneralTransform2DTo3D transformTo3D) { Debug.Assert(ancestor != null); // get the 3D object that contains this visual // this must be a Viewport2DVisual3D since this is the only 3D class that can contain 2D content as a child Viewport2DVisual3D containingVisual3D = VisualTreeHelper.GetContainingVisual3D(this) as Viewport2DVisual3D; // if containingVisual3D is null then ancestor is not the ancestor if (containingVisual3D == null) { throw new System.InvalidOperationException(SR.Get(SRID.Visual_NotAnAncestor)); } GeneralTransform transform2D = this.TransformToAncestor(containingVisual3D.Visual); GeneralTransform3D transform3D = containingVisual3D.TransformToAncestor(ancestor); transformTo3D = new GeneralTransform2DTo3D(transform2D, containingVisual3D, transform3D); return true; } ////// This method converts a point in the current Visual's coordinate /// system into a point in screen coordinates. /// public Point PointToScreen(Point point) { VerifyAPIReadOnly(); PresentationSource inputSource = PresentationSource.FromVisual(this); if (inputSource == null) { throw new InvalidOperationException(SR.Get(SRID.Visual_NoPresentationSource)); } // Translate the point from the visual to the root. GeneralTransform gUp = this.TransformToAncestor(inputSource.RootVisual); if (gUp == null || !gUp.TryTransform(point, out point)) { throw new InvalidOperationException(SR.Get(SRID.Visual_CannotTransformPoint)); } // Translate the point from the root to the screen point = PointUtil.RootToClient(point, inputSource); point = PointUtil.ClientToScreen(point, inputSource); return point; } ////// This method converts a point in screen coordinates into a point /// in the current Visual's coordinate system. /// public Point PointFromScreen(Point point) { VerifyAPIReadOnly(); PresentationSource inputSource = PresentationSource.FromVisual(this); if (inputSource == null) { throw new InvalidOperationException(SR.Get(SRID.Visual_NoPresentationSource)); } // Translate the point from the screen to the root point = PointUtil.ScreenToClient(point, inputSource); point = PointUtil.ClientToRoot(point, inputSource); // Translate the point from the root to the visual. GeneralTransform gDown = inputSource.RootVisual.TransformToDescendant(this); if (gDown == null || !gDown.TryTransform(point, out point)) { throw new InvalidOperationException(SR.Get(SRID.Visual_CannotTransformPoint)); } return point; } #endregion Visual-to-Visual Transforms // -------------------------------------------------------------------- // // Internal Event Handlers // // ------------------------------------------------------------------- #region Internal Event Handlers internal EventHandler ClipChangedHandler { get { return new EventHandler(ClipChanged); } } internal void ClipChanged(object sender, EventArgs e) { PropagateChangedFlags(); } internal EventHandler TransformChangedHandler { get { return new EventHandler(TransformChanged); } } internal void TransformChanged(object sender, EventArgs e) { PropagateChangedFlags(); } internal EventHandler GuidelinesChangedHandler { get { return new EventHandler(GuidelinesChanged); } } internal void GuidelinesChanged(object sender, EventArgs e) { SetFlagsOnAllChannels( true, VisualProxyFlags.IsGuidelineCollectionDirty); PropagateChangedFlags(); } internal EventHandler OpacityMaskChangedHandler { get { return new EventHandler(OpacityMaskChanged); } } internal void OpacityMaskChanged(object sender, EventArgs e) { PropagateChangedFlags(); } internal EventHandler BitmapEffectChangedHandler { get { return new EventHandler(BitmapEffectChanged); } } internal void BitmapEffectChanged(object sender, EventArgs e) { SetFlagsOnAllChannels(true, VisualProxyFlags.IsBitmapEffectDirty); PropagateChangedFlags(); } internal EventHandler BitmapEffectInputChangedHandler { get { return new EventHandler(BitmapEffectInputChanged); } } internal void BitmapEffectInputChanged(object sender, EventArgs e) { if (CheckFlagsAnd(VisualFlags.NodeHasBitmapEffect)) { SetFlagsOnAllChannels(true, VisualProxyFlags.IsBitmapEffectDirty); PropagateFlags( this, VisualFlags.IsSubtreeDirtyForPrecompute | VisualFlags.NodeNeedsBitmapEffectUpdate, VisualProxyFlags.IsSubtreeDirtyForRender); } } internal EventHandler ContentsChangedHandler { get { return new EventHandler(ContentsChanged); } } internal virtual void ContentsChanged(object sender, EventArgs e) { PropagateChangedFlags(); } #endregion Internal Event Handlers // -------------------------------------------------------------------- // // Visual flags manipulation // // ------------------------------------------------------------------- #region Visual flags manipulation ////// SetFlagsOnAllChannels is used to set or unset one /// or multiple flags on all channels this visual is /// marshaled to. /// internal void SetFlagsOnAllChannels( bool value, VisualProxyFlags flagsToChange) { _proxy.SetFlagsOnAllChannels( value, flagsToChange); } ////// SetFlags is used to set or unset one or multiple flags on a given channel. /// internal void SetFlags( DUCE.Channel channel, bool value, VisualProxyFlags flagsToChange) { _proxy.SetFlags( channel, value, flagsToChange); } ////// SetFlags is used to set or unset one or multiple node flags on the node. /// internal void SetFlags(bool value, VisualFlags flags) { _flags = value ? (_flags | flags) : (_flags & (~flags)); } ////// CheckFlagsOnAllChannels returns true if all flags in /// the bitmask flags are set on all channels this visual is /// marshaled to. /// ////// If there aren't any bits set on the specified flags /// the method returns true. /// internal bool CheckFlagsOnAllChannels(VisualProxyFlags flagsToCheck) { return _proxy.CheckFlagsOnAllChannels(flagsToCheck); } ////// CheckFlagsAnd returns true if all flags in the bitmask flags /// are set on a given channel. /// ////// If there aren't any bits set on the specified flags /// the method returns true. /// internal bool CheckFlagsAnd( DUCE.Channel channel, VisualProxyFlags flagsToCheck) { return (_proxy.GetFlags(channel) & flagsToCheck) == flagsToCheck; } ////// CheckFlagsAnd returns true if all flags in the bitmask flags are set on the node. /// ///If there aren't any bits set on the specified flags the method /// returns true internal bool CheckFlagsAnd(VisualFlags flags) { return (_flags & flags) == flags; } ////// Checks if any of the specified flags is set on a given channel. /// ////// If there aren't any bits set on the specified flags /// the method returns true. /// internal bool CheckFlagsOr( DUCE.Channel channel, VisualProxyFlags flagsToCheck) { return (_proxy.GetFlags(channel) & flagsToCheck) != VisualProxyFlags.None; } ////// Checks if any of the specified flags is set on the node. /// ///If there aren't any bits set on the specified flags the method /// returns true internal bool CheckFlagsOr(VisualFlags flags) { return (flags == 0) || ((_flags & flags) > 0); } ////// Set a bit in a Visual node and in all its direct ancestors. /// /// The Visual Element /// The Flag that marks a sub tree to search /// The Flag that marks the node to search for. internal static void SetTreeBits( DependencyObject e, VisualFlags treeFlag, VisualFlags nodeFlag) { Visual eAsVisual; Visual3D eAsVisual3D; if (e != null) { eAsVisual = e as Visual; if (eAsVisual != null) { eAsVisual.SetFlags(true, nodeFlag); } else { ((Visual3D)e).SetFlags(true, nodeFlag); } } while (null!=e) { eAsVisual = e as Visual; if (eAsVisual != null) { // if the bit is already set, then we're done. if(eAsVisual.CheckFlagsAnd(treeFlag)) return; eAsVisual.SetFlags(true, treeFlag); } else { eAsVisual3D = e as Visual3D; // if the bit is already set, then we're done. if(eAsVisual3D.CheckFlagsAnd(treeFlag)) return; eAsVisual3D.SetFlags(true, treeFlag); } e = VisualTreeHelper.GetParent(e); } } ////// Clean a bit in a Visual node and in all its direct ancestors; /// unless the ancestor also has /// /// The Visual Element /// The Flag that marks a sub tree to search /// The Flag that marks the node to search for. internal static void ClearTreeBits( DependencyObject e, VisualFlags treeFlag, VisualFlags nodeFlag) { Visual eAsVisual; Visual3D eAsVisual3D; // This bit might not be set, but checking costs as much as setting // So it is faster to just clear it everytime. if (e != null) { eAsVisual = e as Visual; if (eAsVisual != null) { eAsVisual.SetFlags(false, nodeFlag); } else { ((Visual3D)e).SetFlags(false, nodeFlag); } } while (e != null) { eAsVisual = e as Visual; if (eAsVisual != null) { if(eAsVisual.CheckFlagsAnd(nodeFlag)) { return; // Done; if a parent also has the Node bit set. } if(DoAnyChildrenHaveABitSet(eAsVisual, treeFlag)) { return; // Done; if a other subtrees are set. } eAsVisual.SetFlags(false, treeFlag); } else { eAsVisual3D = e as Visual3D; if(eAsVisual3D.CheckFlagsAnd(nodeFlag)) { return; // Done; if a parent also has the Node bit set. } if(Visual3D.DoAnyChildrenHaveABitSet(eAsVisual3D, treeFlag)) { return; // Done; if a other subtrees are set. } eAsVisual3D.SetFlags(false, treeFlag); } e = VisualTreeHelper.GetParent(e); } } ////// Check all the children for a bit. /// private static bool DoAnyChildrenHaveABitSet( Visual pe, VisualFlags flag) { int count = pe.VisualChildrenCount; for (int i = 0; i < count; i++) { Visual v = pe.GetVisualChild(i); if (v != null && v.CheckFlagsAnd(flag)) { return true; } } return false; } ////// Propagates the flags up to the root. /// ////// The walk stops on a node with all of the required flags set. /// internal static void PropagateFlags( Visual e, VisualFlags flags, VisualProxyFlags proxyFlags) { while ((e != null) && (!e.CheckFlagsAnd(flags) || !e.CheckFlagsOnAllChannels(proxyFlags))) { if (e.CheckFlagsOr(VisualFlags.ShouldPostRender)) { MediaContext mctx = MediaContext.From(e.Dispatcher); if (mctx.Channel != null) { mctx.PostRender(); } } else if (e.CheckFlagsAnd(VisualFlags.NodeIsVisualBrushRoot)) { // // For visuals that are root nodes in visual brushes we // need to fire OnChanged on the owning brushes. // DictionaryvisualBrushToChannelsMap = VisualBrushToChannelsMapField.GetValue(e); Debug.Assert(visualBrushToChannelsMap != null, "Visual brush roots need to have the visual brush to channels map!"); // // Iterate over the visual brushes and fire the OnChanged event. // foreach (VisualBrush visualBrush in visualBrushToChannelsMap.Keys) { visualBrush.FireOnChanged(); } } e.SetFlags(true, flags); e.SetFlagsOnAllChannels(true, proxyFlags); if (e._parent == null) { // Stop propagating. We are at the root of the 2D subtree. return; } Visual parentAsVisual = e._parent as Visual; if (parentAsVisual == null) { // if the parent is not null (saw this with earlier null check) and is not a Visual // it must be a Visual3D - continue the propagation Visual3D.PropagateFlags((Visual3D)e._parent, flags, proxyFlags); return; } e = parentAsVisual; } } /// /// Sets the NodeRequiresNewRealization flag on the current node and /// propagates the subtree realizations and dirty flags up to the root /// ////// The walk stops on a node with all of the required flags set. /// internal void PropagateChangedFlags() { // // Don't always know in advance if node has realizations, so propagate // flags regardless. // SetFlags(true, VisualFlags.NodeRequiresNewRealization); PropagateFlags( this, VisualFlags.IsSubtreeDirtyForPrecompute | VisualFlags.NodeNeedsBitmapEffectUpdate, VisualProxyFlags.IsSubtreeDirtyForRender); } #endregion Visual flags manipulation #if TRACE ////// Debug variable for counting nodes touched in the /// realizations walk /// internal static int MarkVisibleRealizationsCount; #endif ////// This node or a node below it contains a graphness /// inducing element /// internal bool NodeContainsGraphness { get { return CheckFlagsAnd(VisualFlags.NodeOrDescendantIntroducesGraphness); } } // ------------------------------------------------------------------- // // Internal Fields // // ------------------------------------------------------------------- #region Internal Fields internal static readonly UncommonFieldBitmapEffectStateField = new UncommonField (); internal delegate void AncestorChangedEventHandler(object sender, AncestorChangedEventArgs e); // index in parent child array. no meaning if parent is null. // note that we maintain in debug that the _parentIndex is -1 if the parent is null. internal int _parentIndex; // ([....]) I think we have to change the API so that we can save // here. For now that is good enough. internal DependencyObject _parent; internal VisualProxy _proxy; #endregion Internal Fields // -------------------------------------------------------------------- // // Private Fields // // ------------------------------------------------------------------- #region Private Fields // bbox in inner coordinate space of this node including its children. private Rect _bboxSubgraph = Rect.Empty; // // Store the visual brushes hold on to this visual. Also store the corresponding // number of channel, on which that visual brush holds on to this visual. // private static readonly UncommonField > VisualBrushToChannelsMapField = new UncommonField >(); // // Store the channels on which visual brushes hold on to this visual. Also store the // corresponding number of visual brushes on that channel, holding on to this visual. // private static readonly UncommonField > ChannelsToVisualBrushMapField = new UncommonField >(); private static readonly UncommonField ClipField = new UncommonField (); private static readonly UncommonField
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- XmlReflectionImporter.cs
- GridViewEditEventArgs.cs
- TemplateModeChangedEventArgs.cs
- QueryRelOp.cs
- FixedSOMImage.cs
- BaseAppDomainProtocolHandler.cs
- SecureUICommand.cs
- AnnotationAuthorChangedEventArgs.cs
- ApplicationHost.cs
- AddInServer.cs
- RegexFCD.cs
- PersonalizationAdministration.cs
- DeviceContexts.cs
- SignatureHelper.cs
- CompositeControl.cs
- KeySpline.cs
- EventManager.cs
- EdmSchemaError.cs
- DelayedRegex.cs
- LostFocusEventManager.cs
- DataMember.cs
- HttpCachePolicy.cs
- MailBnfHelper.cs
- DataSourceCacheDurationConverter.cs
- Pair.cs
- RecordBuilder.cs
- TreeNodeEventArgs.cs
- WarningException.cs
- PropertyGridCommands.cs
- Asn1Utilities.cs
- WeakRefEnumerator.cs
- Repeater.cs
- TextRangeAdaptor.cs
- ExtentKey.cs
- FloatUtil.cs
- XmlObjectSerializerReadContextComplexJson.cs
- ContextProperty.cs
- PenContexts.cs
- Internal.cs
- Executor.cs
- ChineseLunisolarCalendar.cs
- HttpCapabilitiesBase.cs
- Wildcard.cs
- StaticContext.cs
- StylusButton.cs
- LinkUtilities.cs
- MissingMemberException.cs
- DocumentSchemaValidator.cs
- OleDbWrapper.cs
- SessionStateItemCollection.cs
- MediaScriptCommandRoutedEventArgs.cs
- ResourceContainer.cs
- DataGridPageChangedEventArgs.cs
- ReflectionServiceProvider.cs
- AppearanceEditorPart.cs
- DataSourceView.cs
- RemoteWebConfigurationHostStream.cs
- DocumentViewerBaseAutomationPeer.cs
- NavigationPropertyEmitter.cs
- Formatter.cs
- MyContact.cs
- FileVersion.cs
- PkcsUtils.cs
- ForwardPositionQuery.cs
- Package.cs
- Soap12ServerProtocol.cs
- LinqDataSourceSelectEventArgs.cs
- KeyGestureConverter.cs
- CodeGenerator.cs
- BulletChrome.cs
- SqlCharStream.cs
- FontInfo.cs
- SBCSCodePageEncoding.cs
- Renderer.cs
- diagnosticsswitches.cs
- SiteOfOriginContainer.cs
- RegexBoyerMoore.cs
- XhtmlBasicCommandAdapter.cs
- EditCommandColumn.cs
- WriteableBitmap.cs
- XmlSchemaException.cs
- Style.cs
- MimeXmlReflector.cs
- StreamGeometry.cs
- UserValidatedEventArgs.cs
- GridErrorDlg.cs
- TranslateTransform.cs
- ParamArrayAttribute.cs
- SqlProviderServices.cs
- AssemblyNameUtility.cs
- Evidence.cs
- CmsInterop.cs
- PagePropertiesChangingEventArgs.cs
- MimeParameters.cs
- DocumentViewerHelper.cs
- ValidatedMobileControlConverter.cs
- IntegerFacetDescriptionElement.cs
- LinkLabel.cs
- FactoryGenerator.cs
- ActivityStatusChangeEventArgs.cs