Renderer.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Core / CSharp / MS / Internal / Ink / Renderer.cs / 1 / Renderer.cs

                            //------------------------------------------------------------------------ 
// 
// Copyright (c) Microsoft Corporation. All rights reserved.
// 
//----------------------------------------------------------------------- 

using MS.Utility; 
using System; 
using System.Windows;
using System.Windows.Media; 
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics; 
using MS.Internal.Ink;
using MS.Internal; 
using MS.Internal.PresentationCore; 

namespace System.Windows.Ink 
{
    /// 
    /// The Renderer class is used to render a stroke collection.
    /// This class listens to stroke added and removed events on the 
    /// stroke collection and updates the internal visual tree.
    /// It also listens to the Invalidated event on Stroke (fired when 
    /// DrawingAttributes changes, packet data changes, DrawingAttributes 
    /// replaced, as well as Stroke developer calls Stroke.OnInvalidated)
    /// and updates the visual state as necessary. 
    /// 
    ///
    [FriendAccessAllowed] // Built into Core, also used by Framework.
    internal class Renderer 
    {
        #region StrokeVisual 
 
        /// 
        /// A retained visual for rendering a single stroke in a stroke collection view 
        /// 
        private class StrokeVisual : System.Windows.Media.DrawingVisual
        {
            ///  
            /// Constructor
            ///  
            /// a stroke to render into this visual 
            /// a renderer associated to this visual
            internal StrokeVisual(Stroke stroke, Renderer renderer) : base() 
            {
                Debug.Assert(renderer != null);

                if (stroke == null) 
                {
                    throw new System.ArgumentNullException("stroke"); 
                } 

                _stroke = stroke; 
                _renderer = renderer;

                // The original value of the color and IsHighlighter are cached so
                // when Stroke.Invalidated is fired, Renderer knows whether re-arranging 
                // the visual tree is needed.
                _cachedColor = stroke.DrawingAttributes.Color; 
                _cachedIsHighlighter = stroke.DrawingAttributes.IsHighlighter; 

                // Update the visual contents 
                Update();
            }

            ///  
            /// The Stroke rendedered into this visual.
            ///  
            internal Stroke Stroke 
            {
                get { return _stroke; } 
            }

            /// 
            /// Updates the contents of the visual. 
            /// 
            internal void Update() 
            { 
                using (DrawingContext drawingContext = RenderOpen())
                { 
                    bool highContrast = _renderer.IsHighContrast();

                    if (highContrast == true && _stroke.DrawingAttributes.IsHighlighter)
                    { 
                        // we don't render highlighters in high contrast
                        return; 
                    } 

                    DrawingAttributes da; 
                    if (highContrast)
                    {
                        da = _stroke.DrawingAttributes.Clone();
                        da.Color = _renderer.GetHighContrastColor(); 
                    }
                    else if (_stroke.DrawingAttributes.IsHighlighter == true) 
                    { 
                        // Get the drawing attributes to use for a highlighter stroke. This can be a copied DA with color.A
                        // overridden if color.A != 255. 
                        da = StrokeRenderer.GetHighlighterAttributes(_stroke, _stroke.DrawingAttributes);
                    }
                    else
                    { 
                        // Otherwise, usethe DA on this stroke
                        da = _stroke.DrawingAttributes; 
                    } 

                    // Draw selected stroke as hollow 
                    _stroke.DrawInternal (drawingContext, da, _stroke.IsSelected );
                }
            }
 
            /// 
            /// StrokeVisual should not be hittestable as it interferes with event routing 
            ///  
            protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParams)
            { 
                return null;
            }

            ///  
            /// The previous value of IsHighlighter
            ///  
            internal bool CachedIsHighlighter 
            {
                get {return _cachedIsHighlighter;} 
                set {_cachedIsHighlighter = value;}
            }

            ///  
            /// The previous value of Color
            ///  
            internal Color CachedColor 
            {
                get {return _cachedColor;} 
                set {_cachedColor = value;}
            }

            private Stroke                      _stroke; 
            private bool                        _cachedIsHighlighter;
            private Color                       _cachedColor; 
            private Renderer                    _renderer; 

        } 

        /// 
        /// Private helper that helps reverse map the highlighter dictionary
        ///  
        private class HighlighterContainerVisual : ContainerVisual
        { 
            internal HighlighterContainerVisual(Color color) 
            {
                _color = color; 
            }

            /// 
            /// The Color of the strokes in this highlighter container visual 
            /// 
            internal Color Color 
            { 
                get { return _color; }
            } 
            private Color _color;
        }

        #endregion 

        #region Internal interface 
 
        /// 
        /// Public Constructor 
        /// 
        internal Renderer()
        {
            // Initialize the data members. 
            // We intentionally don't use lazy initialization for the core members to avoid
            // hidden bug situations when one thing has been created while another not. 
            // If the user is looking for lazy initialization, she should do that on her 
            // own and create Renderer only when there's a need for it.
 
            // Create visuals that'll be the containers for all other visuals
            // created by the Renderer. This visuals are created once and are
            // not supposed to be replaced nor destroyed while the Renderer is alive.
            _rootVisual = new ContainerVisual(); 
            _highlightersRoot = new ContainerVisual();
            _regularInkVisuals = new ContainerVisual(); 
            _incrementalRenderingVisuals = new ContainerVisual(); 

            // Highlighters go to the bottom, then regular ink, and regular 
            // ink' incremental rendering in on top.
            VisualCollection rootChildren = _rootVisual.Children;
            rootChildren.Add(_highlightersRoot);
            rootChildren.Add(_regularInkVisuals); 
            rootChildren.Add(_incrementalRenderingVisuals);
 
            // Set the default value of highcontrast to be false. 
            _highContrast = false;
 
            // Create a stroke-visual dictionary
            _visuals = new Dictionary();
        }
 
