|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//
//
using MS.Internal;
using MS.Internal.Interop;
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.Windows.Automation;
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;
using System.Runtime.InteropServices;
using MS.Win32;
namespace System.Windows
{
/// <summary>
/// Visibility - Enum which describes 3 possible visibility options.
/// </summary>
/// <seealso cref="UIElement" />
public enum Visibility : byte
{
/// <summary>
/// Normally visible.
/// </summary>
Visible = 0,
/// <summary>
/// Occupies space in the layout, but is not visible (completely transparent).
/// </summary>
Hidden,
/// <summary>
/// Not visible and does not occupy any space in layout, as if it doesn't exist.
/// </summary>
Collapsed
}
// PreSharp uses message numbers that the C# compiler doesn't know about.
// Disable the C# complaints, per the PreSharp documentation.
#pragma warning disable 1634, 1691
/// <summary>
/// UIElement is the base class for frameworks building on the Windows Presentation Core.
/// </summary>
/// <remarks>
/// 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.<para/>
///
/// UIElement is the most functional type in the Windows Presentation Core.
/// </remarks>
[UidProperty("Uid")]
public partial class UIElement : Visual, IInputElement, IAnimatable
{
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)));
RenderOptions.ClearTypeHintProperty.OverrideMetadata(
typeof(UIElement),
new UIPropertyMetadata(new PropertyChangedCallback(ClearTypeHint_Changed)));
TextOptionsInternal.TextHintingModeProperty.OverrideMetadata(
typeof(UIElement),
new UIPropertyMetadata(new PropertyChangedCallback(TextHintingMode_Changed)));
EventManager.RegisterClassHandler(typeof(UIElement), ManipulationStartingEvent, new EventHandler<ManipulationStartingEventArgs>(OnManipulationStartingThunk));
EventManager.RegisterClassHandler(typeof(UIElement), ManipulationStartedEvent, new EventHandler<ManipulationStartedEventArgs>(OnManipulationStartedThunk));
EventManager.RegisterClassHandler(typeof(UIElement), ManipulationDeltaEvent, new EventHandler<ManipulationDeltaEventArgs>(OnManipulationDeltaThunk));
EventManager.RegisterClassHandler(typeof(UIElement), ManipulationInertiaStartingEvent, new EventHandler<ManipulationInertiaStartingEventArgs>(OnManipulationInertiaStartingThunk));
EventManager.RegisterClassHandler(typeof(UIElement), ManipulationBoundaryFeedbackEvent, new EventHandler<ManipulationBoundaryFeedbackEventArgs>(OnManipulationBoundaryFeedbackThunk));
EventManager.RegisterClassHandler(typeof(UIElement), ManipulationCompletedEvent, new EventHandler<ManipulationCompletedEventArgs>(OnManipulationCompletedThunk));
}
/// <summary>
/// Constructor. This form of constructor will encounter a slight perf hit since it needs to initialize Dispatcher for the instance.
/// </summary>
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.
if (EventTrace.IsEnabled(EventTrace.Keyword.KeywordGeneral, EventTrace.Level.Verbose))
{
PerfService.GetPerfElementID(this);
}
}
#region AllowDrop
/// <summary>
/// The DependencyProperty for the AllowDrop property.
/// </summary>
public static readonly DependencyProperty AllowDropProperty =
DependencyProperty.Register(
"AllowDrop",
typeof(bool),
typeof(UIElement),
new PropertyMetadata(BooleanBoxes.FalseBox));
/// <summary>
/// A dependency property that allows the drop object as DragDrop target.
/// </summary>
public bool AllowDrop
{
get { return (bool) GetValue(AllowDropProperty); }
set { SetValue(AllowDropProperty, BooleanBoxes.Box(value)); }
}
#endregion AllowDrop
/// <summary>
/// Get the StylusPlugInCollection associated with the UIElement
/// </summary>
protected StylusPlugInCollection StylusPlugIns
{
get
{
StylusPlugInCollection stylusCollection = StylusPlugInsField.GetValue(this);
if (stylusCollection == null)
{
stylusCollection = new StylusPlugInCollection(this);
StylusPlugInsField.SetValue(this, stylusCollection);
}
return stylusCollection;
}
}
/// <summary>
/// Returns the size the element computed during the Measure pass.
/// This is only valid if IsMeasureValid is true.
/// </summary>
public Size DesiredSize
{
get
{
if(this.Visibility == Visibility.Collapsed)
return new Size(0,0);
else
return _desiredSize;
}
}
internal Size PreviousConstraint
{
get
{
return _previousAvailableSize;
}
}
// 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;
}
/// <summary>
/// Determines if the DesiredSize is valid.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
public bool IsMeasureValid
{
get { return !MeasureDirty; }
}
/// <summary>
/// Determines if the RenderSize and position of child elements is valid.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
public bool IsArrangeValid
{
get { return !ArrangeDirty; }
}
/// <summary>
/// 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.
/// </summary>
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);
if (EventTrace.IsEnabled(EventTrace.Keyword.KeywordLayout, EventTrace.Level.Verbose))
{
// Knowing when the layout queue goes from clean to dirty is interesting.
if (ContextLayoutManager.MeasureQueue.IsEmpty)
{
EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientLayoutInvalidated, EventTrace.Keyword.KeywordLayout, EventTrace.Level.Verbose, PerfService.GetPerfElementID(this));
}
}
ContextLayoutManager.MeasureQueue.Add(this);
}
MeasureDirty = true;
}
}
/// <summary>
/// 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.
/// </summary>
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;
}
}
/// <summary>
/// Invalidates the rendering of the element.
/// Causes <see cref="System.Windows.UIElement.OnRender"/> to be called at a later time.
/// </summary>
public void InvalidateVisual()
{
InvalidateArrange();
RenderingInvalidated = true;
}
/// <summary>
/// Notification that is called by Measure of a child when
/// it ends up with different desired size for the child.
/// </summary>
/// <remarks>
/// Default implementation simply calls invalidateMeasure(), assuming that layout of a
/// parent should be updated after child changed its size.<para/>
/// 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.
/// </remarks>
protected virtual void OnChildDesiredSizeChanged(UIElement child)
{
if(IsMeasureValid)
{
InvalidateMeasure();
}
}
/// <summary>
/// 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.
/// </summary>
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);
}
}
/// <summary>
/// Recursively propagates IsLayoutSuspended flag down to the whole v's sub tree.
/// </summary>
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;
// 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);
}
}
}
/// <summary>
/// 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.
/// </summary>
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);
}
}
}
/// <summary>
/// Updates DesiredSize of the UIElement. Must be called by parents from theor MeasureCore, to form recursive update.
/// This is first pass of layout update.
/// </summary>
/// <remarks>
/// Measure is called by parents on their children. Internally, Measure calls MeasureCore override on the same object,
/// giving it opportunity to compute its DesiredSize.<para/>
/// This method will return immediately if child is not Dirty, previously measured
/// and availableSize is the same as cached. <para/>
/// This method also resets the IsMeasureinvalid bit on the child.<para/>
/// In case when "unbounded measure to content" is needed, parent can use availableSize
/// as double.PositiveInfinity. Any returned size is OK in this case.
/// </remarks>
/// <param name="availableSize">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...</param>
public void Measure(Size availableSize)
{
bool etwTracingEnabled = false;
long perfElementID = 0;
ContextLayoutManager ContextLayoutManager = ContextLayoutManager.From(Dispatcher);
if (ContextLayoutManager.AutomationEvents.Count != 0)
UIElementHelper.InvalidateAutomationAncestors(this);
if (EventTrace.IsEnabled(EventTrace.Keyword.KeywordLayout, EventTrace.Level.Verbose))
{
perfElementID = PerfService.GetPerfElementID(this);
etwTracingEnabled = true;
EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientMeasureElementBegin, EventTrace.Keyword.KeywordLayout, EventTrace.Level.Verbose, perfElementID, availableSize.Width, availableSize.Height);
}
try
{
// 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 (double.IsNaN(availableSize.Width) || double.IsNaN(availableSize.Height))
throw new InvalidOperationException(SR.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 = 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;
}
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);
gotException = false;
}
finally
{
// reset measure in progress
MeasureInProgress = false;
_previousAvailableSize = availableSize;
layoutManager.ExitMeasure();
if (gotException)
{
// we don't want to reset last exception element on layoutManager if it's been already set.
if (layoutManager.GetLastExceptionElement() == null)
{
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.Format(SR.UIElement_Layout_PositiveInfinityReturned, this.GetType().FullName));
//enforce that MeasureCore can not return NaN size .
if (double.IsNaN(desiredSize.Width) || double.IsNaN(desiredSize.Height))
throw new InvalidOperationException(SR.Format(SR.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);
}
}
}
finally
{
if (etwTracingEnabled == true)
{
EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientMeasureElementEnd, EventTrace.Keyword.KeywordLayout, EventTrace.Level.Verbose, perfElementID, _desiredSize.Width, _desiredSize.Height);
}
}
}
//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;
}
/// <summary>
/// Parents or system call this method to arrange the internals of children on a second pass of layout update.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
/// <param name="finalRect">This is the final size and location that parent or system wants this UIElement to assume.</param>
public void Arrange(Rect finalRect)
{
bool etwTracingEnabled = false;
long perfElementID = 0;
ContextLayoutManager ContextLayoutManager = ContextLayoutManager.From(Dispatcher);
if (ContextLayoutManager.AutomationEvents.Count != 0)
UIElementHelper.InvalidateAutomationAncestors(this);
if (EventTrace.IsEnabled(EventTrace.Keyword.KeywordLayout, EventTrace.Level.Verbose))
{
perfElementID = PerfService.GetPerfElementID(this);
etwTracingEnabled = true;
EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientArrangeElementBegin, EventTrace.Keyword.KeywordLayout, EventTrace.Level.Verbose, perfElementID, finalRect.Top, finalRect.Left, finalRect.Width, finalRect.Height);
}
try
{
// 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)
|| double.IsNaN(finalRect.Width)
|| double.IsNaN(finalRect.Height)
)
{
DependencyObject parent = GetUIParent() as UIElement;
throw new InvalidOperationException(
SR.Format(
SR.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
{
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;
// If using layout rounding, round final size before calling ArrangeCore.
if (CheckFlagsAnd(VisualFlags.UseLayoutRounding))
{
DpiScale dpi = GetDpi();
finalRect = RoundLayoutRect(finalRect, dpi.DpiScaleX, dpi.DpiScaleY);
}
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)
{
// we don't want to reset last exception element on layoutManager if it's been already set.
if (layoutManager.GetLastExceptionElement() == null)
{
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
{
bool etwGeneralEnabled = EventTrace.IsEnabled(EventTrace.Keyword.KeywordGraphics | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Verbose);
if (etwGeneralEnabled == true)
{
EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientOnRenderBegin, EventTrace.Keyword.KeywordGraphics | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Verbose, perfElementID);
}
try
{
OnRender(dc);
}
finally
{
if (etwGeneralEnabled == true)
{
EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientOnRenderEnd, EventTrace.Keyword.KeywordGraphics | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Verbose, perfElementID);
}
}
}
finally
{
dc.Close();
RenderingInvalidated = false;
}
updatePixelSnappingGuidelines();
}
if (firstArrange)
{
EndPropertyInitialization();
}
}
}
}
finally
{
if (etwTracingEnabled == true)
{
EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientArrangeElementEnd, EventTrace.Keyword.KeywordLayout, EventTrace.Level.Verbose, perfElementID, finalRect.Top, finalRect.Left, finalRect.Width, finalRect.Height);
}
}
}
/// <summary>
/// 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.
/// </summary>
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,
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;
}
/// <summary>
/// Rounds a size to integer values for layout purposes, compensating for high DPI screen coordinates.
/// </summary>
/// <param name="size">Input size.</param>
/// <param name="dpiScaleX">DPI along x-dimension.</param>
/// <param name="dpiScaleY">DPI along y-dimension.</param>
/// <returns>Value of size that will be rounded under screen DPI.</returns>
/// <remarks>This is a layout helper method. It takes DPI into account and also does not return
/// the rounded value if it is unacceptable for layout, e.g. Infinity or NaN. It's a helper associated with
/// UseLayoutRounding property and should not be used as a general rounding utility.</remarks>
internal static Size RoundLayoutSize(Size size, double dpiScaleX, double dpiScaleY)
{
return new Size(RoundLayoutValue(size.Width, dpiScaleX), RoundLayoutValue(size.Height, dpiScaleY));
}
/// <summary>
/// Calculates the value to be used for layout rounding at high DPI.
/// </summary>
/// <param name="value">Input value to be rounded.</param>
/// <param name="dpiScale">Ratio of screen's DPI to layout DPI</param>
/// <returns>Adjusted value that will produce layout rounding on screen at high dpi.</returns>
/// <remarks>This is a layout helper method. It takes DPI into account and also does not return
/// the rounded value if it is unacceptable for layout, e.g. Infinity or NaN. It's a helper associated with
/// UseLayoutRounding property and should not be used as a general rounding utility.</remarks>
internal static double RoundLayoutValue(double value, double dpiScale)
{
double newValue;
// If DPI == 1, don't use DPI-aware rounding.
if (!DoubleUtil.AreClose(dpiScale, 1.0))
{
newValue = Math.Round(value * dpiScale) / dpiScale;
// If rounding produces a value unacceptable to layout (NaN, Infinity or MaxValue), use the original value.
if (double.IsNaN(newValue) ||
Double.IsInfinity(newValue) ||
DoubleUtil.AreClose(newValue, Double.MaxValue))
{
newValue = value;
}
}
else
{
newValue = Math.Round(value);
}
return newValue;
}
/// <summary>
/// If layout rounding is in use, rounds the size and offset of a rect.
/// </summary>
/// <param name="rect">Rect to be rounded.</param>
/// <param name="dpiScaleX">DPI along x-dimension.</param>
/// <param name="dpiScaleY">DPI along y-dimension.</param>
/// <returns>Rounded rect.</returns>
/// <remarks>This is a layout helper method. It takes DPI into account and also does not return
/// the rounded value if it is unacceptable for layout, e.g. Infinity or NaN. It's a helper associated with
/// UseLayoutRounding property and should not be used as a general rounding utility.</remarks>
internal static Rect RoundLayoutRect(Rect rect, double dpiScaleX, double dpiScaleY)
{
return new Rect(RoundLayoutValue(rect.X, dpiScaleX),
RoundLayoutValue(rect.Y, dpiScaleY),
RoundLayoutValue(rect.Width, dpiScaleX),
RoundLayoutValue(rect.Height, dpiScaleY)
);
}
/// <summary>
/// Ensures that _dpiScaleX and _dpiScaleY are set.
/// </summary>
/// <remarks>
/// Should be called before reading _dpiScaleX and _dpiScaleY
/// </remarks>
internal static DpiScale EnsureDpiScale()
{
if (_setDpi)
{
_setDpi = false;
int dpiX, dpiY;
HandleRef desktopWnd = new HandleRef(null, IntPtr.Zero);
// Win32Exception will get the Win32 error code so we don't have to
#pragma warning disable 6523
IntPtr dc = UnsafeNativeMethods.GetDC(desktopWnd);
// Detecting error case from unmanaged call, required by PREsharp to throw a Win32Exception
#pragma warning disable 6503
if (dc == IntPtr.Zero)
{
throw new Win32Exception();
}
#pragma warning restore 6503
#pragma warning restore 6523
try
{
dpiX = UnsafeNativeMethods.GetDeviceCaps(new HandleRef(null, dc), NativeMethods.LOGPIXELSX);
dpiY = UnsafeNativeMethods.GetDeviceCaps(new HandleRef(null, dc), NativeMethods.LOGPIXELSY);
_dpiScaleX = (double)dpiX / DpiUtil.DefaultPixelsPerInch;
_dpiScaleY = (double)dpiY / DpiUtil.DefaultPixelsPerInch;
}
finally
{
UnsafeNativeMethods.ReleaseDC(desktopWnd, new HandleRef(null, dc));
}
}
return new DpiScale(_dpiScaleX, _dpiScaleY);
}
/// <summary>
/// This is invoked after layout update before rendering if the element's RenderSize
/// has changed as a result of layout update.
/// </summary>
/// <param name="info">Packaged parameters (<seealso cref="SizeChangedInfo"/>, includes
/// old and new sizes and which dimension actually changes. </param>
protected internal virtual void OnRenderSizeChanged(SizeChangedInfo info)
{}
/// <summary>
/// Measurement override. Implement your size-to-content logic here.
/// </summary>
/// <remarks>
/// 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.<para/>
/// 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):
/// <example>
/// <code lang="C#">
/// <![CDATA[
///
/// protected override Size MeasureCore(Size availableSize)
/// {
/// foreach (UIElement child in ...)
/// {
/// child.Measure(availableSize);
/// availableSize.Deflate(child.DesiredSize);
/// _cache.StoreInfoAboutChild(child);
/// }
///
/// Size desired = CalculateBasedOnCache(_cache);
/// return desired;
/// }
/// ]]>
/// </code>
/// </example>
/// The key aspects of this snippet are:
/// <list type="bullet">
/// <item>You must call Measure on each child element</item>
/// <item>It is common to cache measurement information between the MeasureCore and ArrangeCore method calls</item>
/// <item>Calling base.MeasureCore is not required.</item>
/// <item>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).</item>
/// </list>
/// </remarks>
/// <param name="availableSize">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...</param>
/// <returns>Desired Size of the control, given available size passed as parameter.</returns>
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);
}
/// <summary>
/// ArrangeCore allows for the customization of the final sizing and positioning of children.
/// </summary>
/// <remarks>
/// 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):
/// <example>
/// <code lang="C#">
/// <![CDATA[
///
/// protected override Size ArrangeCore(Rect finalRect)
/// {
/// //Call base, it will set offset and _size to the finalRect:
/// base.ArrangeCore(finalRect);
///
/// foreach (UIElement child in ...)
/// {
/// child.Arrange(new Rect(childX, childY, childWidth, childHeight);
/// }
/// }
/// ]]>
/// </code>
/// </example>
/// </remarks>
/// <param name="finalRect">The final area within the parent that element should use to arrange itself and its children.</param>
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;
}
}
/// <summary>
/// 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.
/// </summary>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Size RenderSize
{
get
{
if (this.Visibility == Visibility.Collapsed)
return new Size();
else
return _size;
}
set
{
_size = value;
InvalidateHitTestBounds();
}
}
/// <summary>
/// This method returns the hit-test bounds of a UIElement to the underlying Visual layer.
/// </summary>
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;
}
/// <summary>
/// The RenderTransform dependency property.
/// </summary>
/// <seealso cref="UIElement.RenderTransform" />
[CommonDependencyProperty]
public static readonly DependencyProperty RenderTransformProperty =
DependencyProperty.Register(
"RenderTransform",
typeof(Transform),
typeof(UIElement),
new PropertyMetadata(
Transform.Identity,
new PropertyChangedCallback(RenderTransform_Changed)));
/// <summary>
/// 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.
/// </summary>
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;
}
}
}
/// <summary>
/// The RenderTransformOrigin dependency property.
/// </summary>
/// <seealso cref="UIElement.RenderTransformOrigin" />
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 ( (!double.IsNaN(v.X) && !Double.IsPositiveInfinity(v.X) && !Double.IsNegativeInfinity(v.X))
&& (!double.IsNaN(v.Y) && !Double.IsPositiveInfinity(v.Y) && !Double.IsNegativeInfinity(v.Y)));
}
/// <summary>
/// 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.
/// </summary>
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;
}
}
/// <summary>
/// OnVisualParentChanged is called when the parent of the Visual is changed.
/// </summary>
/// <param name="oldParent">Old parent or null if the Visual did not have a parent before.</param>
protected internal override void OnVisualParentChanged(DependencyObject oldParent)
{
// Synchronize ForceInherit properties
if (_parent != null)
{
DependencyObject parent = _parent;
if (parent is not UIElement and not UIElement3D)
{
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 (parent is not UIElement and not UIElement3D)
{
// 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);
}
/// <summary>
/// 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.
/// </summary>
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;
}
/// <summary>
/// Called to get the UI parent of this element when there is
/// no visual parent.
/// </summary>
/// <returns>
/// Returns a non-null value when some framework implementation
/// of this method has a non-visual parent connection,
/// </returns>
protected virtual internal DependencyObject GetUIParentCore()
{
return null;
}
/// <summary>
/// Call this method to ensure that the whoel subtree of elements that includes this UIElement
/// is properly updated.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
public void UpdateLayout()
{
// VerifyAccess();
ContextLayoutManager.From(Dispatcher).UpdateLayout();
}
internal static void BuildRouteHelper(DependencyObject e, EventRoute route, RoutedEventArgs args)
{
ArgumentNullException.ThrowIfNull(route);
ArgumentNullException.ThrowIfNull(args);
if (args.Source == null)
{
throw new ArgumentException(SR.SourceNotSet);
}
if (args.RoutedEvent != route.RoutedEvent)
{
throw new ArgumentException(SR.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.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)
{
//Add a Synchronized input pre-opportunity handler just before the class and instance handlers
uiElement.AddSynchronizedInputPreOpportunityHandler(route, args);
continuePastVisualTree = uiElement.BuildRouteCore(route, args);
// Add this element to route
uiElement.AddToEventRoute(route, args);
//Add a Synchronized input post-opportunity handler just after class and instance handlers
uiElement.AddSynchronizedInputPostOpportunityHandler(route, args);
// Get element's visual parent
e = uiElement.GetUIParent(continuePastVisualTree);
}
else if (contentElement != null)
{
//Add a Synchronized input pre-opportunity handler just before the class and instance handlers
contentElement.AddSynchronizedInputPreOpportunityHandler(route, args);
continuePastVisualTree = contentElement.BuildRouteCore(route, args);
// Add this element to route
contentElement.AddToEventRoute(route, args);
//Add a Synchronized input post-opportunity handler just after the class and instance handlers
contentElement.AddSynchronizedInputPostOpportunityHandler(route, args);
// Get element's visual parent
e = (DependencyObject)contentElement.GetUIParent(continuePastVisualTree);
}
else if (uiElement3D != null)
{
//Add a Synchronized input pre-opportunity handler just before the class and instance handlers
uiElement3D.AddSynchronizedInputPreOpportunityHandler(route, args);
continuePastVisualTree = uiElement3D.BuildRouteCore(route, args);
// Add this element to route
uiElement3D.AddToEventRoute(route, args);
//Add a Synchronized input post-opportunity handler just after the class and instance handlers
uiElement3D.AddSynchronizedInputPostOpportunityHandler(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);
}
}
}
}
// If this element is currently listening to synchronized input, add a pre-opportunity handler to keep track of event routed through this element.
internal void AddSynchronizedInputPreOpportunityHandler(EventRoute route, RoutedEventArgs args)
{
if (InputManager.IsSynchronizedInput)
{
if (SynchronizedInputHelper.IsListening(this, args))
{
RoutedEventHandler eventHandler = new RoutedEventHandler(this.SynchronizedInputPreOpportunityHandler);
SynchronizedInputHelper.AddHandlerToRoute(this, route, eventHandler, false);
}
else
{
AddSynchronizedInputPreOpportunityHandlerCore(route, args);
}
}
}
// Helper to add pre-opportunity handler for templated parent of this element in case parent is listening
// for synchronized input.
internal virtual void AddSynchronizedInputPreOpportunityHandlerCore(EventRoute route, RoutedEventArgs args)
{
}
// If this element is currently listening to synchronized input, add a handler to post process the synchronized input otherwise
// add a synchronized input pre-opportunity handler from parent if parent is listening.
internal void AddSynchronizedInputPostOpportunityHandler(EventRoute route, RoutedEventArgs args)
{
if (InputManager.IsSynchronizedInput)
{
if (SynchronizedInputHelper.IsListening(this, args))
{
RoutedEventHandler eventHandler = new RoutedEventHandler(this.SynchronizedInputPostOpportunityHandler);
SynchronizedInputHelper.AddHandlerToRoute(this, route, eventHandler, true);
}
else
{
// Add a preview handler from the parent.
SynchronizedInputHelper.AddParentPreOpportunityHandler(this, route, args);
}
}
}
// This event handler to be called before all the class & instance handlers for this element.
internal void SynchronizedInputPreOpportunityHandler(object sender, RoutedEventArgs args)
{
SynchronizedInputHelper.PreOpportunityHandler(sender, args);
}
// This event handler to be called after class & instance handlers for this element.
internal void SynchronizedInputPostOpportunityHandler(object sender, RoutedEventArgs args)
{
if (args.Handled && (InputManager.SynchronizedInputState == SynchronizedInputStates.HadOpportunity))
{
SynchronizedInputHelper.PostOpportunityHandler(sender, args);
}
}
// Called by automation peer, when called this element will be the listening element for synchronized input.
internal bool StartListeningSynchronizedInput(SynchronizedInputType inputType)
{
if(InputManager.IsSynchronizedInput)
{
return false;
}
else
{
InputManager.StartListeningSynchronizedInput(this, inputType);
return true;
}
}
// When called, input processing will return to normal mode.
internal void CancelSynchronizedInput()
{
InputManager.CancelSynchronizedInput();
}
/// <summary>
/// Adds a handler for the given attached event
/// </summary>
internal static void AddHandler(DependencyObject d, RoutedEvent routedEvent, Delegate handler)
{
ArgumentNullException.ThrowIfNull(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.Format(SR.Invalid_IInputElement, d.GetType()));
}
}
}
}
/// <summary>
/// Removes a handler for the given attached event
/// </summary>
internal static void RemoveHandler(DependencyObject d, RoutedEvent routedEvent, Delegate handler)
{
ArgumentNullException.ThrowIfNull(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.Format(SR.Invalid_IInputElement, d.GetType()));
}
}
}
}
#region LoadedAndUnloadedEvents
///<summary>
/// Initiate the processing for [Un]Loaded event broadcast starting at this node
/// </summary>
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
/// <summary>
/// Translates a point relative to this element to coordinates that
/// are relative to the specified element.
/// </summary>
/// <remarks>
/// Passing null indicates that coordinates should be relative to
/// the root element in the tree that this element belongs to.
/// </remarks>
public Point TranslatePoint(Point point, UIElement relativeTo)
{
return InputElement.TranslatePoint(point, this, relativeTo);
}
/// <summary>
/// Returns the input element within this element that is
/// at the specified coordinates relative to this element.
/// </summary>
public IInputElement InputHitTest(Point point)
{
IInputElement enabledHit;
IInputElement rawHit;
InputHitTest(point, out enabledHit, out rawHit);
return enabledHit;
}
/// <summary>
/// Returns the input element within this element that is
/// at the specified coordinates relative to this element.
/// </summary>
/// <param name="pt">
/// This is the coordinate, relative to this element, at which
/// to look for elements within this one.
/// </param>
/// <param name="enabledHit">
/// This element is the deepest enabled input element that is at the
/// specified coordinates.
/// </param>
/// <param name="rawHit">
/// This element is the deepest input element (not necessarily enabled)
/// that is at the specified coordinates.
/// </param>
internal void InputHitTest(Point pt, out IInputElement enabledHit, out IInputElement rawHit)
{
HitTestResult rawHitResult;
InputHitTest(pt, out enabledHit, out rawHit, out rawHitResult);
}
/// <summary>
/// Returns the input element within this element that is
/// at the specified coordinates relative to this element.
/// </summary>
/// <param name="pt">
/// This is the coordinate, relative to this element, at which
/// to look for elements within this one.
/// </param>
/// <param name="enabledHit">
/// This element is the deepest enabled input element that is at the
/// specified coordinates.
/// </param>
/// <param name="rawHit">
/// This element is the deepest input element (not necessarily enabled)
/// that is at the specified coordinates.
/// </param>
/// <param name="rawHitResult">
/// Visual hit test result for the rawHit element
/// </param>
internal void InputHitTest(Point pt, out IInputElement enabledHit, out IInputElement rawHit, out HitTestResult rawHitResult)
{
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;
rawHitResult = result.HitTestResult;
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);
rawHitResult = null;
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;
rawHitResult = null;
}
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;
rawHitResult = null;
}
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;
}
}
public HitTestResult HitTestResult
{
get
{
return _result;
}
}
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);
}
}
/// <summary>
/// Re-raises an event with as a different RoutedEvent.
/// </summary>
/// <remarks>
/// Only used internally. Added to support cracking generic MouseButtonDown/Up events
/// into MouseLeft/RightButtonDown/Up events.
/// </remarks>
/// <param name="args">
/// RoutedEventsArgs to re-raise with a new RoutedEvent
/// </param>
/// <param name="newEvent">
/// The new RoutedEvent to be associated with the RoutedEventArgs
/// </param>
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);
}
/// <summary>
/// Implementation of RaiseEvent.
/// Called by both the trusted and non-trusted flavors of RaiseEvent.
/// </summary>
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);
}
/// <summary>
/// A property indicating if the mouse is over this element or not.
/// </summary>
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
/// <summary>
/// Asynchronously re-evaluate the reverse-inherited properties.
/// </summary>
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);
}
if (AreAnyTouchesOver)
{
TouchDevice.ReevaluateDirectlyOver(this, oldParent, isCoreParent);
}
if (AreAnyTouchesCapturedWithin)
{
TouchDevice.ReevaluateCapturedWithin(this, oldParent, isCoreParent);
}
}
/// <summary>
/// Controls like popup want to control updating parent properties. This method
/// provides an opportunity for those controls to participate and block it.
/// </summary>
internal virtual bool BlockReverseInheritance()
{
return false;
}
/// <summary>
/// A property indicating if the mouse is over this element or not.
/// </summary>
public bool IsMouseOver
{
get
{
return ReadFlag(CoreFlags.IsMouseOverCache);
}
}
/// <summary>
/// A property indicating if the stylus is over this element or not.
/// </summary>
public bool IsStylusOver
{
get
{
return ReadFlag(CoreFlags.IsStylusOverCache);
}
}
/// <summary>
/// Indicates if Keyboard Focus is anywhere
/// within in the subtree starting at the
/// current instance
/// </summary>
public bool IsKeyboardFocusWithin
{
get
{
return ReadFlag(CoreFlags.IsKeyboardFocusWithinCache);
}
}
#endregion new
/// <summary>
/// A property indicating if the mouse is captured to this element or not.
/// </summary>
public bool IsMouseCaptured
{
get { return (bool) GetValue(IsMouseCapturedProperty); }
}
/// <summary>
/// Captures the mouse to this element.
/// </summary>
public bool CaptureMouse()
{
return Mouse.Capture(this);
}
/// <summary>
/// Releases the mouse capture.
/// </summary>
public void ReleaseMouseCapture()
{
if (Mouse.Captured == this)
{
Mouse.Capture(null);
}
}
/// <summary>
/// Indicates if mouse capture is anywhere within the subtree
/// starting at the current instance
/// </summary>
public bool IsMouseCaptureWithin
{
get
{
return ReadFlag(CoreFlags.IsMouseCaptureWithinCache);
}
}
/// <summary>
/// A property indicating if the stylus is over this element or not.
/// </summary>
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);
}
/// <summary>
/// A property indicating if the stylus is captured to this element or not.
/// </summary>
public bool IsStylusCaptured
{
get { return (bool) GetValue(IsStylusCapturedProperty); }
}
/// <summary>
/// Captures the stylus to this element.
/// </summary>
public bool CaptureStylus()
{
return Stylus.Capture(this);
}
/// <summary>
/// Releases the stylus capture.
/// </summary>
public void ReleaseStylusCapture()
{
Stylus.Capture(null);
}
/// <summary>
/// Indicates if stylus capture is anywhere within the subtree
/// starting at the current instance
/// </summary>
public bool IsStylusCaptureWithin
{
get
{
return ReadFlag(CoreFlags.IsStylusCaptureWithinCache);
}
}
/// <summary>
/// A property indicating if the keyboard is focused on this
/// element or not.
/// </summary>
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);
}
/// <summary>
/// Set a logical focus on the element. If the current keyboard focus is within the same scope move the keyboard on this element.
/// </summary>
public bool Focus()
{
if (Keyboard.Focus(this) == this)
{
// In order to show the touch keyboard we need to prompt the WinRT InputPane API.
// We only do this when the keyboard focus has changed as the keyboard focus dictates
// our current input targets for the touch and physical keyboards.
TipTsfHelper.Show(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;
}
/// <summary>
/// Request to move the focus from this element to another element
/// </summary>
/// <param name="request">Determine how to move the focus</param>
/// <returns> Returns true if focus is moved successfully. Returns false if there is no next element</returns>
public virtual bool MoveFocus(TraversalRequest request)
{
return false;
}
/// <summary>
/// Request to predict the element that should receive focus relative to this element for a
/// given direction, without actually moving focus to it.
/// </summary>
/// <param name="direction">The direction for which focus should be predicted</param>
/// <returns>
/// Returns the next element that focus should move to for a given FocusNavigationDirection.
/// Returns null if focus cannot be moved relative to this element.
/// </returns>
public virtual DependencyObject PredictFocus(FocusNavigationDirection direction)
{
return null;
}
/// <summary>
/// The access key for this element was invoked. Base implementation sets focus to the element.
/// </summary>
/// <param name="e">The arguments to the access key event</param>
protected virtual void OnAccessKey(AccessKeyEventArgs e)
{
this.Focus();
}
/// <summary>
/// A property indicating if the inptu method is enabled.
/// </summary>
public bool IsInputMethodEnabled
{
get { return (bool) GetValue(InputMethod.IsInputMethodEnabledProperty); }
}
/// <summary>
/// The Opacity property.
/// </summary>
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();
}
/// <summary>
/// Opacity applied to the rendered content of the UIElement. When set, this opacity value
/// is applied uniformly to the entire UIElement.
/// </summary>
[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;
}
}
/// <summary>
/// The OpacityMask property.
/// </summary>
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();
}
/// <summary>
/// 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.
/// </summary>
public Brush OpacityMask
{
get { return (Brush) GetValue(OpacityMaskProperty); }
set { SetValue(OpacityMaskProperty, value); }
}
private void pushOpacityMask()
{
base.VisualOpacityMask = OpacityMask;
}
/// <summary>
/// The BitmapEffect property.
/// </summary>
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();
}
/// <summary>
/// BitmapEffect applied to the rendered content of the UIElement.
/// </summary>
[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
}
/// <summary>
/// The Effect property.
/// </summary>
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();
}
/// <summary>
/// Effect applied to the rendered content of the UIElement.
/// </summary>
public Effect Effect
{
get { return (Effect) GetValue(EffectProperty); }
set { SetValue(EffectProperty, value); }
}
private void pushEffect()
{
base.VisualEffect = Effect;
}
/// <summary>
/// The BitmapEffectInput property.
/// </summary>
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);
}
/// <summary>
/// BitmapEffectInput accessor.
/// </summary>
[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);
}
private static void ClearTypeHint_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UIElement uie = (UIElement) d;
uie.pushClearTypeHint();
}
private void pushClearTypeHint()
{
base.VisualClearTypeHint = RenderOptions.GetClearTypeHint(this);
}
private static void TextHintingMode_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UIElement uie = (UIElement) d;
uie.pushTextHintingMode();
}
private void pushTextHintingMode()
{
base.VisualTextHintingMode = TextOptionsInternal.GetTextHintingMode(this);
}
/// <summary>
/// The CacheMode property.
/// </summary>
public static readonly DependencyProperty CacheModeProperty =
DependencyProperty.Register(
"CacheMode",
typeof(CacheMode),
typeof(UIElement),
new UIPropertyMetadata(new PropertyChangedCallback(OnCacheModeChanged)));
private static void OnCacheModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UIElement uie = (UIElement)d;
uie.pushCacheMode();
}
/// <summary>
/// The CacheMode specifies parameters used to create a persistent cache of the UIElement and its subtree
/// in order to increase rendering performance for content that is expensive to realize.
/// </summary>
public CacheMode CacheMode
{
get { return (CacheMode) GetValue(CacheModeProperty); }
set { SetValue(CacheModeProperty, value); }
}
private void pushCacheMode()
{
base.VisualCacheMode = CacheMode;
}
/// <summary>
/// pushVisualEffects - helper to propagate cacheMode, Opacity, OpacityMask, BitmapEffect, BitmapScalingMode and EdgeMode
/// </summary>
private void pushVisualEffects()
{
pushCacheMode();
pushOpacity();
pushOpacityMask();
pushBitmapEffect();
pushEdgeMode();
pushBitmapScalingMode();
pushClearTypeHint();
pushTextHintingMode();
}
#region Uid
/// <summary>
/// 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.
/// </summary>
static public readonly DependencyProperty UidProperty =
DependencyProperty.Register(
"Uid",
typeof(string),
typeof(UIElement),
new UIPropertyMetadata(String.Empty));
/// <summary>
/// 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.
/// </summary>
public string Uid
{
get { return (string)GetValue(UidProperty); }
set { SetValue(UidProperty, value); }
}
#endregion Uid
/// <summary>
/// The Visibility property.
/// </summary>
[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);
}
/// <summary>
/// Visibility accessor
/// </summary>
[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 */);
}
/// <summary>
/// HitTestCore implements precise hit testing against render contents
/// </summary>
protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
{
if (_drawingContent != null)
{
if (_drawingContent.HitTestPoint(hitTestParameters.HitPoint))
{
return new PointHitTestResult(this, hitTestParameters.HitPoint);
}
}
return null;
}
/// <summary>
/// HitTestCore implements precise hit testing against render contents
/// </summary>
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;
}
/// <summary>
/// Opens the DrawingVisual for rendering. The returned DrawingContext can be used to
/// render into the DrawingVisual.
/// </summary>
internal DrawingContext RenderOpen()
{
return new VisualDrawingContext(this);
}
/// <summary>
/// Called from the DrawingContext when the DrawingContext is closed.
/// </summary>
internal override void RenderClose(IDrawingContent newContent)
{
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 */);
}
_drawingContent = newContent;
//
// Mark the visual dirty on all channels and propagate
// the flags up the parent chain.
//
SetFlagsOnAllChannels(true, VisualProxyFlags.IsContentDirty);
PropagateFlags(
this,
VisualFlags.IsSubtreeDirtyForPrecompute,
VisualProxyFlags.IsSubtreeDirtyForRender);
}
/// <summary>
/// Overriding this function to release DUCE resources during Dispose and during removal of a subtree.
/// </summary>
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);
}
/// <summary>
/// Returns the bounding box of the content.
/// </summary>
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;
}
}
/// <summary>
/// WalkContent - method which walks the content (if present) and calls out to the
/// supplied DrawingContextWalker.
/// </summary>
/// <param name="walker">
/// DrawingContextWalker - the target of the calls which occur during
/// the content walk.
/// </param>
internal void WalkContent(DrawingContextWalker walker)
{
VerifyAPIReadOnly();
if (_drawingContent != null)
{
_drawingContent.WalkContent(walker);
}
}
/// <summary>
/// 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.
/// </summary>
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);
}
}
/// <summary>
/// GetDrawing - Returns the drawing content of this Visual.
/// </summary>
/// <remarks>
/// 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()
/// </remarks>
internal override DrawingGroup GetDrawing()
{
// Determine if Visual.Drawing should return mutable content
VerifyAPIReadOnly();
DrawingGroup drawingGroupContent = null;
// Convert our content to a DrawingGroup, if content exists
if (_drawingContent != null)
{
drawingGroupContent = DrawingServices.DrawingGroupFromRenderData((RenderData) _drawingContent);
}
return drawingGroupContent;
}
/// <summary>
/// This method supplies an additional (to the <seealso cref="Clip"/> property) clip geometry
/// that is used to intersect Clip in case if <seealso cref="ClipToBounds"/> property is set to "true".
/// Typcally, this is a size of layout space given to the UIElement.
/// </summary>
/// <returns>Geometry to use as additional clip if ClipToBounds=true</returns>
protected virtual Geometry GetLayoutClip(Size layoutSlotSize)
{
if(ClipToBounds)
{
RectangleGeometry rect = new RectangleGeometry(new Rect(RenderSize));
rect.Freeze();
return rect;
}
else
return null;
}
/// <summary>
/// ClipToBounds Property
/// </summary>
[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();
}
}
/// <summary>
/// ClipToBounds Property
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
public bool ClipToBounds
{
get { return ClipToBoundsCache; }
set { SetValue(ClipToBoundsProperty, BooleanBoxes.Box(value)); }
}
/// <summary>
/// Clip Property
/// </summary>
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();
}
}
/// <summary>
/// Clip Property
/// </summary>
public Geometry Clip
{
get { return (Geometry) GetValue(ClipProperty); }
set { SetValue(ClipProperty, value); }
}
/// <summary>
/// Align Property
/// </summary>
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();
}
}
/// <summary>
/// SnapsToDevicePixels Property
/// </summary>
public bool SnapsToDevicePixels
{
get { return SnapsToDevicePixelsCache; }
set { SetValue(SnapsToDevicePixelsProperty, value); }
}
/// <summary>
/// Indicates if the element has effectively focus.
/// Used by AccessKeyManager to determine starting
/// element in case of duplicate access keys. This
/// helps when the focus is delegated to some
/// other element in the tree.
/// </summary>
protected internal virtual bool HasEffectiveKeyboardFocus
{
get
{
return IsKeyboardFocused;
}
}
// Internal accessor for AccessKeyManager class
internal void InvokeAccessKey(AccessKeyEventArgs e)
{
OnAccessKey(e);
}
/// <summary>
/// GotFocus event
/// </summary>
public static readonly RoutedEvent GotFocusEvent = FocusManager.GotFocusEvent.AddOwner(typeof(UIElement));
/// <summary>
/// An event announcing that IsFocused changed to true.
/// </summary>
public event RoutedEventHandler GotFocus
{
add { AddHandler(GotFocusEvent, value); }
remove { RemoveHandler(GotFocusEvent, value); }
}
/// <summary>
/// LostFocus event
/// </summary>
public static readonly RoutedEvent LostFocusEvent = FocusManager.LostFocusEvent.AddOwner(typeof(UIElement));
/// <summary>
/// An event announcing that IsFocused changed to false.
/// </summary>
public event RoutedEventHandler LostFocus
{
add { AddHandler(LostFocusEvent, value); }
remove { RemoveHandler(LostFocusEvent, value); }
}
/// <summary>
/// The DependencyProperty for the IsFocused property.
/// </summary>
internal static readonly DependencyPropertyKey IsFocusedPropertyKey =
DependencyProperty.RegisterReadOnly(
"IsFocused",
typeof(bool),
typeof(UIElement),
new PropertyMetadata(
BooleanBoxes.FalseBox, // default value
new PropertyChangedCallback(IsFocused_Changed)));
/// <summary>
/// The DependencyProperty for IsFocused.
/// Flags: None
/// Read-Only: true
/// </summary>
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));
}
}
/// <summary>
/// This method is invoked when the IsFocused property changes to true
/// </summary>
/// <param name="e">RoutedEventArgs</param>
protected virtual void OnGotFocus(RoutedEventArgs e)
{
RaiseEvent(e);
}
/// <summary>
/// This method is invoked when the IsFocused property changes to false
/// </summary>
/// <param name="e">RoutedEventArgs</param>
protected virtual void OnLostFocus(RoutedEventArgs e)
{
RaiseEvent(e);
}
/// <summary>
/// Gettor for IsFocused Property
/// </summary>
public bool IsFocused
{
get { return (bool) GetValue(IsFocusedProperty); }
}
//*********************************************************************
#region IsEnabled Property
//*********************************************************************
/// <summary>
/// The DependencyProperty for the IsEnabled property.
/// </summary>
[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)));
/// <summary>
/// A property indicating if this element is enabled or not.
/// </summary>
public bool IsEnabled
{
get { return (bool) GetValue(IsEnabledProperty);}
set { SetValue(IsEnabledProperty, BooleanBoxes.Box(value)); }
}
/// <summary>
/// IsEnabledChanged event
/// </summary>
public event DependencyPropertyChangedEventHandler IsEnabledChanged
{
add {EventHandlersStoreAdd(IsEnabledChangedKey, value);}
remove {EventHandlersStoreRemove(IsEnabledChangedKey, value);}
}
internal static readonly EventPrivateKey IsEnabledChangedKey = new EventPrivateKey(); // Used by ContentElement
/// <summary>
/// Fetches the value that IsEnabled should be coerced to.
/// </summary>
/// <remarks>
/// This method is virtual is so that controls derived from UIElement
/// can combine additional requirements into the coersion logic.
/// <P/>
/// It is important for anyone overriding this property to also
/// call CoerceValue when any of their dependencies change.
/// </remarks>
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.
uie.InvalidateForceInheritPropertyOnChildren(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
//*********************************************************************
/// <summary>
/// The DependencyProperty for the IsHitTestVisible property.
/// </summary>
public static readonly DependencyProperty IsHitTestVisibleProperty =
DependencyProperty.Register(
"IsHitTestVisible",
typeof(bool),
typeof(UIElement),
new UIPropertyMetadata(
BooleanBoxes.TrueBox, // default value
new PropertyChangedCallback(OnIsHitTestVisibleChanged),
new CoerceValueCallback(CoerceIsHitTestVisible)));
/// <summary>
/// A property indicating if this element is hit test visible or not.
/// </summary>
public bool IsHitTestVisible
{
get { return (bool) GetValue(IsHitTestVisibleProperty); }
set { SetValue(IsHitTestVisibleProperty, BooleanBoxes.Box(value)); }
}
/// <summary>
/// IsHitTestVisibleChanged event
/// </summary>
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.
uie.InvalidateForceInheritPropertyOnChildren(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);
/// <summary>
/// The DependencyProperty for the IsVisible property.
/// </summary>
public static readonly DependencyProperty IsVisibleProperty = IsVisiblePropertyKey.DependencyProperty;
/// <summary>
/// A property indicating if this element is Visible or not.
/// </summary>
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;
}
/// <summary>
/// IsVisibleChanged event
/// </summary>
public event DependencyPropertyChangedEventHandler IsVisibleChanged
{
add {EventHandlersStoreAdd(IsVisibleChangedKey, value);}
remove {EventHandlersStoreRemove(IsVisibleChangedKey, value);}
}
internal static readonly EventPrivateKey IsVisibleChangedKey = new EventPrivateKey(); // Used by ContentElement
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
{
// What if We are the root of a VisualBrush? How can we tell?
}
}
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.
uie.InvalidateForceInheritPropertyOnChildren(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
//*********************************************************************
/// <summary>
/// The DependencyProperty for the Focusable property.
/// </summary>
[CommonDependencyProperty]
public static readonly DependencyProperty FocusableProperty =
DependencyProperty.Register(
"Focusable",
typeof(bool),
typeof(UIElement),
new UIPropertyMetadata(
BooleanBoxes.FalseBox, // default value
new PropertyChangedCallback(OnFocusableChanged)));
/// <summary>
/// Gettor and Settor for Focusable Property
/// </summary>
public bool Focusable
{
get { return (bool) GetValue(FocusableProperty); }
set { SetValue(FocusableProperty, BooleanBoxes.Box(value)); }
}
/// <summary>
/// FocusableChanged event
/// </summary>
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
//*********************************************************************
/// <summary>
/// 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
/// </summary>
protected virtual AutomationPeer OnCreateAutomationPeer()
{
if (!AccessibilitySwitches.ItemsControlDoesNotSupportAutomation)
{
AutomationNotSupportedByDefaultField.SetValue(this, true);
}
return null;
}
// work around (ItemsControl.GroupStyle doesn't show items in groups in the UIAutomation tree)
internal virtual AutomationPeer OnCreateAutomationPeerInternal() { return null; }
/// <summary>
/// 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.
/// </summary>
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
{
if (!AccessibilitySwitches.ItemsControlDoesNotSupportAutomation)
{
// work around (ItemsControl.GroupStyle doesn't show items in groups in the UIAutomation tree)
AutomationNotSupportedByDefaultField.ClearValue(this);
ap = OnCreateAutomationPeer();
// if this element returns an explicit peer (even null), use
// it. But if it returns null by reaching the default method
// above, give it a second chance to create a peer.
// [This whole dance, including the UncommonField, would be
// unnecessary once ItemsControl implements its own override
// of OnCreateAutomationPeer.]
if (ap == null && !AutomationNotSupportedByDefaultField.GetValue(this))
{
ap = OnCreateAutomationPeerInternal();
}
}
else
{
ap = OnCreateAutomationPeer();
}
if(ap != null)
{
AutomationPeerField.SetValue(this, ap);
HasAutomationPeer = true;
}
}
return ap;
}
/// <summary>
/// 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.
/// </summary>
internal AutomationPeer GetAutomationPeer()
{
VerifyAccess();
if(HasAutomationPeer)
return AutomationPeerField.GetValue(this);
return null;
}
/// <summary>
/// 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.
/// </summary>
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;
}
/// <summary>
/// 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.
/// </summary>
/// 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; }
}
/// <summary>
/// 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.
/// </summary>
/// <param name="value"></param>
/// To keep PersistId from being serialized the set has been removed from the property and a separate
/// set method has been created.
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()
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.
vChild.InvalidateForceInheritPropertyOnChildren(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.
v3DChild.InvalidateForceInheritPropertyOnChildren(property);
}
}
}
}
}
#endregion
#region Manipulation
/// <summary>
/// The dependency property for the IsManipulationEnabled property.
/// </summary>
public static readonly DependencyProperty IsManipulationEnabledProperty =
DependencyProperty.Register(
"IsManipulationEnabled",
typeof(bool),
typeof(UIElement),
new PropertyMetadata(BooleanBoxes.FalseBox, new PropertyChangedCallback(OnIsManipulationEnabledChanged)));
/// <summary>
/// Whether manipulations are enabled on this element or not.
/// </summary>
/// <remarks>
/// Handle the ManipulationStarting event to customize the behavior of manipulations.
/// Setting to false will immediately complete any current manipulation or inertia
/// on this element and raise a ManipulationCompleted event.
/// </remarks>
[CustomCategory(nameof(SR.Touch_Category))]
public bool IsManipulationEnabled
{
get
{
return (bool)GetValue(IsManipulationEnabledProperty);
}
set
{
SetValue(IsManipulationEnabledProperty, value);
}
}
private static void OnIsManipulationEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue)
{
((UIElement)d).CoerceStylusProperties();
}
else
{
Manipulation.TryCompleteManipulation((UIElement)d);
}
}
private void CoerceStylusProperties()
{
// When manipulation is enabled, disable Stylus.IsFlicksEnabled property.
// This property, when active, produces visible effects that don't apply
// and are distracting during a manipulation.
//
// Not using coercion to avoid adding the cost of doing the callback
// on all elements, even when they don't need it.
if (IsDefaultValue(this, Stylus.IsFlicksEnabledProperty))
{
SetCurrentValueInternal(Stylus.IsFlicksEnabledProperty, BooleanBoxes.FalseBox);
}
}
private static bool IsDefaultValue(DependencyObject dependencyObject, DependencyProperty dependencyProperty)
{
bool hasModifiers, isExpression, isAnimated, isCoerced, isCurrent;
BaseValueSourceInternal source = dependencyObject.GetValueSource(dependencyProperty, null, out hasModifiers, out isExpression, out isAnimated, out isCoerced, out isCurrent);
return (source == BaseValueSourceInternal.Default) && !isExpression && !isAnimated && !isCoerced;
}
/// <summary>
/// Indicates that a manipulation is about to start and allows for configuring its behavior.
/// </summary>
public static readonly RoutedEvent ManipulationStartingEvent = Manipulation.ManipulationStartingEvent.AddOwner(typeof(UIElement));
/// <summary>
/// Indicates that a manipulation is about to start and allows for configuring its behavior.
/// </summary>
[CustomCategory(nameof(SR.Touch_Category))]
public event EventHandler<ManipulationStartingEventArgs> ManipulationStarting
{
add { AddHandler(ManipulationStartingEvent, value, false); }
remove { RemoveHandler(ManipulationStartingEvent, value); }
}
private static void OnManipulationStartingThunk(object sender, ManipulationStartingEventArgs e)
{
((UIElement)sender).OnManipulationStarting(e);
}
/// <summary>
/// Indicates that a manipulation has started.
/// </summary>
protected virtual void OnManipulationStarting(ManipulationStartingEventArgs e) { }
/// <summary>
/// Indicates that a manipulation has started.
/// </summary>
public static readonly RoutedEvent ManipulationStartedEvent = Manipulation.ManipulationStartedEvent.AddOwner(typeof(UIElement));
/// <summary>
/// Indicates that a manipulation has started.
/// </summary>
[CustomCategory(nameof(SR.Touch_Category))]
public event EventHandler<ManipulationStartedEventArgs> ManipulationStarted
{
add { AddHandler(ManipulationStartedEvent, value, false); }
remove { RemoveHandler(ManipulationStartedEvent, value); }
}
private static void OnManipulationStartedThunk(object sender, ManipulationStartedEventArgs e)
{
((UIElement)sender).OnManipulationStarted(e);
}
/// <summary>
/// Indicates that a manipulation has started.
/// </summary>
protected virtual void OnManipulationStarted(ManipulationStartedEventArgs e) { }
/// <summary>
/// Provides data regarding changes to a currently occurring manipulation.
/// </summary>
public static readonly RoutedEvent ManipulationDeltaEvent = Manipulation.ManipulationDeltaEvent.AddOwner(typeof(UIElement));
/// <summary>
/// Provides data regarding changes to a currently occurring manipulation.
/// </summary>
[CustomCategory(nameof(SR.Touch_Category))]
public event EventHandler<ManipulationDeltaEventArgs> ManipulationDelta
{
add { AddHandler(ManipulationDeltaEvent, value, false); }
remove { RemoveHandler(ManipulationDeltaEvent, value); }
}
private static void OnManipulationDeltaThunk(object sender, ManipulationDeltaEventArgs e)
{
((UIElement)sender).OnManipulationDelta(e);
}
/// <summary>
/// Provides data regarding changes to a currently occurring manipulation.
/// </summary>
protected virtual void OnManipulationDelta(ManipulationDeltaEventArgs e) { }
/// <summary>
/// Allows a handler to customize the parameters of an inertia processor.
/// </summary>
public static readonly RoutedEvent ManipulationInertiaStartingEvent = Manipulation.ManipulationInertiaStartingEvent.AddOwner(typeof(UIElement));
/// <summary>
/// Allows a handler to customize the parameters of an inertia processor.
/// </summary>
[CustomCategory(nameof(SR.Touch_Category))]
public event EventHandler<ManipulationInertiaStartingEventArgs> ManipulationInertiaStarting
{
add { AddHandler(ManipulationInertiaStartingEvent, value, false); }
remove { RemoveHandler(ManipulationInertiaStartingEvent, value); }
}
private static void OnManipulationInertiaStartingThunk(object sender, ManipulationInertiaStartingEventArgs e)
{
((UIElement)sender).OnManipulationInertiaStarting(e);
}
/// <summary>
/// Allows a handler to customize the parameters of an inertia processor.
/// </summary>
protected virtual void OnManipulationInertiaStarting(ManipulationInertiaStartingEventArgs e) { }
/// <summary>
/// Allows a handler to provide feedback when a manipulation has encountered a boundary.
/// </summary>
public static readonly RoutedEvent ManipulationBoundaryFeedbackEvent = Manipulation.ManipulationBoundaryFeedbackEvent.AddOwner(typeof(UIElement));
/// <summary>
/// Allows a handler to provide feedback when a manipulation has encountered a boundary.
/// </summary>
[CustomCategory(nameof(SR.Touch_Category))]
public event EventHandler<ManipulationBoundaryFeedbackEventArgs> ManipulationBoundaryFeedback
{
add { AddHandler(ManipulationBoundaryFeedbackEvent, value, false); }
remove { RemoveHandler(ManipulationBoundaryFeedbackEvent, value); }
}
private static void OnManipulationBoundaryFeedbackThunk(object sender, ManipulationBoundaryFeedbackEventArgs e)
{
((UIElement)sender).OnManipulationBoundaryFeedback(e);
}
/// <summary>
/// Allows a handler to provide feedback when a manipulation has encountered a boundary.
/// </summary>
protected virtual void OnManipulationBoundaryFeedback(ManipulationBoundaryFeedbackEventArgs e) { }
/// <summary>
/// Indicates that a manipulation has completed.
/// </summary>
public static readonly RoutedEvent ManipulationCompletedEvent = Manipulation.ManipulationCompletedEvent.AddOwner(typeof(UIElement));
/// <summary>
/// Indicates that a manipulation has completed.
/// </summary>
[CustomCategory(nameof(SR.Touch_Category))]
public event EventHandler<ManipulationCompletedEventArgs> ManipulationCompleted
{
add { AddHandler(ManipulationCompletedEvent, value, false); }
remove { RemoveHandler(ManipulationCompletedEvent, value); }
}
private static void OnManipulationCompletedThunk(object sender, ManipulationCompletedEventArgs e)
{
((UIElement)sender).OnManipulationCompleted(e);
}
/// <summary>
/// Indicates that a manipulation has completed.
/// </summary>
protected virtual void OnManipulationCompleted(ManipulationCompletedEventArgs e) { }
#endregion
#region Touch
/// <summary>
/// A property indicating if any touch devices are over this element or not.
/// </summary>
public bool AreAnyTouchesOver
{
get { return ReadFlag(CoreFlags.TouchesOverCache); }
}
/// <summary>
/// A property indicating if any touch devices are directly over this element or not.
/// </summary>
public bool AreAnyTouchesDirectlyOver
{
get { return (bool)GetValue(AreAnyTouchesDirectlyOverProperty); }
}
/// <summary>
/// A property indicating if any touch devices are captured to elements in this subtree.
/// </summary>
public bool AreAnyTouchesCapturedWithin
{
get { return ReadFlag(CoreFlags.TouchesCapturedWithinCache); }
}
/// <summary>
/// A property indicating if any touch devices are captured to this element.
/// </summary>
public bool AreAnyTouchesCaptured
{
get { return (bool)GetValue(AreAnyTouchesCapturedProperty); }
}
/// <summary>
/// Captures the specified device to this element.
/// </summary>
/// <param name="touchDevice">The touch device to capture.</param>
/// <returns>True if capture was taken.</returns>
public bool CaptureTouch(TouchDevice touchDevice)
{
ArgumentNullException.ThrowIfNull(touchDevice);
return touchDevice.Capture(this);
}
/// <summary>
/// Releases capture from the specified touch device.
/// </summary>
/// <param name="touchDevice">The device that is captured to this element.</param>
/// <returns>true if capture was released, false otherwise.</returns>
public bool ReleaseTouchCapture(TouchDevice touchDevice)
{
ArgumentNullException.ThrowIfNull(touchDevice);
if (touchDevice.Captured == this)
{
touchDevice.Capture(null);
return true;
}
else
{
return false;
}
}
/// <summary>
/// Releases capture on any touch devices captured to this element.
/// </summary>
public void ReleaseAllTouchCaptures()
{
TouchDevice.ReleaseAllCaptures(this);
}
/// <summary>
/// The touch devices captured to this element.
/// </summary>
public IEnumerable<TouchDevice> TouchesCaptured
{
get
{
return TouchDevice.GetCapturedTouches(this, /* includeWithin = */ false);
}
}
/// <summary>
/// The touch devices captured to this element and any elements in the subtree.
/// </summary>
public IEnumerable<TouchDevice> TouchesCapturedWithin
{
get
{
return TouchDevice.GetCapturedTouches(this, /* includeWithin = */ true);
}
}
/// <summary>
/// The touch devices which are over this element and any elements in the subtree.
/// This is particularly relevant to elements which dont take capture (like Label).
/// </summary>
public IEnumerable<TouchDevice> TouchesOver
{
get
{
return TouchDevice.GetTouchesOver(this, /* includeWithin = */ true);
}
}
/// <summary>
/// The touch devices which are directly over this element.
/// This is particularly relevant to elements which dont take capture (like Label).
/// </summary>
public IEnumerable<TouchDevice> TouchesDirectlyOver
{
get
{
return TouchDevice.GetTouchesOver(this, /* includeWithin = */ false);
}
}
#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;
internal static List<double> DpiScaleXValues = new List<double>(3);
internal static List<double> DpiScaleYValues = new List<double>(3);
internal static object DpiLock = new object();
private static double _dpiScaleX = 1.0;
private static double _dpiScaleY = 1.0;
private static bool _setDpi = true;
///// 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<EventHandlersStore> EventHandlersStoreField = new UncommonField<EventHandlersStore>();
internal static readonly UncommonField<InputBindingCollection> InputBindingCollectionField = new UncommonField<InputBindingCollection>();
internal static readonly UncommonField<CommandBindingCollection> CommandBindingCollectionField = new UncommonField<CommandBindingCollection>();
private static readonly UncommonField<object> LayoutUpdatedListItemsField = new UncommonField<object>();
private static readonly UncommonField<EventHandler> LayoutUpdatedHandlersField = new UncommonField<EventHandler>();
private static readonly UncommonField<StylusPlugInCollection> StylusPlugInsField = new UncommonField<StylusPlugInCollection>();
private static readonly UncommonField<AutomationPeer> AutomationPeerField = new UncommonField<AutomationPeer>();
// This field serves to link this UIElement with another UIElement better suited to provide PositionInSet and SizeOfSet Automation properties.
private static readonly UncommonField<WeakReference<UIElement>> _positionAndSizeOfSetController = new UncommonField<WeakReference<UIElement>>();
private static readonly UncommonField<bool> AutomationNotSupportedByDefaultField = new UncommonField<bool>();
internal SizeChangedInfo sizeChangedInfo;
/// <summary>
/// Internal property to expose uncommon field _positionAndSizeOfSetController.
/// </summary>
internal UIElement PositionAndSizeOfSetController
{
get
{
UIElement element = null;
WeakReference<UIElement> wRef = _positionAndSizeOfSetController.GetValue(this);
wRef?.TryGetTarget(out element);
return element;
}
set
{
if (value != null)
{
_positionAndSizeOfSetController.SetValue(this, new WeakReference<UIElement>(value));
}
else
{
_positionAndSizeOfSetController.ClearValue(this);
}
}
}
internal bool HasAutomationPeer
{
get { return ReadFlag(CoreFlags.HasAutomationPeer); }
set { WriteFlag(CoreFlags.HasAutomationPeer, value); }
}
private bool RenderingInvalidated
{
get { return ReadFlag(CoreFlags.RenderingInvalidated); }
set { WriteFlag(CoreFlags.RenderingInvalidated, value); }
}
internal bool SnapsToDevicePixelsCache
{
get { return ReadFlag(CoreFlags.SnapsToDevicePixelsCache); }
set { WriteFlag(CoreFlags.SnapsToDevicePixelsCache, value); }
}
internal bool ClipToBoundsCache
{
get { return ReadFlag(CoreFlags.ClipToBoundsCache); }
set { WriteFlag(CoreFlags.ClipToBoundsCache, value); }
}
internal bool MeasureDirty
{
get { return ReadFlag(CoreFlags.MeasureDirty); }
set { WriteFlag(CoreFlags.MeasureDirty, value); }
}
internal bool ArrangeDirty
{
get { return ReadFlag(CoreFlags.ArrangeDirty); }
set { WriteFlag(CoreFlags.ArrangeDirty, value); }
}
internal bool MeasureInProgress
{
get { return ReadFlag(CoreFlags.MeasureInProgress); }
set { WriteFlag(CoreFlags.MeasureInProgress, value); }
}
internal bool ArrangeInProgress
{
get { return ReadFlag(CoreFlags.ArrangeInProgress); }
set { WriteFlag(CoreFlags.ArrangeInProgress, value); }
}
internal bool NeverMeasured
{
get { return ReadFlag(CoreFlags.NeverMeasured); }
set { WriteFlag(CoreFlags.NeverMeasured, value); }
}
internal bool NeverArranged
{
get { return ReadFlag(CoreFlags.NeverArranged); }
set { WriteFlag(CoreFlags.NeverArranged, value); }
}
internal bool MeasureDuringArrange
{
get { return ReadFlag(CoreFlags.MeasureDuringArrange); }
set { WriteFlag(CoreFlags.MeasureDuringArrange, value); }
}
internal bool AreTransformsClean
{
get { return ReadFlag(CoreFlags.AreTransformsClean); }
set { WriteFlag(CoreFlags.AreTransformsClean, value); }
}
internal static readonly FocusWithinProperty FocusWithinProperty = new FocusWithinProperty();
internal static readonly MouseOverProperty MouseOverProperty = new MouseOverProperty();
internal static readonly MouseCaptureWithinProperty MouseCaptureWithinProperty = new MouseCaptureWithinProperty();
internal static readonly StylusOverProperty StylusOverProperty = new StylusOverProperty();
internal static readonly StylusCaptureWithinProperty StylusCaptureWithinProperty = new StylusCaptureWithinProperty();
internal static readonly TouchesOverProperty TouchesOverProperty = new TouchesOverProperty();
internal static readonly TouchesCapturedWithinProperty TouchesCapturedWithinProperty = new TouchesCapturedWithinProperty();
private Size _size;
internal const int MAX_ELEMENTS_IN_ROUTE = 4096;
}
[Flags]
internal enum CoreFlags : uint
{
None = 0x00000000,
SnapsToDevicePixelsCache = 0x00000001,
ClipToBoundsCache = 0x00000002,
MeasureDirty = 0x00000004,
ArrangeDirty = 0x00000008,
MeasureInProgress = 0x00000010,
ArrangeInProgress = 0x00000020,
NeverMeasured = 0x00000040,
NeverArranged = 0x00000080,
MeasureDuringArrange = 0x00000100,
IsCollapsed = 0x00000200,
IsKeyboardFocusWithinCache = 0x00000400,
IsKeyboardFocusWithinChanged = 0x00000800,
IsMouseOverCache = 0x00001000,
IsMouseOverChanged = 0x00002000,
IsMouseCaptureWithinCache = 0x00004000,
IsMouseCaptureWithinChanged = 0x00008000,
IsStylusOverCache = 0x00010000,
IsStylusOverChanged = 0x00020000,
IsStylusCaptureWithinCache = 0x00040000,
IsStylusCaptureWithinChanged = 0x00080000,
HasAutomationPeer = 0x00100000,
RenderingInvalidated = 0x00200000,
IsVisibleCache = 0x00400000,
AreTransformsClean = 0x00800000,
IsOpacitySuppressed = 0x01000000,
ExistsEventHandlersStore = 0x02000000,
TouchesOverCache = 0x04000000,
TouchesOverChanged = 0x08000000,
TouchesCapturedWithinCache = 0x10000000,
TouchesCapturedWithinChanged = 0x20000000,
TouchLeaveCache = 0x40000000,
TouchEnterCache = 0x80000000,
}
}
|