//------------------------------------------------------------------------------
//
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//
//-----------------------------------------------------------------------------
using MS.Internal;
using MS.Internal.KnownBoxes;
using MS.Internal.Media;
using MS.Internal.PresentationCore;
using MS.Utility;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Security;
using System.Security.Permissions;
using System.Windows.Automation.Peers;
using System.Windows.Input;
using System.Windows.Input.StylusPlugIns;
using System.Windows.Interop;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Composition;
using System.Windows.Media.Effects;
using System.Windows.Media.Media3D;
using System.Windows.Threading;
namespace System.Windows
{
///
/// Visibility - Enum which describes 3 possible visibility options.
///
///
public enum Visibility : byte
{
///
/// Normally visible.
///
Visible = 0,
///
/// Occupies space in the layout, but is not visible (completely transparent).
///
Hidden,
///
/// Not visible and does not occupy any space in layout, as if it doesn't exist.
///
Collapsed
}
///
/// UIElement is the base class for frameworks building on the Windows Presentation Core.
///
///
/// UIElement adds to the base visual class "LIFE" - Layout, Input, Focus, and Eventing.
/// UIElement can be considered roughly equivalent to an HWND in Win32, or an Element in Trident.
/// UIElements can render (because they derive from Visual), visually size and position their children,
/// respond to user input (including control of where input is getting sent to),
/// and raise events that traverse the physical tree.
///
/// UIElement is the most functional type in the Windows Presentation Core.
///
[UidProperty("Uid")]
public partial class UIElement : Visual, IInputElement, IAnimatable
{
///
/// Critical: This code is used to register various thunks that are used to send input to the tree
/// TreatAsSafe: This code attaches handlers that are inside the class and private. Not configurable or overridable
///
[SecurityCritical,SecurityTreatAsSafe]
static UIElement()
{
UIElement.RegisterEvents(typeof(UIElement));
RenderOptions.EdgeModeProperty.OverrideMetadata(
typeof(UIElement),
new UIPropertyMetadata(new PropertyChangedCallback(EdgeMode_Changed)));
RenderOptions.BitmapScalingModeProperty.OverrideMetadata(
typeof(UIElement),
new UIPropertyMetadata(new PropertyChangedCallback(BitmapScalingMode_Changed)));
}
///
/// Constructor. This form of constructor will encounter a slight perf hit since it needs to initialize Dispatcher for the instance.
///
public UIElement()
{
Initialize();
}
private void Initialize()
{
BeginPropertyInitialization();
NeverMeasured = true;
NeverArranged = true;
SnapsToDevicePixelsCache = (bool) SnapsToDevicePixelsProperty.GetDefaultValue(DependencyObjectType);
ClipToBoundsCache = (bool) ClipToBoundsProperty.GetDefaultValue(DependencyObjectType);
VisibilityCache = (Visibility) VisibilityProperty.GetDefaultValue(DependencyObjectType);
SetFlags(true, VisualFlags.IsUIElement);
// Note: IsVisibleCache is false by default.
}
#region AllowDrop
///
/// The DependencyProperty for the AllowDrop property.
///
public static readonly DependencyProperty AllowDropProperty =
DependencyProperty.Register(
"AllowDrop",
typeof(bool),
typeof(UIElement),
new PropertyMetadata(BooleanBoxes.FalseBox));
///
/// A dependency property that allows the drop object as DragDrop target.
///
public bool AllowDrop
{
get { return (bool) GetValue(AllowDropProperty); }
set { SetValue(AllowDropProperty, BooleanBoxes.Box(value)); }
}
#endregion AllowDrop
///
/// Get the StylusPlugInCollection associated with the UIElement
///
protected StylusPlugInCollection StylusPlugIns
{
get
{
StylusPlugInCollection stylusCollection = StylusPlugInsField.GetValue(this);
if (stylusCollection == null)
{
stylusCollection = new StylusPlugInCollection(this);
StylusPlugInsField.SetValue(this, stylusCollection);
}
return stylusCollection;
}
}
///
/// Returns the size the element computed during the Measure pass.
/// This is only valid if IsMeasureValid is true.
///
public Size DesiredSize
{
get
{
if(this.Visibility == Visibility.Collapsed)
return new Size(0,0);
else
return _desiredSize;
}
}
///
/// MeasureData is set on this UIElement prior to calling Measure. Elements that understand MeasureData can
/// use it to retrieve viewport information. This should be a parameter to Measure() but it's not possible
/// to do this (even internally) because of the existing MeasureCore and MeasureOverride virtuals (the idea
/// would be to always call the 'new' one if possible and the old one if not... but there's no reasonable way
/// to tell what a class overrides).
///
internal MeasureData MeasureData
{
get { return MeasureDataField.GetValue(this); }
set
{
if (value == null)
{
MeasureDataField.ClearValue(this);
}
else
{
MeasureDataField.SetValue(this, value);
}
}
}
internal Size PreviousConstraint
{
get
{
Debug.Assert(PreviousMeasureData == null || _previousAvailableSize == PreviousMeasureData.AvailableSize);
return _previousAvailableSize;
}
}
internal MeasureData PreviousMeasureData
{
get { return PreviousMeasureDataField.GetValue(this); }
private set
{
if (value == null)
{
PreviousMeasureDataField.ClearValue(this);
}
else
{
PreviousMeasureDataField.SetValue(this, value);
}
Debug.Assert(_previousAvailableSize == value.AvailableSize);
}
}
// This is needed to prevent dirty elements from drawing and crashing while doing so.
private bool IsRenderable()
{
//elements that were created but never invalidated/measured are clean
//from layout perspective, but we still don't want to render them
//because they don't have state build up enough for that.
if(NeverMeasured || NeverArranged)
return false;
//if element is collapsed, no rendering is needed
//it is not only perf optimization, but also protection from
//UIElement to break itself since RenderSize is reported as (0,0)
//when UIElement is Collapsed
if(ReadFlag(CoreFlags.IsCollapsed))
return false;
return IsMeasureValid && IsArrangeValid;
}
internal void InvalidateMeasureInternal()
{
MeasureDirty = true;
}
internal void InvalidateArrangeInternal()
{
ArrangeDirty = true;
}
///
/// Determines if the DesiredSize is valid.
///
///
/// A developer can force arrangement to be invalidated by calling InvalidateMeasure.
/// IsArrangeValid and IsMeasureValid are related,
/// in that arrangement cannot be valid without measurement first being valid.
///
public bool IsMeasureValid
{
get { return !MeasureDirty; }
}
///
/// Determines if the RenderSize and position of child elements is valid.
///
///
/// A developer can force arrangement to be invalidated by calling InvalidateArrange.
/// IsArrangeValid and IsMeasureValid are related, in that arrangement cannot be valid without measurement first
/// being valid.
///
public bool IsArrangeValid
{
get { return !ArrangeDirty; }
}
///
/// Invalidates the measurement state for the element.
/// This has the effect of also invalidating the arrange state for the element.
/// The element will be queued for an update layout that will occur asynchronously.
///
public void InvalidateMeasure()
{
if( !MeasureDirty
&& !MeasureInProgress )
{
Debug.Assert(MeasureRequest == null, "can't be clean and still have MeasureRequest");
// VerifyAccess();
if(!NeverMeasured) //only measured once elements are allowed in *update* queue
{
ContextLayoutManager ContextLayoutManager = ContextLayoutManager.From(Dispatcher);
ContextLayoutManager.MeasureQueue.Add(this);
}
MeasureDirty = true;
}
}
///
/// Invalidates the arrange state for the element.
/// The element will be queued for an update layout that will occur asynchronously.
/// MeasureCore will not be called unless InvalidateMeasure is also called - or that something
/// else caused the measure state to be invalidated.
///
public void InvalidateArrange()
{
if( !ArrangeDirty
&& !ArrangeInProgress)
{
Debug.Assert(ArrangeRequest == null, "can't be clean and still have MeasureRequest");
// VerifyAccess();
if(!NeverArranged)
{
ContextLayoutManager ContextLayoutManager = ContextLayoutManager.From(Dispatcher);
ContextLayoutManager.ArrangeQueue.Add(this);
}
ArrangeDirty = true;
}
}
///
/// Invalidates the rendering of the element.
/// Causes to be called at a later time.
///
public void InvalidateVisual()
{
InvalidateArrange();
RenderingInvalidated = true;
}
///
/// Notification that is called by Measure of a child when
/// it ends up with different desired size for the child.
///
///
/// Default implementation simply calls invalidateMeasure(), assuming that layout of a
/// parent should be updated after child changed its size.
/// Finer point: this method can only be called in the scenario when the system calls Measure on a child,
/// not when parent calls it since if parent calls it, it means parent has dirty layout and is recalculating already.
///
protected virtual void OnChildDesiredSizeChanged(UIElement child)
{
if(IsMeasureValid)
{
InvalidateMeasure();
}
}
///
/// This event fires every time Layout updates the layout of the trees associated with current Dispatcher.
/// Layout update can happen as a result of some propety change, window resize or explicit user request.
///
public event EventHandler LayoutUpdated
{
add
{
LayoutEventList.ListItem item = getLayoutUpdatedHandler(value);
if(item == null)
{
//set a weak ref in LM
item = ContextLayoutManager.From(Dispatcher).LayoutEvents.Add(value);
addLayoutUpdatedHandler(value, item);
}
}
remove
{
LayoutEventList.ListItem item = getLayoutUpdatedHandler(value);
if(item != null)
{
removeLayoutUpdatedHandler(value);
//remove a weak ref from LM
ContextLayoutManager.From(Dispatcher).LayoutEvents.Remove(item);
}
}
}
private void addLayoutUpdatedHandler(EventHandler handler, LayoutEventList.ListItem item)
{
object cachedLayoutUpdatedItems = LayoutUpdatedListItemsField.GetValue(this);
if(cachedLayoutUpdatedItems == null)
{
LayoutUpdatedListItemsField.SetValue(this, item);
LayoutUpdatedHandlersField.SetValue(this, handler);
}
else
{
EventHandler cachedLayoutUpdatedHandler = LayoutUpdatedHandlersField.GetValue(this);
if(cachedLayoutUpdatedHandler != null)
{
//second unique handler is coming in.
//allocate a datastructure
Hashtable list = new Hashtable(2);
//add previously cached handler
list.Add(cachedLayoutUpdatedHandler, cachedLayoutUpdatedItems);
//add new handler
list.Add(handler, item);
LayoutUpdatedHandlersField.ClearValue(this);
LayoutUpdatedListItemsField.SetValue(this,list);
}
else //already have a list
{
Hashtable list = (Hashtable)cachedLayoutUpdatedItems;
list.Add(handler, item);
}
}
}
private LayoutEventList.ListItem getLayoutUpdatedHandler(EventHandler d)
{
object cachedLayoutUpdatedItems = LayoutUpdatedListItemsField.GetValue(this);
if(cachedLayoutUpdatedItems == null)
{
return null;
}
else
{
EventHandler cachedLayoutUpdatedHandler = LayoutUpdatedHandlersField.GetValue(this);
if(cachedLayoutUpdatedHandler != null)
{
if(cachedLayoutUpdatedHandler == d) return (LayoutEventList.ListItem)cachedLayoutUpdatedItems;
}
else //already have a list
{
Hashtable list = (Hashtable)cachedLayoutUpdatedItems;
LayoutEventList.ListItem item = (LayoutEventList.ListItem)(list[d]);
return item;
}
return null;
}
}
private void removeLayoutUpdatedHandler(EventHandler d)
{
object cachedLayoutUpdatedItems = LayoutUpdatedListItemsField.GetValue(this);
EventHandler cachedLayoutUpdatedHandler = LayoutUpdatedHandlersField.GetValue(this);
if(cachedLayoutUpdatedHandler != null) //single handler
{
if(cachedLayoutUpdatedHandler == d)
{
LayoutUpdatedListItemsField.ClearValue(this);
LayoutUpdatedHandlersField.ClearValue(this);
}
}
else //there is an ArrayList allocated
{
Hashtable list = (Hashtable)cachedLayoutUpdatedItems;
list.Remove(d);
}
}
///
/// Recursively propagates IsLayoutSuspended flag down to the whole v's sub tree.
///
internal static void PropagateSuspendLayout(Visual v)
{
if(v.CheckFlagsAnd(VisualFlags.IsLayoutIslandRoot)) return;
//the subtree is already suspended - happens when already suspended tree is further disassembled
//no need to walk down in this case
if(v.CheckFlagsAnd(VisualFlags.IsLayoutSuspended)) return;
// (bug # 1623922) assert that a UIElement has not being
// removed from the visual tree while updating layout.
if ( Invariant.Strict
&& v.CheckFlagsAnd(VisualFlags.IsUIElement) )
{
UIElement e = (UIElement)v;
Invariant.Assert(!e.MeasureInProgress && !e.ArrangeInProgress);
}
v.SetFlags(true, VisualFlags.IsLayoutSuspended);
v.TreeLevel = 0;
int count = v.InternalVisualChildrenCount;
for (int i = 0; i < count; i++)
{
Visual cv = v.InternalGetVisualChild(i);
if (cv != null)
{
PropagateSuspendLayout(cv);
}
}
}
///
/// Recursively resets IsLayoutSuspended flag on all visuals of the whole v's sub tree.
/// For UIElements also re-inserts the UIElement into Measure and / or Arrange update queues
/// if necessary.
///
internal static void PropagateResumeLayout(Visual parent, Visual v)
{
if(v.CheckFlagsAnd(VisualFlags.IsLayoutIslandRoot)) return;
//the subtree is already active - happens when new elements are added to the active tree
//elements are created layout-active so they don't need to be specifically unsuspended
//no need to walk down in this case
//if(!v.CheckFlagsAnd(VisualFlags.IsLayoutSuspended)) return;
//that can be true only on top of recursion, if suspended v is being connected to suspended parent.
bool parentIsSuspended = parent == null ? false : parent.CheckFlagsAnd(VisualFlags.IsLayoutSuspended);
uint parentTreeLevel = parent == null ? 0 : parent.TreeLevel;
if(parentIsSuspended) return;
v.SetFlags(false, VisualFlags.IsLayoutSuspended);
v.TreeLevel = parentTreeLevel + 1;
if (v.CheckFlagsAnd(VisualFlags.IsUIElement))
{
// re-insert UIElement into the update queues
UIElement e = (UIElement)v;
Invariant.Assert(!e.MeasureInProgress && !e.ArrangeInProgress);
bool requireMeasureUpdate = e.MeasureDirty && !e.NeverMeasured && (e.MeasureRequest == null);
bool requireArrangeUpdate = e.ArrangeDirty && !e.NeverArranged && (e.ArrangeRequest == null);
ContextLayoutManager contextLayoutManager = (requireMeasureUpdate || requireArrangeUpdate)
? ContextLayoutManager.From(e.Dispatcher)
: null;
if (requireMeasureUpdate)
{
contextLayoutManager.MeasureQueue.Add(e);
}
if (requireArrangeUpdate)
{
contextLayoutManager.ArrangeQueue.Add(e);
}
}
int count = v.InternalVisualChildrenCount;
for (int i = 0; i < count; i++)
{
Visual cv = v.InternalGetVisualChild(i);
if (cv != null)
{
PropagateResumeLayout(v, cv);
}
}
}
///
/// Updates DesiredSize of the UIElement. Must be called by parents from theor MeasureCore, to form recursive update.
/// This is first pass of layout update.
///
///
/// Measure is called by parents on their children. Internally, Measure calls MeasureCore override on the same object,
/// giving it opportunity to compute its DesiredSize.
/// This method will return immediately if child is not Dirty, previously measured
/// and availableSize is the same as cached.
/// This method also resets the IsMeasureinvalid bit on the child.
/// In case when "unbounded measure to content" is needed, parent can use availableSize
/// as double.PositiveInfinity. Any returned size is OK in this case.
///
/// Available size that parent can give to the child. May be infinity (when parent wants to
/// measure to content). This is soft constraint. Child can return bigger size to indicate that it wants bigger space and hope
/// that parent can throw in scrolling...
public void Measure(Size availableSize)
{
Debug.Assert(MeasureData == null || availableSize == MeasureData.AvailableSize, "MeasureData needs to be passed down in [....] with size");
bool etwTracingEnabled = false;
long perfElementID = 0;
MeasureData measureData = MeasureData;
if (EventTrace.IsEnabled(EventTrace.Flags.PerToolSupport, EventTrace.Level.verbose))
{
perfElementID = PerfService.GetPerfElementID(this);
etwTracingEnabled = true;
EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.MEASUREGUID), EventType.StartEvent, perfElementID);
}
// VerifyAccess();
// Disable reentrancy during the measure pass. This is because much work is done
// during measure - such as inflating templates, formatting PTS stuff, creating
// fonts, etc. Generally speaking, we cannot survive reentrancy in these code
// paths.
using(Dispatcher.DisableProcessing())
{
//enforce that Measure can not receive NaN size .
if(DoubleUtil.IsNaN(availableSize.Width) || DoubleUtil.IsNaN(availableSize.Height))
throw new InvalidOperationException(SR.Get(SRID.UIElement_Layout_NaNMeasure));
bool neverMeasured = NeverMeasured;
if(neverMeasured)
{
switchVisibilityIfNeeded(this.Visibility);
//to make sure effects are set correctly - otherwise it's not used
//simply because it is never pulled by anybody
pushVisualEffects();
}
bool isCloseToPreviousMeasure = measureData != null ? measureData.IsCloseTo(PreviousMeasureData) :
DoubleUtil.AreClose(availableSize, _previousAvailableSize);
//if Collapsed, we should not Measure, keep dirty bit but remove request
if ( this.Visibility == Visibility.Collapsed
|| ((Visual)this).CheckFlagsAnd(VisualFlags.IsLayoutSuspended) )
{
//reset measure request.
if(MeasureRequest != null)
ContextLayoutManager.From(Dispatcher).MeasureQueue.Remove(this);
// remember though that parent tried to measure at this size
// in case when later this element is called to measure incrementally
// it has up-to-date information stored in _previousAvailableSize
if(!isCloseToPreviousMeasure)
{
//this will ensure that element will be actually re-measured at the new available size
//later when it becomes visible.
InvalidateMeasureInternal();
_previousAvailableSize = availableSize;
if (measureData != null)
{
PreviousMeasureData = measureData;
}
}
return;
}
//your basic bypass. No reason to calc the same thing.
if( IsMeasureValid //element is clean
&& !neverMeasured //previously measured
&& isCloseToPreviousMeasure) //and contraint matches
{
return;
}
NeverMeasured = false;
Size prevSize = _desiredSize;
//we always want to be arranged, ensure arrange request
//doing it before OnMeasure prevents unneeded requests from children in the queue
InvalidateArrange();
//_measureInProgress prevents OnChildDesiredSizeChange to cause the elements be put
//into the queue.
MeasureInProgress = true;
Size desiredSize = new Size(0,0);
ContextLayoutManager layoutManager = ContextLayoutManager.From(Dispatcher);
bool gotException = true;
try
{
layoutManager.EnterMeasure();
desiredSize = MeasureCore(availableSize);
Debug.Assert(MeasureData == null || MeasureData.AvailableSize == availableSize,
"MeasureData must behave the same as a local variable and kept in [....] with availableSize");
gotException = false;
}
finally
{
// reset measure in progress
MeasureInProgress = false;
_previousAvailableSize = availableSize;
if (measureData != null)
{
PreviousMeasureData = measureData;
}
layoutManager.ExitMeasure();
if(gotException)
layoutManager.SetLastExceptionElement(this);
}
//enforce that MeasureCore can not return PositiveInfinity size even if given Infinte availabel size.
//Note: NegativeInfinity can not be returned by definition of Size structure.
if(double.IsPositiveInfinity(desiredSize.Width) || double.IsPositiveInfinity(desiredSize.Height))
throw new InvalidOperationException(SR.Get(SRID.UIElement_Layout_PositiveInfinityReturned, this.GetType().FullName));
//enforce that MeasureCore can not return NaN size .
if(DoubleUtil.IsNaN(desiredSize.Width) || DoubleUtil.IsNaN(desiredSize.Height))
throw new InvalidOperationException(SR.Get(SRID.UIElement_Layout_NaNReturned, this.GetType().FullName));
//reset measure dirtiness
MeasureDirty = false;
//reset measure request.
if(MeasureRequest != null)
ContextLayoutManager.From(Dispatcher).MeasureQueue.Remove(this);
//cache desired size
_desiredSize = desiredSize;
//notify parent if our desired size changed (watefall effect)
if( !MeasureDuringArrange
&& !DoubleUtil.AreClose(prevSize, desiredSize))
{
UIElement p;
IContentHost ich;
GetUIParentOrICH(out p, out ich); //only one will be returned
if(p != null && !p.MeasureInProgress) //this is what differs this code from signalDesiredSizeChange()
p.OnChildDesiredSizeChanged(this);
else if(ich != null)
ich.OnChildDesiredSizeChanged(this);
}
}
if (etwTracingEnabled == true)
{
EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.MEASUREGUID), EventType.EndEvent, perfElementID);
}
}
//only one will be returned, whichever found first
internal void GetUIParentOrICH(out UIElement uiParent, out IContentHost ich)
{
ich = null;
uiParent = null;
for(Visual v = VisualTreeHelper.GetParent(this) as Visual; v != null; v = VisualTreeHelper.GetParent(v) as Visual)
{
ich = v as IContentHost;
if (ich != null) break;
if(v.CheckFlagsAnd(VisualFlags.IsUIElement))
{
uiParent = (UIElement)v;
break;
}
}
}
//walks visual tree up to find UIElement parent within Element Layout Island, so stops the walk if the island's root is found
internal UIElement GetUIParentWithinLayoutIsland()
{
UIElement uiParent = null;
for(Visual v = VisualTreeHelper.GetParent(this) as Visual; v != null; v = VisualTreeHelper.GetParent(v) as Visual)
{
if (v.CheckFlagsAnd(VisualFlags.IsLayoutIslandRoot))
{
break;
}
if(v.CheckFlagsAnd(VisualFlags.IsUIElement))
{
uiParent = (UIElement)v;
break;
}
}
return uiParent;
}
///
/// Parents or system call this method to arrange the internals of children on a second pass of layout update.
///
///
/// This method internally calls ArrangeCore override, giving the derived class opportunity
/// to arrange its children and/or content using final computed size.
/// In their ArrangeCore overrides, derived class is supposed to create its visual structure and
/// prepare itself for rendering. Arrange is called by parents
/// from their implementation of ArrangeCore or by system when needed.
/// This method sets Bounds=finalSize before calling ArrangeCore.
///
/// This is the final size and location that parent or system wants this UIElement to assume.
public void Arrange(Rect finalRect)
{
bool etwTracingEnabled = false;
long perfElementID = 0;
MeasureData previousMeasureData = PreviousMeasureData;
if (EventTrace.IsEnabled(EventTrace.Flags.PerToolSupport, EventTrace.Level.verbose))
{
perfElementID = PerfService.GetPerfElementID(this);
etwTracingEnabled = true;
EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.ARRANGEGUID), EventType.StartEvent, perfElementID);
}
// VerifyAccess();
// Disable reentrancy during the arrange pass. This is because much work is done
// during arrange - such as formatting PTS stuff, creating
// fonts, etc. Generally speaking, we cannot survive reentrancy in these code
// paths.
using(Dispatcher.DisableProcessing())
{
//enforce that Arrange can not come with Infinity size or NaN
if( double.IsPositiveInfinity(finalRect.Width)
|| double.IsPositiveInfinity(finalRect.Height)
|| DoubleUtil.IsNaN(finalRect.Width)
|| DoubleUtil.IsNaN(finalRect.Height)
)
{
DependencyObject parent = GetUIParent() as UIElement;
throw new InvalidOperationException(
SR.Get(
SRID.UIElement_Layout_InfinityArrange,
(parent == null ? "" : parent.GetType().FullName),
this.GetType().FullName));
}
//if Collapsed, we should not Arrange, keep dirty bit but remove request
if ( this.Visibility == Visibility.Collapsed
|| ((Visual)this).CheckFlagsAnd(VisualFlags.IsLayoutSuspended) )
{
//reset arrange request.
if(ArrangeRequest != null)
ContextLayoutManager.From(Dispatcher).ArrangeQueue.Remove(this);
// remember though that parent tried to arrange at this rect
// in case when later this element is called to arrange incrementally
// it has up-to-date information stored in _finalRect
_finalRect = finalRect;
return;
}
//in case parent did not call Measure on a child, we call it now.
//parent can skip calling Measure on a child if it does not care about child's size
//passing finalSize practically means "set size" because that's what Measure(sz)/Arrange(same_sz) means
//Note that in case of IsLayoutSuspended (temporarily out of the tree) the MeasureDirty can be true
//while it does not indicate that we should re-measure - we just came of Measure that did nothing
//because of suspension
if( MeasureDirty
|| NeverMeasured)
{
try
{
MeasureDuringArrange = true;
//If never measured - that means "set size", arrange-only scenario
//Otherwise - the parent previosuly measured the element at constriant
//and the fact that we are arranging the measure-dirty element now means
//we are not in the UpdateLayout loop but rather in manual sequence of Measure/Arrange
//(like in HwndSource when new RootVisual is attached) so there are no loops and there could be
//measure-dirty elements left after previosu single Measure pass) - so need to use cached constraint
if (NeverMeasured)
Measure(finalRect.Size);
else
{
if (previousMeasureData != null)
{
MeasureData = previousMeasureData; // MeasureData needs to be set as if it were a param to Measure.
}
Measure(PreviousConstraint);
}
}
finally
{
MeasureDuringArrange = false;
}
}
//bypass - if clean and rect is the same, no need to re-arrange
if( !IsArrangeValid
|| NeverArranged
|| !DoubleUtil.AreClose(finalRect, _finalRect))
{
bool firstArrange = NeverArranged;
NeverArranged = false;
ArrangeInProgress = true;
ContextLayoutManager layoutManager = ContextLayoutManager.From(Dispatcher);
Size oldSize = RenderSize;
bool sizeChanged = false;
bool gotException = true;
try
{
layoutManager.EnterArrange();
//This has to update RenderSize
ArrangeCore(finalRect);
//to make sure Clip is tranferred to Visual
ensureClip(finalRect.Size);
// see if we need to call OnRenderSizeChanged on this element
sizeChanged = markForSizeChangedIfNeeded(oldSize, RenderSize);
gotException = false;
}
finally
{
ArrangeInProgress = false;
layoutManager.ExitArrange();
if(gotException)
layoutManager.SetLastExceptionElement(this);
}
_finalRect = finalRect;
ArrangeDirty = false;
//reset request.
if(ArrangeRequest != null)
ContextLayoutManager.From(Dispatcher).ArrangeQueue.Remove(this);
if((sizeChanged || RenderingInvalidated || firstArrange) && IsRenderable())
{
DrawingContext dc = RenderOpen();
try
{
if (etwTracingEnabled == true)
{
EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.ONRENDERGUID), EventType.StartEvent, perfElementID);
}
OnRender(dc);
if (etwTracingEnabled == true)
{
EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.ONRENDERGUID), EventType.EndEvent, perfElementID);
}
}
finally
{
dc.Close();
RenderingInvalidated = false;
}
updatePixelSnappingGuidelines();
}
if (firstArrange)
{
EndPropertyInitialization();
}
}
}
if (etwTracingEnabled == true)
{
EventTrace.EventProvider.TraceEvent(EventTrace.GuidFromId(EventTraceGuidId.ARRANGEGUID), EventType.EndEvent, perfElementID);
}
}
///
/// OnRender is called by the base class when the rendering instructions of the UIElement are required.
/// Note: the drawing instructions sent to DrawingContext are not rendered immediately on the screen
/// but rather stored and later passed to the rendering engine at proper time.
/// Derived classes override this method to draw the content of the UIElement.
///
protected virtual void OnRender(DrawingContext drawingContext)
{
}
private void updatePixelSnappingGuidelines()
{
if((!SnapsToDevicePixels) || (_drawingContent == null))
{
this.VisualXSnappingGuidelines = this.VisualYSnappingGuidelines = null;
}
else
{
DoubleCollection xLines = this.VisualXSnappingGuidelines;
if(xLines == null)
{
xLines = new DoubleCollection();
xLines.Add(0d);
xLines.Add(this.RenderSize.Width);
this.VisualXSnappingGuidelines = xLines;
}
else
{
// xLines[0] = 0d; - this already should be so
// check to avoid potential dirtiness in renderer
int lastGuideline = xLines.Count - 1;
if(!DoubleUtil.AreClose(xLines[lastGuideline], this.RenderSize.Width))
xLines[lastGuideline] = this.RenderSize.Width;
}
DoubleCollection yLines = this.VisualYSnappingGuidelines;
if(yLines == null)
{
yLines = new DoubleCollection();
yLines.Add(0d);
yLines.Add(this.RenderSize.Height);
this.VisualYSnappingGuidelines = yLines;
}
else
{
// yLines[0] = 0d; - this already should be so
// check to avoid potential dirtiness in renderer
int lastGuideline = yLines.Count - 1;
if(!DoubleUtil.AreClose(yLines[lastGuideline], this.RenderSize.Height))
yLines[lastGuideline] = this.RenderSize.Height;
}
}
}
private bool markForSizeChangedIfNeeded(Size oldSize, Size newSize)
{
//already marked for SizeChanged, simply update the newSize
bool widthChanged = !DoubleUtil.AreClose(oldSize.Width, newSize.Width);
bool heightChanged = !DoubleUtil.AreClose(oldSize.Height, newSize.Height);
SizeChangedInfo info = sizeChangedInfo;
if(info != null)
{
info.Update(widthChanged, heightChanged);
return true;
}
else if(widthChanged || heightChanged)
{
info = new SizeChangedInfo(this, oldSize, widthChanged, heightChanged);
sizeChangedInfo = info;
ContextLayoutManager.From(Dispatcher).AddToSizeChangedChain(info);
//
// This notifies Visual layer that hittest boundary potentially changed
//
PropagateFlags(
this,
VisualFlags.IsSubtreeDirtyForPrecompute | VisualFlags.NodeNeedsBitmapEffectUpdate,
VisualProxyFlags.IsSubtreeDirtyForRender);
return true;
}
//this result is used to determine if we need to call OnRender after Arrange
//OnRender is called for 2 reasons - someone called InvalidateVisual - then OnRender is called
//on next Arrange, or the size changed.
return false;
}
///
/// This is invoked after layout update before rendering if the element's RenderSize
/// has changed as a result of layout update.
///
/// Packaged parameters (, includes
/// old and new sizes and which dimension actually changes.
protected internal virtual void OnRenderSizeChanged(SizeChangedInfo info)
{}
///
/// Measurement override. Implement your size-to-content logic here.
///
///
/// MeasureCore is designed to be the main customizability point for size control of layout.
/// Element authors should override this method, call Measure on each child element,
/// and compute their desired size based upon the measurement of the children.
/// The return value should be the desired size.
/// Note: It is required that a parent element calls Measure on each child or they won't be sized/arranged.
/// Typical override follows a pattern roughly like this (pseudo-code):
///
///
///
///
///
/// The key aspects of this snippet are:
///
/// You must call Measure on each child element
/// It is common to cache measurement information between the MeasureCore and ArrangeCore method calls
/// Calling base.MeasureCore is not required.
/// Calls to Measure on children are passing either the same availableSize as the parent, or a subset of the area depending
/// on the type of layout the parent will perform (for example, it would be valid to remove the area
/// for some border or padding).
///
///
/// Available size that parent can give to the child. May be infinity (when parent wants to
/// measure to content). This is soft constraint. Child can return bigger size to indicate that it wants bigger space and hope
/// that parent can throw in scrolling...
/// Desired Size of the control, given available size passed as parameter.
protected virtual Size MeasureCore(Size availableSize)
{
//can not return availableSize here - this is too "greedy" and can cause the Infinity to be
//returned. So the next "reasonable" choice is (0,0).
return new Size(0,0);
}
///
/// ArrangeCore allows for the customization of the final sizing and positioning of children.
///
///
/// Element authors should override this method, call Arrange on each visible child element,
/// to size and position each child element by passing a rectangle reserved for the child within parent space.
/// Note: It is required that a parent element calls Arrange on each child or they won't be rendered.
/// Typical override follows a pattern roughly like this (pseudo-code):
///
///
///
///
///
///
/// The final area within the parent that element should use to arrange itself and its children.
protected virtual void ArrangeCore(Rect finalRect)
{
// Set the element size.
RenderSize = finalRect.Size;
//Set transform to reflect the offset of finalRect - parents that have multiple children
//pass offset in the finalRect to communicate the location of this child withing the parent.
Transform renderTransform = RenderTransform;
if(renderTransform == Transform.Identity)
renderTransform = null;
Vector oldOffset = VisualOffset;
if (!DoubleUtil.AreClose(oldOffset.X, finalRect.X) ||
!DoubleUtil.AreClose(oldOffset.Y, finalRect.Y))
{
VisualOffset = new Vector(finalRect.X, finalRect.Y);
}
if (renderTransform != null)
{
//render transform + layout offset, create a collection
TransformGroup t = new TransformGroup();
Point origin = RenderTransformOrigin;
bool hasOrigin = (origin.X != 0d || origin.Y != 0d);
if (hasOrigin)
t.Children.Add(new TranslateTransform(-(finalRect.Width * origin.X), -(finalRect.Height * origin.Y)));
t.Children.Add(renderTransform);
if (hasOrigin)
t.Children.Add(new TranslateTransform(finalRect.Width * origin.X,
finalRect.Height * origin.Y));
VisualTransform = t;
}
else
{
VisualTransform = null;
}
}
///
/// This is a public read-only property that returns size of the UIElement.
/// This size is typcally used to find out where ink of the element will go.
///
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Size RenderSize
{
get
{
if (this.Visibility == Visibility.Collapsed)
return new Size();
else
return _size;
}
set
{
_size = value;
InvalidateHitTestBounds();
}
}
///
/// This method returns the hit-test bounds of a UIElement to the underlying Visual layer.
///
internal override Rect GetHitTestBounds()
{
Rect hitBounds = new Rect(_size);
if (_drawingContent != null)
{
MediaContext mediaContext = MediaContext.From(Dispatcher);
BoundsDrawingContextWalker ctx = mediaContext.AcquireBoundsDrawingContextWalker();
Rect resultRect = _drawingContent.GetContentBounds(ctx);
mediaContext.ReleaseBoundsDrawingContextWalker(ctx);
hitBounds.Union(resultRect);
}
return hitBounds;
}
///
/// The RenderTransform dependency property.
///
///
[CommonDependencyProperty]
public static readonly DependencyProperty RenderTransformProperty =
DependencyProperty.Register(
"RenderTransform",
typeof(Transform),
typeof(UIElement),
new PropertyMetadata(
Transform.Identity,
new PropertyChangedCallback(RenderTransform_Changed)));
///
/// The RenderTransform property defines the transform that will be applied to UIElement during rendering of its content.
/// This transform does not affect layout of the panel into which the UIElement is nested - the layout does not take this
/// transform into account to determine the location and RenderSize of the UIElement.
///
public Transform RenderTransform
{
get { return (Transform) GetValue(RenderTransformProperty); }
set { SetValue(RenderTransformProperty, value); }
}
private static void RenderTransform_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UIElement uie = (UIElement)d;
//if never measured, then nothing to do, it should be measured at some point
if(!uie.NeverMeasured && !uie.NeverArranged)
{
// If the change is simply a subproperty change, there is no
// need to Arrange. (which combines RenderTransform with all the
// other transforms.)
if (!e.IsASubPropertyChange)
{
uie.InvalidateArrange();
uie.AreTransformsClean = false;
}
}
}
///
/// The RenderTransformOrigin dependency property.
///
///
public static readonly DependencyProperty RenderTransformOriginProperty =
DependencyProperty.Register(
"RenderTransformOrigin",
typeof(Point),
typeof(UIElement),
new PropertyMetadata(
new Point(0d,0d),
new PropertyChangedCallback(RenderTransformOrigin_Changed)),
new ValidateValueCallback(IsRenderTransformOriginValid));
private static bool IsRenderTransformOriginValid(object value)
{
Point v = (Point)value;
return ( (!DoubleUtil.IsNaN(v.X) && !Double.IsPositiveInfinity(v.X) && !Double.IsNegativeInfinity(v.X))
&& (!DoubleUtil.IsNaN(v.Y) && !Double.IsPositiveInfinity(v.Y) && !Double.IsNegativeInfinity(v.Y)));
}
///
/// The RenderTransformOrigin property defines the center of the RenderTransform relative to
/// bounds of the element. It is a Point and both X and Y components are between 0 and 1.0 - the
/// (0,0) is the default value and specified top-left corner of the element, (0.5, 0.5) is the center of element
/// and (1,1) is the bottom-right corner of element.
///
public Point RenderTransformOrigin
{
get { return (Point)GetValue(RenderTransformOriginProperty); }
set { SetValue(RenderTransformOriginProperty, value); }
}
private static void RenderTransformOrigin_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UIElement uie = (UIElement)d;
//if never measured, then nothing to do, it should be measured at some point
if(!uie.NeverMeasured && !uie.NeverArranged)
{
uie.InvalidateArrange();
uie.AreTransformsClean = false;
}
}
///
/// 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 override void OnVisualParentChanged(DependencyObject oldParent)
{
// Synchronize ForceInherit properties
if (_parent != null)
{
DependencyObject parent = _parent;
if (!InputElement.IsUIElement(parent) && !InputElement.IsUIElement3D(parent))
{
Visual parentAsVisual = parent as Visual;
if (parentAsVisual != null)
{
// We are being plugged into a non-UIElement visual. This
// means that our parent doesn't play by the same rules we
// do, so we need to track changes to our ancestors in
// order to bridge the gap.
parentAsVisual.VisualAncestorChanged += new AncestorChangedEventHandler(OnVisualAncestorChanged_ForceInherit);
// Try to find a UIElement ancestor to use for coersion.
parent = InputElement.GetContainingUIElement(parentAsVisual);
}
else
{
Visual3D parentAsVisual3D = parent as Visual3D;
if (parentAsVisual3D != null)
{
// We are being plugged into a non-UIElement visual. This
// means that our parent doesn't play by the same rules we
// do, so we need to track changes to our ancestors in
// order to bridge the gap.
parentAsVisual3D.VisualAncestorChanged += new AncestorChangedEventHandler(OnVisualAncestorChanged_ForceInherit);
// Try to find a UIElement ancestor to use for coersion.
parent = InputElement.GetContainingUIElement(parentAsVisual3D);
}
}
}
if (parent != null)
{
SynchronizeForceInheritProperties(this, null, null, parent);
}
else
{
// We don't have a UIElement parent or ancestor, so no
// coersions are necessary at this time.
}
}
else
{
DependencyObject parent = oldParent;
if (!InputElement.IsUIElement(parent) && !InputElement.IsUIElement3D(parent))
{
// We are being unplugged from a non-UIElement visual. This
// means that our parent didn't play by the same rules we
// do, so we started track changes to our ancestors in
// order to bridge the gap. Now we can stop.
if (oldParent is Visual)
{
((Visual) oldParent).VisualAncestorChanged -= new AncestorChangedEventHandler(OnVisualAncestorChanged_ForceInherit);
}
else if (oldParent is Visual3D)
{
((Visual3D) oldParent).VisualAncestorChanged -= new AncestorChangedEventHandler(OnVisualAncestorChanged_ForceInherit);
}
// Try to find a UIElement ancestor to use for coersion.
parent = InputElement.GetContainingUIElement(oldParent);
}
if (parent != null)
{
SynchronizeForceInheritProperties(this, null, null, parent);
}
else
{
// We don't have a UIElement parent or ancestor, so no
// coersions are necessary at this time.
}
}
// Synchronize ReverseInheritProperty Flags
//
// NOTE: do this AFTER synchronizing force-inherited flags, since
// they often effect focusability and such.
this.SynchronizeReverseInheritPropertyFlags(oldParent, true);
}
private void OnVisualAncestorChanged_ForceInherit(object sender, AncestorChangedEventArgs e)
{
// NOTE:
//
// We are forced to listen to AncestorChanged events because
// a UIElement may have raw Visuals between it and its nearest
// UIElement parent. We only care about changes that happen
// to the visual tree BETWEEN this UIElement and its nearest
// UIElement parent. This is because we can rely on our
// nearest UIElement parent to notify us when its force-inherit
// properties change.
DependencyObject parent = null;
if(e.OldParent == null)
{
// We were plugged into something.
// Find our nearest UIElement parent.
parent = InputElement.GetContainingUIElement(_parent);
// See if this parent is a child of the ancestor who's parent changed.
// If so, we don't care about changes that happen above us.
if(parent != null && VisualTreeHelper.IsAncestorOf(e.Ancestor, parent))
{
parent = null;
}
}
else
{
// we were unplugged from something.
// Find our nearest UIElement parent.
parent = InputElement.GetContainingUIElement(_parent);
if(parent != null)
{
// If we found a UIElement parent in our subtree, the
// break in the visual tree must have been above it,
// so we don't need to respond.
parent = null;
}
else
{
// There was no UIElement parent in our subtree, so we
// may be detaching from some UIElement parent above
// the break point in the tree.
parent = InputElement.GetContainingUIElement(e.OldParent);
}
}
if(parent != null)
{
SynchronizeForceInheritProperties(this, null, null, parent);
}
}
internal void OnVisualAncestorChanged(object sender, AncestorChangedEventArgs e)
{
UIElement uie = sender as UIElement;
if(null != uie)
PresentationSource.OnVisualAncestorChanged(uie, e);
}
///
/// Helper, gives the UIParent under control of which
/// the OnMeasure or OnArrange are currently called.
/// This may be implemented as a tree walk up until
/// LayoutElement is found.
///
internal DependencyObject GetUIParent()
{
return UIElementHelper.GetUIParent(this, false);
}
internal DependencyObject GetUIParent(bool continuePastVisualTree)
{
return UIElementHelper.GetUIParent(this, continuePastVisualTree);
}
internal DependencyObject GetUIParentNo3DTraversal()
{
DependencyObject parent = null;
// Try to find a UIElement parent in the visual ancestry.
DependencyObject myParent = InternalVisualParent;
parent = InputElement.GetContainingUIElement(myParent, true);
return parent;
}
///
/// Called to get the UI parent of this element when there is
/// no visual parent.
///
///
/// Returns a non-null value when some framework implementation
/// of this method has a non-visual parent connection,
///
protected virtual internal DependencyObject GetUIParentCore()
{
return null;
}
///
/// Call this method to ensure that the whoel subtree of elements that includes this UIElement
/// is properly updated.
///
///
/// This ensures that UIElements with IsMeasureInvalid or IsArrangeInvalid will
/// get call to their MeasureCore and ArrangeCore, and all computed sizes will be validated.
/// This method does nothing if layout is clean but it does work if layout is not clean so avoid calling
/// it after each change in the element tree. It makes sense to either never call it (system will do this
/// in a deferred manner) or only call it if you absolutely need updated sizes and positions after you do all changes.
///
public void UpdateLayout()
{
// VerifyAccess();
ContextLayoutManager.From(Dispatcher).UpdateLayout();
}
internal static void BuildRouteHelper(DependencyObject e, EventRoute route, RoutedEventArgs args)
{
if (route == null)
{
throw new ArgumentNullException("route");
}
if (args == null)
{
throw new ArgumentNullException("args");
}
if (args.Source == null)
{
throw new ArgumentException(SR.Get(SRID.SourceNotSet));
}
if (args.RoutedEvent != route.RoutedEvent)
{
throw new ArgumentException(SR.Get(SRID.Mismatched_RoutedEvent));
}
// Route via visual tree
if (args.RoutedEvent.RoutingStrategy == RoutingStrategy.Direct)
{
UIElement uiElement = e as UIElement;
ContentElement contentElement = null;
UIElement3D uiElement3D = null;
if (uiElement == null)
{
contentElement = e as ContentElement;
if (contentElement == null)
{
uiElement3D = e as UIElement3D;
}
}
// Add this element to route
if (uiElement != null)
{
uiElement.AddToEventRoute(route, args);
}
else if (contentElement != null)
{
contentElement.AddToEventRoute(route, args);
}
else if (uiElement3D != null)
{
uiElement3D.AddToEventRoute(route, args);
}
}
else
{
int cElements = 0;
while (e != null)
{
UIElement uiElement = e as UIElement;
ContentElement contentElement = null;
UIElement3D uiElement3D = null;
if (uiElement == null)
{
contentElement = e as ContentElement;
if (contentElement == null)
{
uiElement3D = e as UIElement3D;
}
}
// Protect against infinite loops by limiting the number of elements
// that we will process.
if (cElements++ > MAX_ELEMENTS_IN_ROUTE)
{
throw new InvalidOperationException(SR.Get(SRID.TreeLoop));
}
// Allow the element to adjust source
object newSource = null;
if (uiElement != null)
{
newSource = uiElement.AdjustEventSource(args);
}
else if (contentElement != null)
{
newSource = contentElement.AdjustEventSource(args);
}
else if (uiElement3D != null)
{
newSource = uiElement3D.AdjustEventSource(args);
}
// Add changed source information to the route
if (newSource != null)
{
route.AddSource(newSource);
}
// Invoke BuildRouteCore
bool continuePastVisualTree = false;
if (uiElement != null)
{
continuePastVisualTree = uiElement.BuildRouteCore(route, args);
// Add this element to route
uiElement.AddToEventRoute(route, args);
// Get element's visual parent
e = uiElement.GetUIParent(continuePastVisualTree);
}
else if (contentElement != null)
{
continuePastVisualTree = contentElement.BuildRouteCore(route, args);
// Add this element to route
contentElement.AddToEventRoute(route, args);
// Get element's visual parent
e = (DependencyObject) contentElement.GetUIParent(continuePastVisualTree);
}
else if (uiElement3D != null)
{
continuePastVisualTree = uiElement3D.BuildRouteCore(route, args);
// Add this element to route
uiElement3D.AddToEventRoute(route, args);
// Get element's visual parent
e = uiElement3D.GetUIParent(continuePastVisualTree);
}
// If the BuildRouteCore implementation changed the
// args.Source to the route parent, respect it in
// the actual route.
if (e == args.Source)
{
route.AddSource(e);
}
}
}
}
///
/// Adds a handler for the given attached event
///
[FriendAccessAllowed] // Built into Core, also used by Framework.
internal static void AddHandler(DependencyObject d, RoutedEvent routedEvent, Delegate handler)
{
if (d == null)
{
throw new ArgumentNullException("d");
}
Debug.Assert(routedEvent != null, "RoutedEvent must not be null");
UIElement uiElement = d as UIElement;
if (uiElement != null)
{
uiElement.AddHandler(routedEvent, handler);
}
else
{
ContentElement contentElement = d as ContentElement;
if (contentElement != null)
{
contentElement.AddHandler(routedEvent, handler);
}
else
{
UIElement3D uiElement3D = d as UIElement3D;
if (uiElement3D != null)
{
uiElement3D.AddHandler(routedEvent, handler);
}
else
{
throw new ArgumentException(SR.Get(SRID.Invalid_IInputElement, d.GetType()));
}
}
}
}
///
/// Removes a handler for the given attached event
///
[FriendAccessAllowed] // Built into Core, also used by Framework.
internal static void RemoveHandler(DependencyObject d, RoutedEvent routedEvent, Delegate handler)
{
if (d == null)
{
throw new ArgumentNullException("d");
}
Debug.Assert(routedEvent != null, "RoutedEvent must not be null");
UIElement uiElement = d as UIElement;
if (uiElement != null)
{
uiElement.RemoveHandler(routedEvent, handler);
}
else
{
ContentElement contentElement = d as ContentElement;
if (contentElement != null)
{
contentElement.RemoveHandler(routedEvent, handler);
}
else
{
UIElement3D uiElement3D = d as UIElement3D;
if (uiElement3D != null)
{
uiElement3D.RemoveHandler(routedEvent, handler);
}
else
{
throw new ArgumentException(SR.Get(SRID.Invalid_IInputElement, d.GetType()));
}
}
}
}
#region LoadedAndUnloadedEvents
///
/// Initiate the processing for [Un]Loaded event broadcast starting at this node
///
internal virtual void OnPresentationSourceChanged(bool attached)
{
// Reset the FocusedElementProperty in order to get LostFocus event
if (!attached && FocusManager.GetFocusedElement(this)!=null)
FocusManager.SetFocusedElement(this, null);
}
#endregion LoadedAndUnloadedEvents
///
/// Translates a point relative to this element to coordinates that
/// are relative to the specified element.
///
///
/// Passing null indicates that coordinates should be relative to
/// the root element in the tree that this element belongs to.
///
public Point TranslatePoint(Point point, UIElement relativeTo)
{
return InputElement.TranslatePoint(point, this, relativeTo);
}
///
/// Returns the input element within this element that is
/// at the specified coordinates relative to this element.
///
public IInputElement InputHitTest(Point point)
{
IInputElement enabledHit;
IInputElement rawHit;
InputHitTest(point, out enabledHit, out rawHit);
return enabledHit;
}
///
/// Returns the input element within this element that is
/// at the specified coordinates relative to this element.
///
///
/// This is the coordinate, relative to this element, at which
/// to look for elements within this one.
///
///
/// This element is the deepest enabled input element that is at the
/// specified coordinates.
///
///
/// This element is the deepest input element (not necessarily enabled)
/// that is at the specified coordinates.
///
internal void InputHitTest(Point pt, out IInputElement enabledHit, out IInputElement rawHit)
{
PointHitTestParameters hitTestParameters = new PointHitTestParameters(pt);
// We store the result of the hit testing here. Note that the
// HitTestResultCallback is an instance method on this class
// so that it can store the element we hit.
InputHitTestResult result = new InputHitTestResult();
VisualTreeHelper.HitTest(this,
new HitTestFilterCallback(InputHitTestFilterCallback),
new HitTestResultCallback(result.InputHitTestResultCallback),
hitTestParameters);
DependencyObject candidate = result.Result;
rawHit = candidate as IInputElement;
enabledHit = null;
while (candidate != null)
{
IContentHost contentHost = candidate as IContentHost;
if (contentHost != null)
{
// Do not call IContentHost.InputHitTest if the containing UIElement
// is not enabled.
DependencyObject containingElement = InputElement.GetContainingUIElement(candidate);
if ((bool)containingElement.GetValue(IsEnabledProperty))
{
pt = InputElement.TranslatePoint(pt, this, candidate);
enabledHit = rawHit = contentHost.InputHitTest(pt);
if (enabledHit != null)
{
break;
}
}
}
UIElement element = candidate as UIElement;
if (element != null)
{
if (rawHit == null)
{
// Earlier we hit a non-IInputElement. This is the first one
// we've found, so use that as rawHit.
rawHit = element;
}
if (element.IsEnabled)
{
enabledHit = element;
break;
}
}
UIElement3D element3D = candidate as UIElement3D;
if (element3D != null)
{
if (rawHit == null)
{
// Earlier we hit a non-IInputElement. This is the first one
// we've found, so use that as rawHit.
rawHit = element3D;
}
if (element3D.IsEnabled)
{
enabledHit = element3D;
break;
}
}
if (candidate == this)
{
// We are at the element where the hit-test was initiated.
// If we haven't found the hit element by now, we missed
// everything.
break;
}
candidate = VisualTreeHelper.GetParentInternal(candidate);
}
}
private HitTestFilterBehavior InputHitTestFilterCallback(DependencyObject currentNode)
{
HitTestFilterBehavior behavior = HitTestFilterBehavior.Continue;
if(UIElementHelper.IsUIElementOrUIElement3D(currentNode))
{
if(!UIElementHelper.IsVisible(currentNode))
{
// The element we are currently processing is not visible,
// so we do not allow hit testing to continue down this
// subtree.
behavior = HitTestFilterBehavior.ContinueSkipSelfAndChildren;
}
if(!UIElementHelper.IsHitTestVisible(currentNode))
{
// The element we are currently processing is not visible for hit testing,
// so we do not allow hit testing to continue down this
// subtree.
behavior = HitTestFilterBehavior.ContinueSkipSelfAndChildren;
}
}
else
{
// This is just a raw Visual, so it cannot receive input.
// We allow the hit testing to continue through this visual.
//
// When we report the final input, we will return the containing
// UIElement.
behavior = HitTestFilterBehavior.Continue;
}
return behavior;
}
private class InputHitTestResult
{
public HitTestResultBehavior InputHitTestResultCallback(HitTestResult result)
{
_result = result;
return HitTestResultBehavior.Stop;
}
public DependencyObject Result
{
get
{
return _result != null ? _result.VisualHit : null;
}
}
private HitTestResult _result;
}
//
// Left/Right Mouse Button Cracking Routines:
//
private static RoutedEvent CrackMouseButtonEvent(MouseButtonEventArgs e)
{
RoutedEvent newEvent = null;
switch(e.ChangedButton)
{
case MouseButton.Left:
if(e.RoutedEvent == Mouse.PreviewMouseDownEvent)
newEvent = UIElement.PreviewMouseLeftButtonDownEvent;
else if(e.RoutedEvent == Mouse.MouseDownEvent)
newEvent = UIElement.MouseLeftButtonDownEvent;
else if(e.RoutedEvent == Mouse.PreviewMouseUpEvent)
newEvent = UIElement.PreviewMouseLeftButtonUpEvent;
else
newEvent = UIElement.MouseLeftButtonUpEvent;
break;
case MouseButton.Right:
if(e.RoutedEvent == Mouse.PreviewMouseDownEvent)
newEvent = UIElement.PreviewMouseRightButtonDownEvent;
else if(e.RoutedEvent == Mouse.MouseDownEvent)
newEvent = UIElement.MouseRightButtonDownEvent;
else if(e.RoutedEvent == Mouse.PreviewMouseUpEvent)
newEvent = UIElement.PreviewMouseRightButtonUpEvent;
else
newEvent = UIElement.MouseRightButtonUpEvent;
break;
default:
// No wrappers exposed for the other buttons.
break;
}
return ( newEvent );
}
private static void CrackMouseButtonEventAndReRaiseEvent(DependencyObject sender, MouseButtonEventArgs e)
{
RoutedEvent newEvent = CrackMouseButtonEvent(e);
if (newEvent != null)
{
ReRaiseEventAs(sender, e, newEvent);
}
}
///
/// Re-raises an event with as a different RoutedEvent.
///
///
/// Only used internally. Added to support cracking generic MouseButtonDown/Up events
/// into MouseLeft/RightButtonDown/Up events.
///
///
/// RoutedEventsArgs to re-raise with a new RoutedEvent
///
///
/// The new RoutedEvent to be associated with the RoutedEventArgs
///
private static void ReRaiseEventAs(DependencyObject sender, RoutedEventArgs args, RoutedEvent newEvent)
{
// Preseve and change the RoutedEvent
RoutedEvent preservedRoutedEvent = args.RoutedEvent;
args.OverrideRoutedEvent( newEvent );
// Preserve Source
object preservedSource = args.Source;
EventRoute route = EventRouteFactory.FetchObject(args.RoutedEvent);
if( TraceRoutedEvent.IsEnabled )
{
TraceRoutedEvent.Trace(
TraceEventType.Start,
TraceRoutedEvent.ReRaiseEventAs,
args.RoutedEvent,
sender,
args,
args.Handled );
}
try
{
// Build the route and invoke the handlers
UIElement.BuildRouteHelper(sender, route, args);
route.ReInvokeHandlers(sender, args);
// Restore Source
args.OverrideSource(preservedSource);
// Restore RoutedEvent
args.OverrideRoutedEvent(preservedRoutedEvent);
}
finally
{
if( TraceRoutedEvent.IsEnabled )
{
TraceRoutedEvent.Trace(
TraceEventType.Stop,
TraceRoutedEvent.ReRaiseEventAs,
args.RoutedEvent,
sender,
args,
args.Handled );
}
}
// Recycle the route object
EventRouteFactory.RecycleObject(route);
}
///
/// Implementation of RaiseEvent.
/// Called by both the trusted and non-trusted flavors of RaiseEvent.
///
internal static void RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
{
EventRoute route = EventRouteFactory.FetchObject(args.RoutedEvent);
if( TraceRoutedEvent.IsEnabled )
{
TraceRoutedEvent.Trace(
TraceEventType.Start,
TraceRoutedEvent.RaiseEvent,
args.RoutedEvent,
sender,
args,
args.Handled );
}
try
{
// Set Source
args.Source = sender;
UIElement.BuildRouteHelper(sender, route, args);
route.InvokeHandlers(sender, args);
// Reset Source to OriginalSource
args.Source = args.OriginalSource;
}
finally
{
if( TraceRoutedEvent.IsEnabled )
{
TraceRoutedEvent.Trace(
TraceEventType.Stop,
TraceRoutedEvent.RaiseEvent,
args.RoutedEvent,
sender,
args,
args.Handled );
}
}
EventRouteFactory.RecycleObject(route);
}
///
/// A property indicating if the mouse is over this element or not.
///
public bool IsMouseDirectlyOver
{
get
{
// We do not return the cached value of reverse-inherited seed properties.
//
// The cached value is only used internally to detect a "change".
//
// More Info:
// The act of invalidating the seed property of a reverse-inherited property
// on the first side of the path causes the invalidation of the
// reverse-inherited properties on both sides. The input system has not yet
// invalidated the seed property on the second side, so its cached value can
// be incorrect.
//
return IsMouseDirectlyOver_ComputeValue();
}
}
private bool IsMouseDirectlyOver_ComputeValue()
{
return (Mouse.DirectlyOver == this);
}
#region new
///
/// Asynchronously re-evaluate the reverse-inherited properties.
///
[FriendAccessAllowed]
internal void SynchronizeReverseInheritPropertyFlags(DependencyObject oldParent, bool isCoreParent)
{
if(IsKeyboardFocusWithin)
{
Keyboard.PrimaryDevice.ReevaluateFocusAsync(this, oldParent, isCoreParent);
}
// Reevelauate the stylus properties first to guarentee that our property change
// notifications fire before mouse properties.
if(IsStylusOver)
{
StylusLogic.CurrentStylusLogicReevaluateStylusOver(this, oldParent, isCoreParent);
}
if(IsStylusCaptureWithin)
{
StylusLogic.CurrentStylusLogicReevaluateCapture(this, oldParent, isCoreParent);
}
if(IsMouseOver)
{
Mouse.PrimaryDevice.ReevaluateMouseOver(this, oldParent, isCoreParent);
}
if(IsMouseCaptureWithin)
{
Mouse.PrimaryDevice.ReevaluateCapture(this, oldParent, isCoreParent);
}
}
///
/// Controls like popup want to control updating parent properties. This method
/// provides an opportunity for those controls to participate and block it.
///
internal virtual bool BlockReverseInheritance()
{
return false;
}
///
/// A property indicating if the mouse is over this element or not.
///
public bool IsMouseOver
{
get
{
return ReadFlag(CoreFlags.IsMouseOverCache);
}
}
///
/// A property indicating if the stylus is over this element or not.
///
public bool IsStylusOver
{
get
{
return ReadFlag(CoreFlags.IsStylusOverCache);
}
}
///
/// Indicates if Keyboard Focus is anywhere
/// within in the subtree starting at the
/// current instance
///
public bool IsKeyboardFocusWithin
{
get
{
return ReadFlag(CoreFlags.IsKeyboardFocusWithinCache);
}
}
#endregion new
///
/// A property indicating if the mouse is captured to this element or not.
///
public bool IsMouseCaptured
{
get { return (bool) GetValue(IsMouseCapturedProperty); }
}
///
/// Captures the mouse to this element.
///
public bool CaptureMouse()
{
return Mouse.Capture(this);
}
///
/// Releases the mouse capture.
///
public void ReleaseMouseCapture()
{
if (Mouse.Captured == this)
{
Mouse.Capture(null);
}
}
///
/// Indicates if mouse capture is anywhere within the subtree
/// starting at the current instance
///
public bool IsMouseCaptureWithin
{
get
{
return ReadFlag(CoreFlags.IsMouseCaptureWithinCache);
}
}
///
/// A property indicating if the stylus is over this element or not.
///
public bool IsStylusDirectlyOver
{
get
{
// We do not return the cached value of reverse-inherited seed properties.
//
// The cached value is only used internally to detect a "change".
//
// More Info:
// The act of invalidating the seed property of a reverse-inherited property
// on the first side of the path causes the invalidation of the
// reverse-inherited properties on both sides. The input system has not yet
// invalidated the seed property on the second side, so its cached value can
// be incorrect.
//
return IsStylusDirectlyOver_ComputeValue();
}
}
private bool IsStylusDirectlyOver_ComputeValue()
{
return (Stylus.DirectlyOver == this);
}
///
/// A property indicating if the stylus is captured to this element or not.
///
public bool IsStylusCaptured
{
get { return (bool) GetValue(IsStylusCapturedProperty); }
}
///
/// Captures the stylus to this element.
///
public bool CaptureStylus()
{
return Stylus.Capture(this);
}
///
/// Releases the stylus capture.
///
public void ReleaseStylusCapture()
{
Stylus.Capture(null);
}
///
/// Indicates if stylus capture is anywhere within the subtree
/// starting at the current instance
///
public bool IsStylusCaptureWithin
{
get
{
return ReadFlag(CoreFlags.IsStylusCaptureWithinCache);
}
}
///
/// A property indicating if the keyboard is focused on this
/// element or not.
///
public bool IsKeyboardFocused
{
get
{
// We do not return the cached value of reverse-inherited seed properties.
//
// The cached value is only used internally to detect a "change".
//
// More Info:
// The act of invalidating the seed property of a reverse-inherited property
// on the first side of the path causes the invalidation of the
// reverse-inherited properties on both sides. The input system has not yet
// invalidated the seed property on the second side, so its cached value can
// be incorrect.
//
return IsKeyboardFocused_ComputeValue();
}
}
private bool IsKeyboardFocused_ComputeValue()
{
return (Keyboard.FocusedElement == this);
}
///
/// Set a logical focus on the element. If the current keyboard focus is within the same scope move the keyboard on this element.
///
public bool Focus()
{
if (Keyboard.Focus(this) == this)
{
// Successfully setting the keyboard focus updated the logical focus as well
return true;
}
if (Focusable && IsEnabled)
{
// If we cannot set keyboard focus then set the logical focus only
// Find element's FocusScope and set its FocusedElement if not already set
// If FocusedElement is already set we don't want to steal focus for that scope
DependencyObject focusScope = FocusManager.GetFocusScope(this);
if (FocusManager.GetFocusedElement(focusScope) == null)
{
FocusManager.SetFocusedElement(focusScope, (IInputElement)this);
}
}
// Return false because current KeyboardFocus is not set on the element - only the logical focus is set
return false;
}
///
/// Request to move the focus from this element to another element
///
/// Determine how to move the focus
/// Returns true if focus is moved successfully. Returns false if there is no next element
public virtual bool MoveFocus(TraversalRequest request)
{
return false;
}
///
/// Request to predict the element that should receive focus relative to this element for a
/// given direction, without actually moving focus to it.
///
/// The direction for which focus should be predicted
///
/// Returns the next element that focus should move to for a given FocusNavigationDirection.
/// Returns null if focus cannot be moved relative to this element.
///
public virtual DependencyObject PredictFocus(FocusNavigationDirection direction)
{
return null;
}
///
/// The access key for this element was invoked. Base implementation sets focus to the element.
///
/// The arguments to the access key event
protected virtual void OnAccessKey(AccessKeyEventArgs e)
{
this.Focus();
}
///
/// A property indicating if the inptu method is enabled.
///
public bool IsInputMethodEnabled
{
get { return (bool) GetValue(InputMethod.IsInputMethodEnabledProperty); }
}
///
/// The Opacity property.
///
public static readonly DependencyProperty OpacityProperty =
DependencyProperty.Register(
"Opacity",
typeof(double),
typeof(UIElement),
new UIPropertyMetadata(
1.0d,
new PropertyChangedCallback(Opacity_Changed)));
private static void Opacity_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UIElement uie = (UIElement) d;
uie.pushOpacity();
}
///
/// Opacity applied to the rendered content of the UIElement. When set, this opacity value
/// is applied uniformly to the entire UIElement.
///
[Localizability(LocalizationCategory.None, Readability = Readability.Unreadable)]
public double Opacity
{
get { return (double) GetValue(OpacityProperty); }
set { SetValue(OpacityProperty, value); }
}
private void pushOpacity()
{
if(this.Visibility == Visibility.Visible)
{
base.VisualOpacity = Opacity;
}
}
///
/// The OpacityMask property.
///
public static readonly DependencyProperty OpacityMaskProperty
= DependencyProperty.Register("OpacityMask", typeof(Brush), typeof(UIElement),
new UIPropertyMetadata(new PropertyChangedCallback(OpacityMask_Changed)));
private static void OpacityMask_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UIElement uie = (UIElement) d;
uie.pushOpacityMask();
}
///
/// OpacityMask applied to the rendered content of the UIElement. When set, the alpha channel
/// of the Brush's rendered content is applied to the rendered content of the UIElement.
/// The other channels of the Brush's rendered content (e.g., Red, Green, or Blue) are ignored.
///
public Brush OpacityMask
{
get { return (Brush) GetValue(OpacityMaskProperty); }
set { SetValue(OpacityMaskProperty, value); }
}
private void pushOpacityMask()
{
base.VisualOpacityMask = OpacityMask;
}
///
/// The BitmapEffect property.
///
public static readonly DependencyProperty BitmapEffectProperty =
DependencyProperty.Register(
"BitmapEffect",
typeof(BitmapEffect),
typeof(UIElement),
new UIPropertyMetadata(new PropertyChangedCallback(OnBitmapEffectChanged)));
private static void OnBitmapEffectChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UIElement uie = (UIElement)d;
uie.pushBitmapEffect();
}
///
/// BitmapEffect applied to the rendered content of the UIElement.
///
[Obsolete(MS.Internal.Media.VisualTreeUtils.BitmapEffectObsoleteMessage)]
public BitmapEffect BitmapEffect
{
get { return (BitmapEffect) GetValue(BitmapEffectProperty); }
set { SetValue(BitmapEffectProperty, value); }
}
private void pushBitmapEffect()
{
#pragma warning disable 0618
base.VisualBitmapEffect = BitmapEffect;
#pragma warning restore 0618
}
///
/// The Effect property.
///
public static readonly DependencyProperty EffectProperty =
DependencyProperty.Register(
"Effect",
typeof(Effect),
typeof(UIElement),
new UIPropertyMetadata(new PropertyChangedCallback(OnEffectChanged)));
private static void OnEffectChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UIElement uie = (UIElement)d;
uie.pushEffect();
}
///
/// Effect applied to the rendered content of the UIElement.
///
public Effect Effect
{
get { return (Effect) GetValue(EffectProperty); }
set { SetValue(EffectProperty, value); }
}
private void pushEffect()
{
base.VisualEffect = Effect;
}
///
/// The BitmapEffectInput property.
///
public static readonly DependencyProperty BitmapEffectInputProperty =
DependencyProperty.Register(
"BitmapEffectInput",
typeof(BitmapEffectInput),
typeof(UIElement),
new UIPropertyMetadata(new PropertyChangedCallback(OnBitmapEffectInputChanged)));
private static void OnBitmapEffectInputChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((UIElement) d).pushBitmapEffectInput((BitmapEffectInput) e.NewValue);
}
///
/// BitmapEffectInput accessor.
///
[Obsolete(MS.Internal.Media.VisualTreeUtils.BitmapEffectObsoleteMessage)]
public BitmapEffectInput BitmapEffectInput
{
get { return (BitmapEffectInput) GetValue(BitmapEffectInputProperty); }
set { SetValue(BitmapEffectInputProperty, value); }
}
private void pushBitmapEffectInput(BitmapEffectInput newValue)
{
#pragma warning disable 0618
base.VisualBitmapEffectInput = newValue;
#pragma warning restore 0618
}
private static void EdgeMode_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UIElement uie = (UIElement) d;
uie.pushEdgeMode();
}
private void pushEdgeMode()
{
base.VisualEdgeMode = RenderOptions.GetEdgeMode(this);
}
private static void BitmapScalingMode_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UIElement uie = (UIElement) d;
uie.pushBitmapScalingMode();
}
private void pushBitmapScalingMode()
{
base.VisualBitmapScalingMode = RenderOptions.GetBitmapScalingMode(this);
}
///
/// pushVisualEffects - helper to propagate Opacity, OpacityMask, BitmapEffect, BitmapScalingMode and EdgeMode
///
private void pushVisualEffects()
{
pushOpacity();
pushOpacityMask();
pushBitmapEffect();
pushEdgeMode();
pushBitmapScalingMode();
}
#region Uid
///
/// Uid can be specified in xaml at any point using the xaml language attribute x:Uid.
/// This is a long lasting (persisted in source) unique id for an element.
///
static public readonly DependencyProperty UidProperty =
DependencyProperty.Register(
"Uid",
typeof(string),
typeof(UIElement),
new UIPropertyMetadata(String.Empty));
///
/// Uid can be specified in xaml at any point using the xaml language attribute x:Uid.
/// This is a long lasting (persisted in source) unique id for an element.
///
public string Uid
{
get { return (string)GetValue(UidProperty); }
set { SetValue(UidProperty, value); }
}
#endregion Uid
///
/// The Visibility property.
///
[CommonDependencyProperty]
public static readonly DependencyProperty VisibilityProperty =
DependencyProperty.Register(
"Visibility",
typeof(Visibility),
typeof(UIElement),
new PropertyMetadata(
VisibilityBoxes.VisibleBox,
new PropertyChangedCallback(OnVisibilityChanged)),
new ValidateValueCallback(ValidateVisibility));
private static void OnVisibilityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UIElement uie = (UIElement) d;
Visibility newVisibility = (Visibility) e.NewValue;
uie.VisibilityCache = newVisibility;
uie.switchVisibilityIfNeeded(newVisibility);
// The IsVisible property depends on this property.
uie.UpdateIsVisibleCache();
}
private static bool ValidateVisibility(object o)
{
Visibility value = (Visibility) o;
return (value == Visibility.Visible) || (value == Visibility.Hidden) || (value == Visibility.Collapsed);
}
///
/// Visibility accessor
///
[Localizability(LocalizationCategory.None, Readability = Readability.Unreadable)]
public Visibility Visibility
{
get { return VisibilityCache; }
set { SetValue(VisibilityProperty, VisibilityBoxes.Box(value)); }
}
private void switchVisibilityIfNeeded(Visibility visibility)
{
switch(visibility)
{
case Visibility.Visible:
ensureVisible();
break;
case Visibility.Hidden:
ensureInvisible(false);
break;
case Visibility.Collapsed:
ensureInvisible(true);
break;
}
}
private void ensureVisible()
{
if(ReadFlag(CoreFlags.IsOpacitySuppressed))
{
//restore Opacity
base.VisualOpacity = Opacity;
if(ReadFlag(CoreFlags.IsCollapsed))
{
WriteFlag(CoreFlags.IsCollapsed, false);
//invalidate parent if needed
signalDesiredSizeChange();
//we are suppressing rendering (see IsRenderable) of collapsed children (to avoid
//confusion when they see RenderSize=(0,0) reported for them)
//so now we should invalidate to re-render if some rendering props
//changed while UIElement was Collapsed (Arrange will cause re-rendering)
InvalidateVisual();
}
WriteFlag(CoreFlags.IsOpacitySuppressed, false);
}
}
private void ensureInvisible(bool collapsed)
{
if(!ReadFlag(CoreFlags.IsOpacitySuppressed))
{
base.VisualOpacity = 0;
WriteFlag(CoreFlags.IsOpacitySuppressed, true);
}
if(!ReadFlag(CoreFlags.IsCollapsed) && collapsed) //Hidden or Visible->Collapsed
{
WriteFlag(CoreFlags.IsCollapsed, true);
//invalidate parent
signalDesiredSizeChange();
}
else if(ReadFlag(CoreFlags.IsCollapsed) && !collapsed) //Collapsed -> Hidden
{
WriteFlag(CoreFlags.IsCollapsed, false);
//invalidate parent
signalDesiredSizeChange();
}
}
private void signalDesiredSizeChange()
{
UIElement p;
IContentHost ich;
GetUIParentOrICH(out p, out ich); //only one will be returned
if(p != null)
p.OnChildDesiredSizeChanged(this);
else if(ich != null)
ich.OnChildDesiredSizeChanged(this);
}
private void ensureClip(Size layoutSlotSize)
{
Geometry clipGeometry = GetLayoutClip(layoutSlotSize);
if(Clip != null)
{
if(clipGeometry == null)
clipGeometry = Clip;
else
{
CombinedGeometry cg = new CombinedGeometry(
GeometryCombineMode.Intersect,
clipGeometry,
Clip);
clipGeometry = cg;
}
}
ChangeVisualClip(clipGeometry, true /* dontSetWhenClose */);
}
///
/// HitTestCore implements precise hit testing against render contents
///
protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
{
if (_drawingContent != null)
{
if (_drawingContent.HitTestPoint(hitTestParameters.HitPoint))
{
return new PointHitTestResult(this, hitTestParameters.HitPoint);
}
}
return null;
}
///
/// HitTestCore implements precise hit testing against render contents
///
protected override GeometryHitTestResult HitTestCore(GeometryHitTestParameters hitTestParameters)
{
if ((_drawingContent != null) && GetHitTestBounds().IntersectsWith(hitTestParameters.Bounds))
{
IntersectionDetail intersectionDetail;
intersectionDetail = _drawingContent.HitTestGeometry(hitTestParameters.InternalHitGeometry);
Debug.Assert(intersectionDetail != IntersectionDetail.NotCalculated);
if (intersectionDetail != IntersectionDetail.Empty)
{
return new GeometryHitTestResult(this, intersectionDetail);
}
}
return null;
}
///
/// Opens the DrawingVisual for rendering. The returned DrawingContext can be used to
/// render into the DrawingVisual.
///
[FriendAccessAllowed]
internal DrawingContext RenderOpen()
{
return new VisualDrawingContext(this);
}
///
/// Precomputes the render data content.
///
internal override void PrecomputeContent()
{
base.PrecomputeContent();
if (_drawingContent != null)
{
_drawingContent.PrecomputeContent();
bool requiresRealizations = _drawingContent.ContentRequiresRealizationUpdates;
// Set the NodeUsesRealizationCaches flag for this content.
SetFlags(requiresRealizations, VisualFlags.NodeUsesRealizationCaches);
SetFlags(_drawingContent.ContentIntroducesGraphness, VisualFlags.NodeOrDescendantIntroducesGraphness);
}
}
///
/// Called from the DrawingContext when the DrawingContext is closed.
///
internal override void RenderClose(IDrawingContent newContent)
{
VisualFlags flags = VisualFlags.IsSubtreeDirtyForPrecompute | VisualFlags.NodeNeedsBitmapEffectUpdate;
IDrawingContent oldContent = _drawingContent;
//this element does not render - return
if(oldContent == null && newContent == null)
return;
//
// First cleanup the old content and the state associate with this node
// related to it's content.
//
_drawingContent = null;
if (oldContent != null)
{
//
// Remove the notification handlers.
//
oldContent.PropagateChangedHandler(ContentsChangedHandler, false /* remove */);
//
// Disconnect the old content from this visual.
//
DisconnectAttachedResource(
VisualProxyFlags.IsContentConnected,
((DUCE.IResource)oldContent));
}
//
// Prepare the new content.
//
if (newContent != null)
{
// Propagate notification handlers.
newContent.PropagateChangedHandler(ContentsChangedHandler, true /* adding */);
// Might need a new realization if this content contains text.
SetFlags(true, VisualFlags.NodeRequiresNewRealization);
flags |= VisualFlags.NodeInSubtreeRequiresNewRealization;
}
_drawingContent = newContent;
//
// Mark the visual dirty on all channels and propagate
// the flags up the parent chain.
//
SetFlagsOnAllChannels(true, VisualProxyFlags.IsContentDirty);
PropagateFlags(
this,
flags,
VisualProxyFlags.IsSubtreeDirtyForRender);
}
///
/// Overriding this function to release DUCE resources during Dispose and during removal of a subtree.
///
///
/// Critical - calls other critical code (base)
///
[SecurityCritical]
internal override void FreeContent(DUCE.Channel channel)
{
Debug.Assert(_proxy.IsOnChannel(channel));
if (_drawingContent != null)
{
if (CheckFlagsAnd(channel, VisualProxyFlags.IsContentConnected))
{
DUCE.CompositionNode.SetContent(
_proxy.GetHandle(channel),
DUCE.ResourceHandle.Null,
channel);
((DUCE.IResource)_drawingContent).ReleaseOnChannel(channel);
SetFlags(channel, false, VisualProxyFlags.IsContentConnected);
}
}
// Call the base method too
base.FreeContent(channel);
}
///
/// Returns the bounding box of the content.
///
internal override Rect GetContentBounds()
{
if (_drawingContent != null)
{
Rect resultRect = Rect.Empty;
MediaContext mediaContext = MediaContext.From(Dispatcher);
BoundsDrawingContextWalker ctx = mediaContext.AcquireBoundsDrawingContextWalker();
resultRect = _drawingContent.GetContentBounds(ctx);
mediaContext.ReleaseBoundsDrawingContextWalker(ctx);
return resultRect;
}
else
{
return Rect.Empty;
}
}
///
/// WalkContent - method which walks the content (if present) and calls out to the
/// supplied DrawingContextWalker.
///
///
/// DrawingContextWalker - the target of the calls which occur during
/// the content walk.
///
internal void WalkContent(DrawingContextWalker walker)
{
VerifyAPIReadOnly();
if (_drawingContent != null)
{
_drawingContent.WalkContent(walker);
}
}
///
/// RenderContent is implemented by derived classes to hook up their
/// content. The implementer of this function can assert that the visual
/// resource is valid on a channel when the function is executed.
///
internal override void RenderContent(RenderContext ctx, bool isOnChannel)
{
DUCE.Channel channel = ctx.Channel;
Debug.Assert(!CheckFlagsAnd(channel, VisualProxyFlags.IsContentConnected));
Debug.Assert(_proxy.IsOnChannel(channel));
//
// Create the content on the channel.
//
if (_drawingContent != null)
{
DUCE.IResource drawingContent = (DUCE.IResource)_drawingContent;
drawingContent.AddRefOnChannel(channel);
// Hookup it up to the composition node.
DUCE.CompositionNode.SetContent(
_proxy.GetHandle(channel),
drawingContent.GetHandle(channel),
channel);
SetFlags(
channel,
true,
VisualProxyFlags.IsContentConnected);
}
else if (isOnChannel) /* _drawingContent == null */
{
DUCE.CompositionNode.SetContent(
_proxy.GetHandle(channel),
DUCE.ResourceHandle.Null,
channel);
}
}
///
/// Called by the base class to update realization caches.
/// Updates the realization cache on the content.
///
internal override void UpdateRealizations(RealizationContext ctx)
{
if (_drawingContent != null)
{
_drawingContent.UpdateRealizations(ctx);
}
}
///
/// GetDrawing - Returns the drawing content of this Visual.
///
///
/// Changes to this DrawingGroup will not be propagated to the Visual's content.
/// This method is called by both the Drawing property, and VisualTreeHelper.GetDrawing()
///
internal override DrawingGroup GetDrawing()
{
//
VerifyAPIReadOnly();
DrawingGroup drawingGroupContent = null;
// Convert our content to a DrawingGroup, if content exists
if (_drawingContent != null)
{
drawingGroupContent = DrawingServices.DrawingGroupFromRenderData((RenderData) _drawingContent);
}
return drawingGroupContent;
}
///
/// This method supplies an additional (to the property) clip geometry
/// that is used to intersect Clip in case if property is set to "true".
/// Typcally, this is a size of layout space given to the UIElement.
///
/// Geometry to use as additional clip if ClipToBounds=true
protected virtual Geometry GetLayoutClip(Size layoutSlotSize)
{
if(ClipToBounds)
{
RectangleGeometry rect = new RectangleGeometry(new Rect(RenderSize));
rect.Freeze();
return rect;
}
else
return null;
}
///
/// ClipToBounds Property
///
[CommonDependencyProperty]
public static readonly DependencyProperty ClipToBoundsProperty =
DependencyProperty.Register(
"ClipToBounds",
typeof(bool),
typeof(UIElement),
new PropertyMetadata(
BooleanBoxes.FalseBox, // default value
new PropertyChangedCallback(ClipToBounds_Changed)));
private static void ClipToBounds_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UIElement uie = (UIElement) d;
uie.ClipToBoundsCache = (bool) e.NewValue;
//if never measured, then nothing to do, it should be measured at some point
if(!uie.NeverMeasured || !uie.NeverArranged)
{
uie.InvalidateArrange();
}
}
///
/// ClipToBounds Property
///
///
/// This property enables the content of this UIElement to be clipped by automatic Layout
/// in order to "fit" into small space even if the content is larger.
/// For example, if a text string is longer then available space, and Layout can not give it the
/// "full" space to render, setting this property to "true" will ensure that the part of text string that
/// does not fit will be automatically clipped.
///
public bool ClipToBounds
{
get { return ClipToBoundsCache; }
set { SetValue(ClipToBoundsProperty, BooleanBoxes.Box(value)); }
}
///
/// Clip Property
///
public static readonly DependencyProperty ClipProperty =
DependencyProperty.Register(
"Clip",
typeof(Geometry),
typeof(UIElement),
new PropertyMetadata(
(Geometry) null,
new PropertyChangedCallback(Clip_Changed)));
private static void Clip_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UIElement uie = (UIElement) d;
// if never measured, then nothing to do, it should be measured at some point
if(!uie.NeverMeasured || !uie.NeverArranged)
{
uie.InvalidateArrange();
}
}
///
/// Clip Property
///
public Geometry Clip
{
get { return (Geometry) GetValue(ClipProperty); }
set { SetValue(ClipProperty, value); }
}
///
/// Align Property
///
public static readonly DependencyProperty SnapsToDevicePixelsProperty =
DependencyProperty.Register(
"SnapsToDevicePixels",
typeof(bool),
typeof(UIElement),
new PropertyMetadata(
BooleanBoxes.FalseBox,
new PropertyChangedCallback(SnapsToDevicePixels_Changed)));
private static void SnapsToDevicePixels_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UIElement uie = (UIElement) d;
uie.SnapsToDevicePixelsCache = (bool) e.NewValue;
// if never measured, then nothing to do, it should be measured at some point
if(!uie.NeverMeasured || !uie.NeverArranged)
{
uie.InvalidateArrange();
}
}
///
/// SnapsToDevicePixels Property
///
public bool SnapsToDevicePixels
{
get { return SnapsToDevicePixelsCache; }
set { SetValue(SnapsToDevicePixelsProperty, value); }
}
// Internal accessor for AccessKeyManager class
internal void InvokeAccessKey(AccessKeyEventArgs e)
{
OnAccessKey(e);
}
///
/// GotFocus event
///
public static readonly RoutedEvent GotFocusEvent = FocusManager.GotFocusEvent.AddOwner(typeof(UIElement));
///
/// An event announcing that IsFocused changed to true.
///
public event RoutedEventHandler GotFocus
{
add { AddHandler(GotFocusEvent, value); }
remove { RemoveHandler(GotFocusEvent, value); }
}
///
/// LostFocus event
///
public static readonly RoutedEvent LostFocusEvent = FocusManager.LostFocusEvent.AddOwner(typeof(UIElement));
///
/// An event announcing that IsFocused changed to false.
///
public event RoutedEventHandler LostFocus
{
add { AddHandler(LostFocusEvent, value); }
remove { RemoveHandler(LostFocusEvent, value); }
}
///
/// The DependencyProperty for the IsFocused property.
///
internal static readonly DependencyPropertyKey IsFocusedPropertyKey =
DependencyProperty.RegisterReadOnly(
"IsFocused",
typeof(bool),
typeof(UIElement),
new PropertyMetadata(
BooleanBoxes.FalseBox, // default value
new PropertyChangedCallback(IsFocused_Changed)));
///
/// The DependencyProperty for IsFocused.
/// Flags: None
/// Read-Only: true
///
public static readonly DependencyProperty IsFocusedProperty
= IsFocusedPropertyKey.DependencyProperty;
private static void IsFocused_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UIElement uiElement = ((UIElement)d);
if ((bool) e.NewValue)
{
uiElement.OnGotFocus(new RoutedEventArgs(GotFocusEvent, uiElement));
}
else
{
uiElement.OnLostFocus(new RoutedEventArgs(LostFocusEvent, uiElement));
}
}
///
/// This method is invoked when the IsFocused property changes to true
///
/// RoutedEventArgs
protected virtual void OnGotFocus(RoutedEventArgs e)
{
RaiseEvent(e);
}
///
/// This method is invoked when the IsFocused property changes to false
///
/// RoutedEventArgs
protected virtual void OnLostFocus(RoutedEventArgs e)
{
RaiseEvent(e);
}
///
/// Gettor for IsFocused Property
///
public bool IsFocused
{
get { return (bool) GetValue(IsFocusedProperty); }
}
//*********************************************************************
#region IsEnabled Property
//*********************************************************************
///
/// The DependencyProperty for the IsEnabled property.
///
[CommonDependencyProperty]
public static readonly DependencyProperty IsEnabledProperty =
DependencyProperty.Register(
"IsEnabled",
typeof(bool),
typeof(UIElement),
new UIPropertyMetadata(
BooleanBoxes.TrueBox, // default value
new PropertyChangedCallback(OnIsEnabledChanged),
new CoerceValueCallback(CoerceIsEnabled)));
///
/// A property indicating if this element is enabled or not.
///
public bool IsEnabled
{
get { return (bool) GetValue(IsEnabledProperty);}
set { SetValue(IsEnabledProperty, BooleanBoxes.Box(value)); }
}
///
/// IsEnabledChanged event
///
public event DependencyPropertyChangedEventHandler IsEnabledChanged
{
add {EventHandlersStoreAdd(IsEnabledChangedKey, value);}
remove {EventHandlersStoreRemove(IsEnabledChangedKey, value);}
}
internal static readonly EventPrivateKey IsEnabledChangedKey = new EventPrivateKey(); // Used by ContentElement
///
/// Fetches the value that IsEnabled should be coerced to.
///
///
/// This method is virtual is so that controls derived from UIElement
/// can combine additional requirements into the coersion logic.
///
/// It is important for anyone overriding this property to also
/// call CoerceValue when any of their dependencies change.
///
protected virtual bool IsEnabledCore
{
get
{
// As of 1/25/2006, the following controls override this method:
// ButtonBase.IsEnabledCore: CanExecute
// MenuItem.IsEnabledCore: CanExecute
// ScrollBar.IsEnabledCore: _canScroll
return true;
}
}
private static object CoerceIsEnabled(DependencyObject d, object value)
{
UIElement uie = (UIElement) d;
// We must be false if our parent is false, but we can be
// either true or false if our parent is true.
//
// Another way of saying this is that we can only be true
// if our parent is true, but we can always be false.
if((bool) value)
{
// Our parent can constrain us. We can be plugged into either
// a "visual" or "content" tree. If we are plugged into a
// "content" tree, the visual tree is just considered a
// visual representation, and is normally composed of raw
// visuals, not UIElements, so we prefer the content tree.
//
// The content tree uses the "logical" links. But not all
// "logical" links lead to a content tree.
//
DependencyObject parent = uie.GetUIParentCore() as ContentElement;
if(parent == null)
{
parent = InputElement.GetContainingUIElement(uie._parent);
}
if(parent == null || (bool)parent.GetValue(IsEnabledProperty))
{
return BooleanBoxes.Box(uie.IsEnabledCore);
}
else
{
return BooleanBoxes.FalseBox;
}
}
else
{
return BooleanBoxes.FalseBox;
}
}
private static void OnIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UIElement uie = (UIElement)d;
// Raise the public changed event.
uie.RaiseDependencyPropertyChanged(IsEnabledChangedKey, e);
// Invalidate the children so that they will inherit the new value.
InvalidateForceInheritPropertyOnChildren(uie, e.Property);
// The input manager needs to re-hittest because something changed
// that is involved in the hit-testing we do, so a different result
// could be returned.
InputManager.SafeCurrentNotifyHitTestInvalidated();
//Notify Automation in case it is interested.
AutomationPeer peer = uie.GetAutomationPeer();
if(peer != null)
peer.InvalidatePeer();
}
//**********************************************************************
#endregion IsEnabled Property
//*********************************************************************
//**********************************************************************
#region IsHitTestVisible Property
//**********************************************************************
///
/// The DependencyProperty for the IsHitTestVisible property.
///
public static readonly DependencyProperty IsHitTestVisibleProperty =
DependencyProperty.Register(
"IsHitTestVisible",
typeof(bool),
typeof(UIElement),
new UIPropertyMetadata(
BooleanBoxes.TrueBox, // default value
new PropertyChangedCallback(OnIsHitTestVisibleChanged),
new CoerceValueCallback(CoerceIsHitTestVisible)));
///
/// A property indicating if this element is hit test visible or not.
///
public bool IsHitTestVisible
{
get { return (bool) GetValue(IsHitTestVisibleProperty); }
set { SetValue(IsHitTestVisibleProperty, BooleanBoxes.Box(value)); }
}
///
/// IsHitTestVisibleChanged event
///
public event DependencyPropertyChangedEventHandler IsHitTestVisibleChanged
{
add {EventHandlersStoreAdd(IsHitTestVisibleChangedKey, value);}
remove {EventHandlersStoreRemove(IsHitTestVisibleChangedKey, value);}
}
internal static readonly EventPrivateKey IsHitTestVisibleChangedKey = new EventPrivateKey(); // Used by ContentElement
private static object CoerceIsHitTestVisible(DependencyObject d, object value)
{
UIElement uie = (UIElement) d;
// We must be false if our parent is false, but we can be
// either true or false if our parent is true.
//
// Another way of saying this is that we can only be true
// if our parent is true, but we can always be false.
if((bool) value)
{
// Our parent can constrain us. We can be plugged into either
// a "visual" or "content" tree. If we are plugged into a
// "content" tree, the visual tree is just considered a
// visual representation, and is normally composed of raw
// visuals, not UIElements, so we prefer the content tree.
//
// The content tree uses the "logical" links. But not all
// "logical" links lead to a content tree.
//
// However, ContentElements don't understand IsHitTestVisible,
// so we ignore them.
//
DependencyObject parent = InputElement.GetContainingUIElement(uie._parent);
if (parent == null || UIElementHelper.IsHitTestVisible(parent))
{
return BooleanBoxes.TrueBox;
}
else
{
return BooleanBoxes.FalseBox;
}
}
else
{
return BooleanBoxes.FalseBox;
}
}
private static void OnIsHitTestVisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UIElement uie = (UIElement)d;
// Raise the public changed event.
uie.RaiseDependencyPropertyChanged(IsHitTestVisibleChangedKey, e);
// Invalidate the children so that they will inherit the new value.
InvalidateForceInheritPropertyOnChildren(uie, e.Property);
// The input manager needs to re-hittest because something changed
// that is involved in the hit-testing we do, so a different result
// could be returned.
InputManager.SafeCurrentNotifyHitTestInvalidated();
}
//*********************************************************************
#endregion IsHitTestVisible Property
//**********************************************************************
//*********************************************************************
#region IsVisible Property
//*********************************************************************
// The IsVisible property is a read-only reflection of the Visibility
// property.
private static PropertyMetadata _isVisibleMetadata = new ReadOnlyPropertyMetadata(BooleanBoxes.FalseBox,
new GetReadOnlyValueCallback(GetIsVisible),
new PropertyChangedCallback(OnIsVisibleChanged));
internal static readonly DependencyPropertyKey IsVisiblePropertyKey =
DependencyProperty.RegisterReadOnly(
"IsVisible",
typeof(bool),
typeof(UIElement),
_isVisibleMetadata);
///
/// The DependencyProperty for the IsVisible property.
///
public static readonly DependencyProperty IsVisibleProperty = IsVisiblePropertyKey.DependencyProperty;
///
/// A property indicating if this element is Visible or not.
///
public bool IsVisible
{
get { return ReadFlag(CoreFlags.IsVisibleCache); }
}
private static object GetIsVisible(DependencyObject d, out BaseValueSourceInternal source)
{
source = BaseValueSourceInternal.Local;
return ((UIElement)d).IsVisible ? BooleanBoxes.TrueBox : BooleanBoxes.FalseBox;
}
///
/// IsVisibleChanged event
///
public event DependencyPropertyChangedEventHandler IsVisibleChanged
{
add {EventHandlersStoreAdd(IsVisibleChangedKey, value);}
remove {EventHandlersStoreRemove(IsVisibleChangedKey, value);}
}
internal static readonly EventPrivateKey IsVisibleChangedKey = new EventPrivateKey(); // Used by ContentElement
///
/// Critical - Calls a critical method (PresentationSource.CriticalFromVisual)
/// TreatAsSafe - No exposure
///
[SecurityCritical, SecurityTreatAsSafe]
internal void UpdateIsVisibleCache() // Called from PresentationSource
{
// IsVisible is a read-only property. It derives its "base" value
// from the Visibility property.
bool isVisible = (Visibility == Visibility.Visible);
// We must be false if our parent is false, but we can be
// either true or false if our parent is true.
//
// Another way of saying this is that we can only be true
// if our parent is true, but we can always be false.
if(isVisible)
{
bool constraintAllowsVisible = false;
// Our parent can constrain us. We can be plugged into either
// a "visual" or "content" tree. If we are plugged into a
// "content" tree, the visual tree is just considered a
// visual representation, and is normally composed of raw
// visuals, not UIElements, so we prefer the content tree.
//
// The content tree uses the "logical" links. But not all
// "logical" links lead to a content tree.
//
// However, ContentElements don't understand IsVisible,
// so we ignore them.
//
DependencyObject parent = InputElement.GetContainingUIElement(_parent);
if(parent != null)
{
constraintAllowsVisible = UIElementHelper.IsVisible(parent);
}
else
{
// We cannot be visible if we have no visual parent, unless:
// 1) We are the root, connected to a PresentationHost.
PresentationSource presentationSource = PresentationSource.CriticalFromVisual(this);
if(presentationSource != null)
{
constraintAllowsVisible = true;
}
else
{
// CODE
}
}
if(!constraintAllowsVisible)
{
isVisible = false;
}
}
if(isVisible != IsVisible)
{
// Our IsVisible force-inherited property has changed. Update our
// cache and raise a change notification.
WriteFlag(CoreFlags.IsVisibleCache, isVisible);
NotifyPropertyChange(new DependencyPropertyChangedEventArgs(IsVisibleProperty, _isVisibleMetadata, BooleanBoxes.Box(!isVisible), BooleanBoxes.Box(isVisible)));
}
}
private static void OnIsVisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UIElement uie = (UIElement) d;
// Raise the public changed event.
uie.RaiseDependencyPropertyChanged(IsVisibleChangedKey, e);
// Invalidate the children so that they will inherit the new value.
InvalidateForceInheritPropertyOnChildren(uie, e.Property);
// The input manager needs to re-hittest because something changed
// that is involved in the hit-testing we do, so a different result
// could be returned.
InputManager.SafeCurrentNotifyHitTestInvalidated();
}
//*********************************************************************
#endregion IsVisible Property
//**********************************************************************
//*********************************************************************
#region Focusable Property
//**********************************************************************
///
/// The DependencyProperty for the Focusable property.
///
[CommonDependencyProperty]
public static readonly DependencyProperty FocusableProperty =
DependencyProperty.Register(
"Focusable",
typeof(bool),
typeof(UIElement),
new UIPropertyMetadata(
BooleanBoxes.FalseBox, // default value
new PropertyChangedCallback(OnFocusableChanged)));
///
/// Gettor and Settor for Focusable Property
///
public bool Focusable
{
get { return (bool) GetValue(FocusableProperty); }
set { SetValue(FocusableProperty, BooleanBoxes.Box(value)); }
}
///
/// FocusableChanged event
///
public event DependencyPropertyChangedEventHandler FocusableChanged
{
add {EventHandlersStoreAdd(FocusableChangedKey, value);}
remove {EventHandlersStoreRemove(FocusableChangedKey, value);}
}
internal static readonly EventPrivateKey FocusableChangedKey = new EventPrivateKey(); // Used by ContentElement
private static void OnFocusableChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UIElement uie = (UIElement) d;
// Raise the public changed event.
uie.RaiseDependencyPropertyChanged(FocusableChangedKey, e);
}
//**********************************************************************
#endregion Focusable Property
//*********************************************************************
///
/// Called by the Automation infrastructure when AutomationPeer
/// is requested for this element. The element can return null or
/// the instance of AutomationPeer-derived clas, if it supports UI Automation
///
protected virtual AutomationPeer OnCreateAutomationPeer() { return null; }
///
/// Called by the Automation infrastructure or Control author
/// to make sure the AutomationPeer is created. The element may
/// create AP or return null, depending on OnCreateAutomationPeer override.
///
internal AutomationPeer CreateAutomationPeer()
{
VerifyAccess(); //this will ensure the AP is created in the right context
AutomationPeer ap = null;
if(HasAutomationPeer)
{
ap = AutomationPeerField.GetValue(this);
}
else
{
ap = OnCreateAutomationPeer();
if(ap != null)
{
AutomationPeerField.SetValue(this, ap);
HasAutomationPeer = true;
}
}
return ap;
}
///
/// Returns AutomationPeer if one exists.
/// The AutomationPeer may not exist if not yet created by Automation infrastructure
/// or if this element is not supposed to have one.
///
internal AutomationPeer GetAutomationPeer()
{
VerifyAccess();
if(HasAutomationPeer)
return AutomationPeerField.GetValue(this);
return null;
}
///
/// Called by the Automation infrastructure only in the case when the UIElement does not have a specific
/// peer (does not override OnCreateAutomationPeer) but we still want some generic peer to be created and cached.
/// For example, this is needed when HwndTarget contains a Panel and 2 Buttons underneath - the Panel
/// normally does not have a peer so only one of the Buttons is visible in the tree.
///
internal AutomationPeer CreateGenericRootAutomationPeer()
{
VerifyAccess(); //this will ensure the AP is created in the right context
AutomationPeer ap = null;
// If some peer was already created, specific or generic - use it.
if(HasAutomationPeer)
{
ap = AutomationPeerField.GetValue(this);
}
else
{
ap = new GenericRootAutomationPeer(this);
AutomationPeerField.SetValue(this, ap);
HasAutomationPeer = true;
}
return ap;
}
///
/// This is used by the parser and journaling to uniquely identify a given element
/// in a deterministic fashion, i.e., each time the same XAML/BAML is parsed/read,
/// the items will be given the same PersistId.
///
/// To keep PersistId from being serialized the set has been removed from the property and a separate
/// set method has been created.
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Obsolete("PersistId is an obsolete property and may be removed in a future release. The value of this property is not defined.")]
public int PersistId
{
get { return _persistId; }
}
///
/// This is used by the parser and journaling to uniquely identify a given element
/// in a deterministic fashion, i.e., each time the same XAML/BAML is parsed/read,
/// the items will be given the same PersistId.
///
///
/// To keep PersistId from being serialized the set has been removed from the property and a separate
/// set method has been created.
[FriendAccessAllowed] // Built into Core, also used by Framework.
internal void SetPersistId(int value)
{
_persistId = value;
}
// Helper method to retrieve and fire Clr Event handlers for DependencyPropertyChanged event
private void RaiseDependencyPropertyChanged(EventPrivateKey key, DependencyPropertyChangedEventArgs args)
{
EventHandlersStore store = EventHandlersStore;
if (store != null)
{
Delegate handler = store.Get(key);
if (handler != null)
{
((DependencyPropertyChangedEventHandler)handler)(this, args);
}
}
}
internal Rect PreviousArrangeRect
{
// called from PresentationFramework!System.Windows.Controls.Primitives.LayoutInformation.GetLayoutSlot()
[FriendAccessAllowed]
get
{
return _finalRect;
}
}
// Cache for the Visibility property. Storage is in Visual._nodeProperties.
private Visibility VisibilityCache
{
get
{
if (CheckFlagsAnd(VisualFlags.VisibilityCache_Visible))
{
return Visibility.Visible;
}
else if (CheckFlagsAnd(VisualFlags.VisibilityCache_TakesSpace))
{
return Visibility.Hidden;
}
else
{
return Visibility.Collapsed;
}
}
set
{
Debug.Assert(value == Visibility.Visible || value == Visibility.Hidden || value == Visibility.Collapsed);
switch (value)
{
case Visibility.Visible:
SetFlags(true, VisualFlags.VisibilityCache_Visible);
SetFlags(false, VisualFlags.VisibilityCache_TakesSpace);
break;
case Visibility.Hidden:
SetFlags(false, VisualFlags.VisibilityCache_Visible);
SetFlags(true, VisualFlags.VisibilityCache_TakesSpace);
break;
case Visibility.Collapsed:
SetFlags(false, VisualFlags.VisibilityCache_Visible);
SetFlags(false, VisualFlags.VisibilityCache_TakesSpace);
break;
}
}
}
#region ForceInherit property support
// Also called by FrameworkContentElement
internal static void SynchronizeForceInheritProperties(
UIElement uiElement,
ContentElement contentElement,
UIElement3D uiElement3D,
DependencyObject parent)
{
if(uiElement != null || uiElement3D != null)
{
bool parentValue = (bool) parent.GetValue(IsEnabledProperty);
if(!parentValue)
{
// For Read/Write force-inherited properties, use the standard coersion pattern.
//
// The IsEnabled property must be coerced false if the parent is false.
if (uiElement != null)
{
uiElement.CoerceValue(IsEnabledProperty);
}
else
{
uiElement3D.CoerceValue(IsEnabledProperty);
}
}
parentValue = (bool) parent.GetValue(IsHitTestVisibleProperty);
if(!parentValue)
{
// For Read/Write force-inherited properties, use the standard coersion pattern.
//
// The IsHitTestVisible property must be coerced false if the parent is false.
if (uiElement != null)
{
uiElement.CoerceValue(IsHitTestVisibleProperty);
}
else
{
uiElement3D.CoerceValue(IsHitTestVisibleProperty);
}
}
parentValue = (bool) parent.GetValue(IsVisibleProperty);
if(parentValue)
{
// For Read-Only force-inherited properties, use a private update method.
//
// The IsVisible property can only be true if the parent is true.
if (uiElement != null)
{
uiElement.UpdateIsVisibleCache();
}
else
{
uiElement3D.UpdateIsVisibleCache();
}
}
}
else if(contentElement != null)
{
bool parentValue = (bool) parent.GetValue(IsEnabledProperty);
if(!parentValue)
{
// The IsEnabled property must be coerced false if the parent is false.
contentElement.CoerceValue(IsEnabledProperty);
}
}
}
// This is called from the force-inherit property changed events.
internal static void InvalidateForceInheritPropertyOnChildren(Visual v, DependencyProperty property)
{
int cChildren = v.InternalVisual2DOr3DChildrenCount;
for (int iChild = 0; iChild < cChildren; iChild++)
{
DependencyObject child = v.InternalGet2DOr3DVisualChild(iChild);
Visual vChild = child as Visual;
if (vChild != null)
{
UIElement element = vChild as UIElement;
if (element != null)
{
if(property == IsVisibleProperty)
{
// For Read-Only force-inherited properties, use
// a private update method.
element.UpdateIsVisibleCache();
}
else
{
// For Read/Write force-inherited properties, use
// the standard coersion pattern.
element.CoerceValue(property);
}
}
else
{
// We have to "walk through" non-UIElement visuals.
InvalidateForceInheritPropertyOnChildren(vChild, property);
}
}
else
{
Visual3D v3DChild = child as Visual3D;
if (v3DChild != null)
{
UIElement3D element3D = v3DChild as UIElement3D;
if(element3D != null)
{
if(property == IsVisibleProperty)
{
// For Read-Only force-inherited properties, use
// a private update method.
element3D.UpdateIsVisibleCache();
}
else
{
// For Read/Write force-inherited properties, use
// the standard coersion pattern.
element3D.CoerceValue(property);
}
}
else
{
// We have to "walk through" non-UIElement visuals.
UIElement3D.InvalidateForceInheritPropertyOnChildren(v3DChild, property);
}
}
}
}
}
#endregion
///// LAYOUT DATA /////
private Rect _finalRect;
private Size _desiredSize;
private Size _previousAvailableSize;
private IDrawingContent _drawingContent;
//right after creation all elements are Clean so go Invalidate at least one
internal ContextLayoutManager.LayoutQueue.Request MeasureRequest;
internal ContextLayoutManager.LayoutQueue.Request ArrangeRequest;
// See PersistId property
private int _persistId = 0;
///// ATTACHED STORAGE /////
// Perf analysis showed we were not using these fields enough to warrant
// bloating each instance with the field, so storage is created on-demand
// in the local store.
internal static readonly UncommonField EventHandlersStoreField = new UncommonField();
internal static readonly UncommonField InputBindingCollectionField = new UncommonField();
internal static readonly UncommonField CommandBindingCollectionField = new UncommonField();
private static readonly UncommonField