        /// 
        /// Returns a reference to a visual tree that can be used to render the ink. 
        /// This property may be either a single visual or a container visual with 
        /// children. The element uses this visual as a child visual and arranges
        /// it with respects to the other siblings. 
        /// Note: No visuals are actually generated until the application gets
        /// this property for the first time. If no strokes are set then an empty
        /// visual is returned.
        ///  
        internal Visual RootVisual { get { return _rootVisual; } }
 
        ///  
        /// Set the strokes property to the collection of strokes to be rendered.
        /// The Renderer will then listen to changes to the StrokeCollection 
        /// and update its state to reflect the changes.
        /// 
        internal StrokeCollection Strokes
        { 
            get
            { 
                // We should never return a null value. 
                if ( _strokes == null )
                { 
                    _strokes = new StrokeCollection();

                    // Start listening on events from the stroke collection.
                    _strokes.StrokesChangedInternal += new StrokeCollectionChangedEventHandler(OnStrokesChanged); 
                }
 
                return _strokes; 
            }
            set 
            {
                if (value == null)
                {
                    throw new System.ArgumentNullException("value"); 
                }
                if (value == _strokes) 
                { 
                    return;
                } 

                // Detach the current stroke collection
                if (null != _strokes)
                { 
                    // Stop listening on events from the stroke collection.
                    _strokes.StrokesChangedInternal -= new StrokeCollectionChangedEventHandler(OnStrokesChanged); 
 
                    foreach (StrokeVisual visual in _visuals.Values)
                    { 
                        StopListeningOnStrokeEvents(visual.Stroke);
                        // Detach the visual from the tree
                        DetachVisual(visual);
                    } 
                    _visuals.Clear();
                } 
 
                // Set it.
                _strokes = value; 

                // Create visuals
                foreach (Stroke stroke in _strokes)
                { 
                    // Create a visual per stroke
                    StrokeVisual visual = new StrokeVisual(stroke, this); 
 
                    // Store the stroke-visual pair in the dictionary
                    _visuals.Add(stroke, visual); 

                    StartListeningOnStrokeEvents(visual.Stroke);

                    // Attach it to the visual tree 
                    AttachVisual(visual, true/*buildingStrokeCollection*/);
                } 
 
                // Start listening on events from the stroke collection.
                _strokes.StrokesChangedInternal += new StrokeCollectionChangedEventHandler(OnStrokesChanged); 
            }
        }

        ///  
        /// User supposed to use this method to attach IncrementalRenderer's root visual
        /// to the visual tree of a stroke collection view. 
        ///  
        /// visual to attach
        /// drawing attributes that used in the incremental rendering 
        internal void AttachIncrementalRendering(Visual visual, DrawingAttributes drawingAttributes)
        {
            // Check the input parameters
            if (visual == null) 
            {
                throw new System.ArgumentNullException("visual"); 
            } 
            if (drawingAttributes == null)
            { 
                throw new System.ArgumentNullException("drawingAttributes");
            }

            //harden against eaten exceptions 
            bool exceptionRaised = false;
 
            // Verify that the visual hasn't been attached already 
            if (_attachedVisuals != null)
            { 
                foreach(Visual alreadyAttachedVisual in _attachedVisuals)
                {
                    if (visual == alreadyAttachedVisual)
                    { 
                        exceptionRaised = true;
                        throw new System.InvalidOperationException(SR.Get(SRID.CannotAttachVisualTwice)); 
                    } 
                }
            } 
            else
            {
                // Create the list to register attached visuals in
                _attachedVisuals = new List(); 
            }
 
 
            if (!exceptionRaised)
            { 
                // The position of the visual in the tree depends on the drawingAttributes
                // Find the appropriate parent visual to attach this visual to.
                ContainerVisual parent = drawingAttributes.IsHighlighter ? GetContainerVisual(drawingAttributes) : _incrementalRenderingVisuals;
 
                // Attach the visual to the tree
                parent.Children.Add(visual); 
 
                // Put the visual into the list of visuals attached via this method
                _attachedVisuals.Add(visual); 
            }
        }

        ///  
        /// Detaches a visual previously attached via AttachIncrementalRendering
        ///  
        /// the visual to detach 
        internal void DetachIncrementalRendering(Visual visual)
        { 
            if (visual == null)
            {
                throw new System.ArgumentNullException("visual");
            } 

            // Remove the visual in the list of attached via AttachIncrementalRendering 
            if ((_attachedVisuals == null) || (_attachedVisuals.Remove(visual) == false)) 
            {
                throw new System.InvalidOperationException(SR.Get(SRID.VisualCannotBeDetached)); 
            }

            // Detach it from the tree
            DetachVisual(visual); 
        }
 
        ///  
        /// Internal helper used to indicate if a visual was previously attached
        /// via a call to AttachIncrementalRendering 
        /// 
        internal bool ContainsAttachedIncrementalRenderingVisual(Visual visual)
        {
            if (visual == null || _attachedVisuals == null) 
            {
                return false; 
            } 

            return _attachedVisuals.Contains(visual); 
        }

        /// 
        /// Internal helper used to determine if a visual is in the right spot in the visual tree 
        /// 
        internal bool AttachedVisualIsPositionedCorrectly(Visual visual, DrawingAttributes drawingAttributes) 
        { 
            if (visual == null || drawingAttributes == null || _attachedVisuals == null || !_attachedVisuals.Contains(visual))
            { 
                return false;
            }

            ContainerVisual correctParent 
                = drawingAttributes.IsHighlighter ? GetContainerVisual(drawingAttributes) : _incrementalRenderingVisuals;
 
            ContainerVisual currentParent 
                = VisualTreeHelper.GetParent(visual) as ContainerVisual;
 
            if (currentParent == null || correctParent != currentParent)
            {
                return false;
            } 
            return true;
        } 
 

 
        /// 
        /// TurnOnHighContrast turns on the HighContrast rendering mode
        /// 
        /// The stroke color under high contrast 
        internal void TurnHighContrastOn(Color strokeColor)
        { 
            if ( !_highContrast || strokeColor != _highContrastColor ) 
            {
                _highContrast = true; 
                _highContrastColor = strokeColor;
                UpdateStrokeVisuals();
            }
        } 

        ///  
        /// ResetHighContrast turns off the HighContrast mode 
        /// 
        internal void TurnHighContrastOff() 
        {
            if ( _highContrast )
            {
                _highContrast = false; 
                UpdateStrokeVisuals();
            } 
        } 

        ///  
        /// Indicates whether the renderer is in high contrast mode.
        /// 
        /// 
        internal bool IsHighContrast() 
        {
            return _highContrast; 
        } 

        ///  
        /// returns the stroke color for the high contrast rendering.
        /// 
        /// 
        public Color GetHighContrastColor() 
        {
            return _highContrastColor; 
        } 

        #endregion 

        #region Event handlers

        ///  
        /// StrokeCollectionChanged event handler
        ///  
        private void OnStrokesChanged(object sender, StrokeCollectionChangedEventArgs eventArgs) 
        {
            System.Diagnostics.Debug.Assert(sender == _strokes); 

            // Read the args
            StrokeCollection added = eventArgs.Added;
            StrokeCollection removed = eventArgs.Removed; 

            // Add new strokes 
            foreach (Stroke stroke in added) 
            {
                // Verify that it's not a dupe 
                if (_visuals.ContainsKey(stroke))
                {
                    throw new System.ArgumentException(SR.Get(SRID.DuplicateStrokeAdded));
                } 

                // Create a visual for the new stroke and add it to the dictionary 
                StrokeVisual visual = new StrokeVisual(stroke, this); 
                _visuals.Add(stroke, visual);
 
                // Start listening on the stroke events
                StartListeningOnStrokeEvents(visual.Stroke);

                // Attach it to the visual tree 
                AttachVisual(visual, false/*buildingStrokeCollection*/);
            } 
 
            // Deal with removed strokes first
            foreach (Stroke stroke in removed) 
            {
                // Verify that the event is in [....] with the view
                StrokeVisual visual = null;
                if (_visuals.TryGetValue(stroke, out visual)) 
                {
                    // get rid of both the visual and the stroke 
                    DetachVisual(visual); 
                    StopListeningOnStrokeEvents(visual.Stroke);
                    _visuals.Remove(stroke); 
                }
                else
                {
                    throw new System.ArgumentException(SR.Get(SRID.UnknownStroke3)); 
                }
            } 
        } 

        ///  
        /// Stroke Invalidated event handler
        /// 
        private void OnStrokeInvalidated(object sender, EventArgs eventArgs)
        { 
            System.Diagnostics.Debug.Assert(_strokes.IndexOf(sender as Stroke) != -1);
 
            // Find the visual associated with the changed stroke. 
            StrokeVisual visual;
            Stroke stroke = (Stroke)sender; 
            if (_visuals.TryGetValue(stroke, out visual) == false)
            {
                throw new System.ArgumentException(SR.Get(SRID.UnknownStroke1));
            } 

            // The original value of IsHighligher and Color are cached in StrokeVisual. 
            // if (IsHighlighter value changed or (IsHighlighter == true and not changed and color changed) 
            // detach and re-attach the corresponding visual;
            // otherwise Invalidate the corresponding StrokeVisual 
            if (visual.CachedIsHighlighter != stroke.DrawingAttributes.IsHighlighter ||
                (stroke.DrawingAttributes.IsHighlighter &&
                    StrokeRenderer.GetHighlighterColor(visual.CachedColor) != StrokeRenderer.GetHighlighterColor(stroke.DrawingAttributes.Color)))
            { 
                // The change requires reparenting the visual in the tree.
                DetachVisual(visual); 
                AttachVisual(visual, false/*buildingStrokeCollection*/); 

                // Update the cached values 
                visual.CachedIsHighlighter = stroke.DrawingAttributes.IsHighlighter;
                visual.CachedColor = stroke.DrawingAttributes.Color;
            }
            // Update the visual. 
            visual.Update();
        } 
 
        #endregion
 
        #region Helper methods

        /// 
        /// Update the stroke visuals 
        /// 
        private void UpdateStrokeVisuals() 
        { 
            foreach ( StrokeVisual strokeVisual in _visuals.Values )
            { 
                strokeVisual.Update();
            }
        }
 
        /// 
        /// Attaches a stroke visual to the tree based on the stroke's 
        /// drawing attributes and/or its z-order (index in the collection). 
        /// 
        private void AttachVisual(StrokeVisual visual, bool buildingStrokeCollection) 
        {
            System.Diagnostics.Debug.Assert(_strokes != null);

            if (visual.Stroke.DrawingAttributes.IsHighlighter) 
            {
                // Find or create a container visual for highlighter strokes of the color 
                ContainerVisual parent = GetContainerVisual(visual.Stroke.DrawingAttributes); 
                Debug.Assert(visual is StrokeVisual);
 
                //insert StrokeVisuals under any non-StrokeVisuals used for dynamic inking
                int i = 0;
                for (int j = parent.Children.Count - 1; j >= 0; j--)
                { 
                    if (parent.Children[j] is StrokeVisual)
                    { 
                        i = j + 1; 
                        break;
                    } 
                }
                parent.Children.Insert(i, visual);
            }
            else 
            {
                // For regular ink we have to respect the z-order of the strokes. 
                // The implementation below is not optimal in a generic case, but the 
                // most simple and should work ok in most common scenarios.
 
                // Find the nearest non-highlighter stroke with a lower z-order
                // and insert the new visual right next to the visual of that stroke.
                StrokeVisual precedingVisual = null;
                int i = 0; 
                if (buildingStrokeCollection)
                { 
                    Stroke visualStroke = visual.Stroke; 
                    //we're building up a stroke collection, no need to start at IndexOf,
                    i = Math.Min(_visuals.Count, _strokes.Count); //not -1, we're about to decrement 
                    while (--i >= 0)
                    {
                        if (object.ReferenceEquals(_strokes[i], visualStroke))
                        { 
                            break;
                        } 
                    } 
                }
                else 
                {
                    i = _strokes.IndexOf(visual.Stroke);
                }
                while (--i >= 0) 
                {
                    Stroke stroke = _strokes[i]; 
                    if ((stroke.DrawingAttributes.IsHighlighter == false) 
                        && (_visuals.TryGetValue(stroke, out precedingVisual) == true)
                        && (VisualTreeHelper.GetParent(precedingVisual) != null)) 
                    {
                        VisualCollection children = ((ContainerVisual)(VisualTreeHelper.GetParent(precedingVisual))).Children;
                        int index = children.IndexOf(precedingVisual);
                        children.Insert(index + 1, visual); 
                        break;
                    } 
                } 
                // If found no non-highlighter strokes with a lower z-order, insert
                // the stroke at the very bottom of the regular ink visual tree. 
                if (i < 0)
                {
                    ContainerVisual parent = GetContainerVisual(visual.Stroke.DrawingAttributes);
                    parent.Children.Insert(0, visual); 
                }
            } 
        } 

        ///  
        /// Detaches a visual from the tree, also removes highligher parents if empty
        /// when true is passed
        /// 
        private void DetachVisual(Visual visual) 
        {
            ContainerVisual parent = (ContainerVisual)(VisualTreeHelper.GetParent(visual)); 
            if (parent != null) 
            {
                VisualCollection children = parent.Children; 
                children.Remove(visual);

                // If the parent is a childless highlighter, detach it too.
                HighlighterContainerVisual hcVisual = parent as HighlighterContainerVisual; 
                if (hcVisual != null &&
                    hcVisual.Children.Count == 0 && 
                    _highlighters != null && 
                    _highlighters.ContainsValue(hcVisual))
                { 
                    DetachVisual(hcVisual);
                    _highlighters.Remove(hcVisual.Color);
                }
            } 
        }
 
        ///  
        /// Attaches event handlers to stroke events
        ///  
        private void StartListeningOnStrokeEvents(Stroke stroke)
        {
            System.Diagnostics.Debug.Assert(stroke != null);
            stroke.Invalidated += new EventHandler(OnStrokeInvalidated); 
        }
 
        ///  
        /// Detaches event handlers from stroke
        ///  
        private void StopListeningOnStrokeEvents(Stroke stroke)
        {
            System.Diagnostics.Debug.Assert(stroke != null);
            stroke.Invalidated -= new EventHandler(OnStrokeInvalidated); 
        }
 
        ///  
        /// Finds a container for a new visual based on the drawing attributes
        /// of the stroke rendered into that visual. 
        /// 
        /// drawing attributes
        /// visual
        private ContainerVisual GetContainerVisual(DrawingAttributes drawingAttributes) 
        {
            System.Diagnostics.Debug.Assert(drawingAttributes != null); 
 
            HighlighterContainerVisual hcVisual;
            if (drawingAttributes.IsHighlighter) 
            {
                // For a highlighter stroke, the color.A is neglected.
                Color color = StrokeRenderer.GetHighlighterColor(drawingAttributes.Color);
                if ((_highlighters == null) || (_highlighters.TryGetValue(color, out hcVisual) == false)) 
                {
                    if (_highlighters == null) 
                    { 
                        _highlighters = new Dictionary();
                    } 

                    hcVisual = new HighlighterContainerVisual(color);
                    hcVisual.Opacity = StrokeRenderer.HighlighterOpacity;
                    _highlightersRoot.Children.Add(hcVisual); 

                    _highlighters.Add(color, hcVisual); 
                } 
                else if (VisualTreeHelper.GetParent(hcVisual) == null)
                { 
                    _highlightersRoot.Children.Add(hcVisual);
                }
                return hcVisual;
            } 
            else
            { 
                return _regularInkVisuals; 
            }
        } 

        #endregion

        #region Fields 

        // The renderer's top level container visuals 
        private ContainerVisual _rootVisual; 
        private ContainerVisual _highlightersRoot;
        private ContainerVisual _incrementalRenderingVisuals; 
        private ContainerVisual _regularInkVisuals;

        // Stroke-to-visual map
        private Dictionary _visuals; 

        // Color-to-visual map for highlighter ink container visuals 
        private Dictionary _highlighters = null; 

        // Collection of strokes this Renderer renders 
        private StrokeCollection _strokes = null;

        // List of visuals attached via AttachIncrementalRendering
        private List _attachedVisuals = null; 

        // Whhen true, will render in high contrast mode 
        private bool _highContrast; 
        private Color _highContrastColor = Colors.White;
 
        #endregion
    }
}
 


// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------ 
// 
// Copyright (c) Microsoft Corporation. All rights reserved.
// 
//----------------------------------------------------------------------- 

using MS.Utility; 
using System; 
using System.Windows;
using System.Windows.Media; 
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics; 
using MS.Internal.Ink;
using MS.Internal; 
using MS.Internal.PresentationCore; 

namespace System.Windows.Ink 
{
    /// 
    /// The Renderer class is used to render a stroke collection.
    /// This class listens to stroke added and removed events on the 
    /// stroke collection and updates the internal visual tree.
    /// It also listens to the Invalidated event on Stroke (fired when 
    /// DrawingAttributes changes, packet data changes, DrawingAttributes 
    /// replaced, as well as Stroke developer calls Stroke.OnInvalidated)
    /// and updates the visual state as necessary. 
    /// 
    ///
    [FriendAccessAllowed] // Built into Core, also used by Framework.
    internal class Renderer 
    {
        #region StrokeVisual 
 
        /// 
        /// A retained visual for rendering a single stroke in a stroke collection view 
        /// 
        private class StrokeVisual : System.Windows.Media.DrawingVisual
        {
            ///  
            /// Constructor
            ///  
            /// a stroke to render into this visual 
            /// a renderer associated to this visual
            internal StrokeVisual(Stroke stroke, Renderer renderer) : base() 
            {
                Debug.Assert(renderer != null);

                if (stroke == null) 
                {
                    throw new System.ArgumentNullException("stroke"); 
                } 

                _stroke = stroke; 
                _renderer = renderer;

                // The original value of the color and IsHighlighter are cached so
                // when Stroke.Invalidated is fired, Renderer knows whether re-arranging 
                // the visual tree is needed.
                _cachedColor = stroke.DrawingAttributes.Color; 
                _cachedIsHighlighter = stroke.DrawingAttributes.IsHighlighter; 

                // Update the visual contents 
                Update();
            }

            ///  
            /// The Stroke rendedered into this visual.
            ///  
            internal Stroke Stroke 
            {
                get { return _stroke; } 
            }

            /// 
            /// Updates the contents of the visual. 
            /// 
            internal void Update() 
            { 
                using (DrawingContext drawingContext = RenderOpen())
                { 
                    bool highContrast = _renderer.IsHighContrast();

                    if (highContrast == true && _stroke.DrawingAttributes.IsHighlighter)
                    { 
                        // we don't render highlighters in high contrast
                        return; 
                    } 

                    DrawingAttributes da; 
                    if (highContrast)
                    {
                        da = _stroke.DrawingAttributes.Clone();
                        da.Color = _renderer.GetHighContrastColor(); 
                    }
                    else if (_stroke.DrawingAttributes.IsHighlighter == true) 
                    { 
                        // Get the drawing attributes to use for a highlighter stroke. This can be a copied DA with color.A
                        // overridden if color.A != 255. 
                        da = StrokeRenderer.GetHighlighterAttributes(_stroke, _stroke.DrawingAttributes);
                    }
                    else
                    { 
                        // Otherwise, usethe DA on this stroke
                        da = _stroke.DrawingAttributes; 
                    } 

                    // Draw selected stroke as hollow 
                    _stroke.DrawInternal (drawingContext, da, _stroke.IsSelected );
                }
            }
 
            /// 
            /// StrokeVisual should not be hittestable as it interferes with event routing 
            ///  
            protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParams)
            { 
                return null;
            }

            ///  
            /// The previous value of IsHighlighter
            ///  
            internal bool CachedIsHighlighter 
            {
                get {return _cachedIsHighlighter;} 
                set {_cachedIsHighlighter = value;}
            }

            ///  
            /// The previous value of Color
            ///  
            internal Color CachedColor 
            {
                get {return _cachedColor;} 
                set {_cachedColor = value;}
            }

            private Stroke                      _stroke; 
            private bool                        _cachedIsHighlighter;
            private Color                       _cachedColor; 
            private Renderer                    _renderer; 

        } 

        /// 
        /// Private helper that helps reverse map the highlighter dictionary
        ///  
        private class HighlighterContainerVisual : ContainerVisual
        { 
            internal HighlighterContainerVisual(Color color) 
            {
                _color = color; 
            }

            /// 
            /// The Color of the strokes in this highlighter container visual 
            /// 
            internal Color Color 
            { 
                get { return _color; }
            } 
            private Color _color;
        }

        #endregion 

        #region Internal interface 
 
        /// 
        /// Public Constructor 
        /// 
        internal Renderer()
        {
            // Initialize the data members. 
            // We intentionally don't use lazy initialization for the core members to avoid
            // hidden bug situations when one thing has been created while another not. 
            // If the user is looking for lazy initialization, she should do that on her 
            // own and create Renderer only when there's a need for it.
 
            // Create visuals that'll be the containers for all other visuals
            // created by the Renderer. This visuals are created once and are
            // not supposed to be replaced nor destroyed while the Renderer is alive.
            _rootVisual = new ContainerVisual(); 
            _highlightersRoot = new ContainerVisual();
            _regularInkVisuals = new ContainerVisual(); 
            _incrementalRenderingVisuals = new ContainerVisual(); 

            // Highlighters go to the bottom, then regular ink, and regular 
            // ink' incremental rendering in on top.
            VisualCollection rootChildren = _rootVisual.Children;
            rootChildren.Add(_highlightersRoot);
            rootChildren.Add(_regularInkVisuals); 
            rootChildren.Add(_incrementalRenderingVisuals);
 
            // Set the default value of highcontrast to be false. 
            _highContrast = false;
 
            // Create a stroke-visual dictionary
            _visuals = new Dictionary();
        }
 
        /// 
        /// Returns a reference to a visual tree that can be used to render the ink. 
        /// This property may be either a single visual or a container visual with 
        /// children. The element uses this visual as a child visual and arranges
        /// it with respects to the other siblings. 
        /// Note: No visuals are actually generated until the application gets
        /// this property for the first time. If no strokes are set then an empty
        /// visual is returned.
        ///  
        internal Visual RootVisual { get { return _rootVisual; } }
 
        ///  
        /// Set the strokes property to the collection of strokes to be rendered.
        /// The Renderer will then listen to changes to the StrokeCollection 
        /// and update its state to reflect the changes.
        /// 
        internal StrokeCollection Strokes
        { 
            get
            { 
                // We should never return a null value. 
                if ( _strokes == null )
                { 
                    _strokes = new StrokeCollection();

                    // Start listening on events from the stroke collection.
                    _strokes.StrokesChangedInternal += new StrokeCollectionChangedEventHandler(OnStrokesChanged); 
                }
 
                return _strokes; 
            }
            set 
            {
                if (value == null)
                {
                    throw new System.ArgumentNullException("value"); 
                }
                if (value == _strokes) 
                { 
                    return;
                } 

                // Detach the current stroke collection
                if (null != _strokes)
                { 
                    // Stop listening on events from the stroke collection.
                    _strokes.StrokesChangedInternal -= new StrokeCollectionChangedEventHandler(OnStrokesChanged); 
 
                    foreach (StrokeVisual visual in _visuals.Values)
                    { 
                        StopListeningOnStrokeEvents(visual.Stroke);
                        // Detach the visual from the tree
                        DetachVisual(visual);
                    } 
                    _visuals.Clear();
                } 
 
                // Set it.
                _strokes = value; 

                // Create visuals
                foreach (Stroke stroke in _strokes)
                { 
                    // Create a visual per stroke
                    StrokeVisual visual = new StrokeVisual(stroke, this); 
 
                    // Store the stroke-visual pair in the dictionary
                    _visuals.Add(stroke, visual); 

                    StartListeningOnStrokeEvents(visual.Stroke);

                    // Attach it to the visual tree 
                    AttachVisual(visual, true/*buildingStrokeCollection*/);
                } 
 
                // Start listening on events from the stroke collection.
                _strokes.StrokesChangedInternal += new StrokeCollectionChangedEventHandler(OnStrokesChanged); 
            }
        }

        ///  
        /// User supposed to use this method to attach IncrementalRenderer's root visual
        /// to the visual tree of a stroke collection view. 
        ///  
        /// visual to attach
        /// drawing attributes that used in the incremental rendering 
        internal void AttachIncrementalRendering(Visual visual, DrawingAttributes drawingAttributes)
        {
            // Check the input parameters
            if (visual == null) 
            {
                throw new System.ArgumentNullException("visual"); 
            } 
            if (drawingAttributes == null)
            { 
                throw new System.ArgumentNullException("drawingAttributes");
            }

            //harden against eaten exceptions 
            bool exceptionRaised = false;
 
            // Verify that the visual hasn't been attached already 
            if (_attachedVisuals != null)
            { 
                foreach(Visual alreadyAttachedVisual in _attachedVisuals)
                {
                    if (visual == alreadyAttachedVisual)
                    { 
                        exceptionRaised = true;
                        throw new System.InvalidOperationException(SR.Get(SRID.CannotAttachVisualTwice)); 
                    } 
                }
            } 
            else
            {
                // Create the list to register attached visuals in
                _attachedVisuals = new List(); 
            }
 
 
            if (!exceptionRaised)
            { 
                // The position of the visual in the tree depends on the drawingAttributes
                // Find the appropriate parent visual to attach this visual to.
                ContainerVisual parent = drawingAttributes.IsHighlighter ? GetContainerVisual(drawingAttributes) : _incrementalRenderingVisuals;
 
                // Attach the visual to the tree
                parent.Children.Add(visual); 
 
                // Put the visual into the list of visuals attached via this method
                _attachedVisuals.Add(visual); 
            }
        }

        ///  
        /// Detaches a visual previously attached via AttachIncrementalRendering
        ///  
        /// the visual to detach 
        internal void DetachIncrementalRendering(Visual visual)
        { 
            if (visual == null)
            {
                throw new System.ArgumentNullException("visual");
            } 

            // Remove the visual in the list of attached via AttachIncrementalRendering 
            if ((_attachedVisuals == null) || (_attachedVisuals.Remove(visual) == false)) 
            {
                throw new System.InvalidOperationException(SR.Get(SRID.VisualCannotBeDetached)); 
            }

            // Detach it from the tree
            DetachVisual(visual); 
        }
 
        ///  
        /// Internal helper used to indicate if a visual was previously attached
        /// via a call to AttachIncrementalRendering 
        /// 
        internal bool ContainsAttachedIncrementalRenderingVisual(Visual visual)
        {
            if (visual == null || _attachedVisuals == null) 
            {
                return false; 
            } 

            return _attachedVisuals.Contains(visual); 
        }

        /// 
        /// Internal helper used to determine if a visual is in the right spot in the visual tree 
        /// 
        internal bool AttachedVisualIsPositionedCorrectly(Visual visual, DrawingAttributes drawingAttributes) 
        { 
            if (visual == null || drawingAttributes == null || _attachedVisuals == null || !_attachedVisuals.Contains(visual))
            { 
                return false;
            }

            ContainerVisual correctParent 
                = drawingAttributes.IsHighlighter ? GetContainerVisual(drawingAttributes) : _incrementalRenderingVisuals;
 
            ContainerVisual currentParent 
                = VisualTreeHelper.GetParent(visual) as ContainerVisual;
 
            if (currentParent == null || correctParent != currentParent)
            {
                return false;
            } 
            return true;
        } 
 

 
        /// 
        /// TurnOnHighContrast turns on the HighContrast rendering mode
        /// 
        /// The stroke color under high contrast 
        internal void TurnHighContrastOn(Color strokeColor)
        { 
            if ( !_highContrast || strokeColor != _highContrastColor ) 
            {
                _highContrast = true; 
                _highContrastColor = strokeColor;
                UpdateStrokeVisuals();
            }
        } 

        ///  
        /// ResetHighContrast turns off the HighContrast mode 
        /// 
        internal void TurnHighContrastOff() 
        {
            if ( _highContrast )
            {
                _highContrast = false; 
                UpdateStrokeVisuals();
            } 
        } 

        ///  
        /// Indicates whether the renderer is in high contrast mode.
        /// 
        /// 
        internal bool IsHighContrast() 
        {
            return _highContrast; 
        } 

        ///  
        /// returns the stroke color for the high contrast rendering.
        /// 
        /// 
        public Color GetHighContrastColor() 
        {
            return _highContrastColor; 
        } 

        #endregion 

        #region Event handlers

        ///  
        /// StrokeCollectionChanged event handler
        ///  
        private void OnStrokesChanged(object sender, StrokeCollectionChangedEventArgs eventArgs) 
        {
            System.Diagnostics.Debug.Assert(sender == _strokes); 

            // Read the args
            StrokeCollection added = eventArgs.Added;
            StrokeCollection removed = eventArgs.Removed; 

            // Add new strokes 
            foreach (Stroke stroke in added) 
            {
                // Verify that it's not a dupe 
                if (_visuals.ContainsKey(stroke))
                {
                    throw new System.ArgumentException(SR.Get(SRID.DuplicateStrokeAdded));
                } 

                // Create a visual for the new stroke and add it to the dictionary 
                StrokeVisual visual = new StrokeVisual(stroke, this); 
                _visuals.Add(stroke, visual);
 
                // Start listening on the stroke events
                StartListeningOnStrokeEvents(visual.Stroke);

                // Attach it to the visual tree 
                AttachVisual(visual, false/*buildingStrokeCollection*/);
            } 
 
            // Deal with removed strokes first
            foreach (Stroke stroke in removed) 
            {
                // Verify that the event is in [....] with the view
                StrokeVisual visual = null;
                if (_visuals.TryGetValue(stroke, out visual)) 
                {
                    // get rid of both the visual and the stroke 
                    DetachVisual(visual); 
                    StopListeningOnStrokeEvents(visual.Stroke);
                    _visuals.Remove(stroke); 
                }
                else
                {
                    throw new System.ArgumentException(SR.Get(SRID.UnknownStroke3)); 
                }
            } 
        } 

        ///  
        /// Stroke Invalidated event handler
        /// 
        private void OnStrokeInvalidated(object sender, EventArgs eventArgs)
        { 
            System.Diagnostics.Debug.Assert(_strokes.IndexOf(sender as Stroke) != -1);
 
            // Find the visual associated with the changed stroke. 
            StrokeVisual visual;
            Stroke stroke = (Stroke)sender; 
            if (_visuals.TryGetValue(stroke, out visual) == false)
            {
                throw new System.ArgumentException(SR.Get(SRID.UnknownStroke1));
            } 

            // The original value of IsHighligher and Color are cached in StrokeVisual. 
            // if (IsHighlighter value changed or (IsHighlighter == true and not changed and color changed) 
            // detach and re-attach the corresponding visual;
            // otherwise Invalidate the corresponding StrokeVisual 
            if (visual.CachedIsHighlighter != stroke.DrawingAttributes.IsHighlighter ||
                (stroke.DrawingAttributes.IsHighlighter &&
                    StrokeRenderer.GetHighlighterColor(visual.CachedColor) != StrokeRenderer.GetHighlighterColor(stroke.DrawingAttributes.Color)))
            { 
                // The change requires reparenting the visual in the tree.
                DetachVisual(visual); 
                AttachVisual(visual, false/*buildingStrokeCollection*/); 

                // Update the cached values 
                visual.CachedIsHighlighter = stroke.DrawingAttributes.IsHighlighter;
                visual.CachedColor = stroke.DrawingAttributes.Color;
            }
            // Update the visual. 
            visual.Update();
        } 
 
        #endregion
 
        #region Helper methods

        /// 
        /// Update the stroke visuals 
        /// 
        private void UpdateStrokeVisuals() 
        { 
            foreach ( StrokeVisual strokeVisual in _visuals.Values )
            { 
                strokeVisual.Update();
            }
        }
 
        /// 
        /// Attaches a stroke visual to the tree based on the stroke's 
        /// drawing attributes and/or its z-order (index in the collection). 
        /// 
        private void AttachVisual(StrokeVisual visual, bool buildingStrokeCollection) 
        {
            System.Diagnostics.Debug.Assert(_strokes != null);

            if (visual.Stroke.DrawingAttributes.IsHighlighter) 
            {
                // Find or create a container visual for highlighter strokes of the color 
                ContainerVisual parent = GetContainerVisual(visual.Stroke.DrawingAttributes); 
                Debug.Assert(visual is StrokeVisual);
 
                //insert StrokeVisuals under any non-StrokeVisuals used for dynamic inking
                int i = 0;
                for (int j = parent.Children.Count - 1; j >= 0; j--)
                { 
                    if (parent.Children[j] is StrokeVisual)
                    { 
                        i = j + 1; 
                        break;
                    } 
                }
                parent.Children.Insert(i, visual);
            }
            else 
            {
                // For regular ink we have to respect the z-order of the strokes. 
                // The implementation below is not optimal in a generic case, but the 
                // most simple and should work ok in most common scenarios.
 
                // Find the nearest non-highlighter stroke with a lower z-order
                // and insert the new visual right next to the visual of that stroke.
                StrokeVisual precedingVisual = null;
                int i = 0; 
                if (buildingStrokeCollection)
                { 
                    Stroke visualStroke = visual.Stroke; 
                    //we're building up a stroke collection, no need to start at IndexOf,
                    i = Math.Min(_visuals.Count, _strokes.Count); //not -1, we're about to decrement 
                    while (--i >= 0)
                    {
                        if (object.ReferenceEquals(_strokes[i], visualStroke))
                        { 
                            break;
                        } 
                    } 
                }
                else 
                {
                    i = _strokes.IndexOf(visual.Stroke);
                }
                while (--i >= 0) 
                {
                    Stroke stroke = _strokes[i]; 
                    if ((stroke.DrawingAttributes.IsHighlighter == false) 
                        && (_visuals.TryGetValue(stroke, out precedingVisual) == true)
                        && (VisualTreeHelper.GetParent(precedingVisual) != null)) 
                    {
                        VisualCollection children = ((ContainerVisual)(VisualTreeHelper.GetParent(precedingVisual))).Children;
                        int index = children.IndexOf(precedingVisual);
                        children.Insert(index + 1, visual); 
                        break;
                    } 
                } 
                // If found no non-highlighter strokes with a lower z-order, insert
                // the stroke at the very bottom of the regular ink visual tree. 
                if (i < 0)
                {
                    ContainerVisual parent = GetContainerVisual(visual.Stroke.DrawingAttributes);
                    parent.Children.Insert(0, visual); 
                }
            } 
        } 

        ///  
        /// Detaches a visual from the tree, also removes highligher parents if empty
        /// when true is passed
        /// 
        private void DetachVisual(Visual visual) 
        {
            ContainerVisual parent = (ContainerVisual)(VisualTreeHelper.GetParent(visual)); 
            if (parent != null) 
            {
                VisualCollection children = parent.Children; 
                children.Remove(visual);

                // If the parent is a childless highlighter, detach it too.
                HighlighterContainerVisual hcVisual = parent as HighlighterContainerVisual; 
                if (hcVisual != null &&
                    hcVisual.Children.Count == 0 && 
                    _highlighters != null && 
                    _highlighters.ContainsValue(hcVisual))
                { 
                    DetachVisual(hcVisual);
                    _highlighters.Remove(hcVisual.Color);
                }
            } 
        }
 
        ///  
        /// Attaches event handlers to stroke events
        ///  
        private void StartListeningOnStrokeEvents(Stroke stroke)
        {
            System.Diagnostics.Debug.Assert(stroke != null);
            stroke.Invalidated += new EventHandler(OnStrokeInvalidated); 
        }
 
        ///  
        /// Detaches event handlers from stroke
        ///  
        private void StopListeningOnStrokeEvents(Stroke stroke)
        {
            System.Diagnostics.Debug.Assert(stroke != null);
            stroke.Invalidated -= new EventHandler(OnStrokeInvalidated); 
        }
 
        ///  
        /// Finds a container for a new visual based on the drawing attributes
        /// of the stroke rendered into that visual. 
        /// 
        /// drawing attributes
        /// visual
        private ContainerVisual GetContainerVisual(DrawingAttributes drawingAttributes) 
        {
            System.Diagnostics.Debug.Assert(drawingAttributes != null); 
 
            HighlighterContainerVisual hcVisual;
            if (drawingAttributes.IsHighlighter) 
            {
                // For a highlighter stroke, the color.A is neglected.
                Color color = StrokeRenderer.GetHighlighterColor(drawingAttributes.Color);
                if ((_highlighters == null) || (_highlighters.TryGetValue(color, out hcVisual) == false)) 
                {
                    if (_highlighters == null) 
                    { 
                        _highlighters = new Dictionary();
                    } 

                    hcVisual = new HighlighterContainerVisual(color);
                    hcVisual.Opacity = StrokeRenderer.HighlighterOpacity;
                    _highlightersRoot.Children.Add(hcVisual); 

                    _highlighters.Add(color, hcVisual); 
                } 
                else if (VisualTreeHelper.GetParent(hcVisual) == null)
                { 
                    _highlightersRoot.Children.Add(hcVisual);
                }
                return hcVisual;
            } 
            else
            { 
                return _regularInkVisuals; 
            }
        } 

        #endregion

        #region Fields 

        // The renderer's top level container visuals 
        private ContainerVisual _rootVisual; 
        private ContainerVisual _highlightersRoot;
        private ContainerVisual _incrementalRenderingVisuals; 
        private ContainerVisual _regularInkVisuals;

        // Stroke-to-visual map
        private Dictionary _visuals; 

        // Color-to-visual map for highlighter ink container visuals 
        private Dictionary _highlighters = null; 

        // Collection of strokes this Renderer renders 
        private StrokeCollection _strokes = null;

        // List of visuals attached via AttachIncrementalRendering
        private List _attachedVisuals = null; 

        // Whhen true, will render in high contrast mode 
        private bool _highContrast; 
        private Color _highContrastColor = Colors.White;
 
        #endregion
    }
}
 


// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
                        

Link Menu

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