Created
January 10, 2012 21:12
-
-
Save anonymous/1591233 to your computer and use it in GitHub Desktop.
Comments to some WPF sourcces
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Код UIElement: | |
/// <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) { | |
// ... | |
} | |
/// <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) { | |
// ... | |
} | |
Код FrameworkElement : | |
/// <summary> | |
/// Measurement override. Implement your size-to-content logic here. | |
/// </summary> | |
/// <remarks> | |
/// MeasureOverride 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 MeasureOverride(Size availableSize) | |
/// { | |
/// foreach (UIElement child in VisualChildren) | |
/// { | |
/// child.Measure(availableSize); | |
/// availableSize.Deflate(child.DesiredSize); | |
/// } | |
/// | |
/// Size desired = ... compute sum of children's DesiredSize ...; | |
/// 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 MeasureOverride and ArrangeOverride method calls</item> | |
/// <item>Calling base.MeasureOverride 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 MeasureOverride(Size availableSize) | |
{ | |
return new Size(0,0); | |
} | |
/// <summary> | |
/// ArrangeOverride allows for the customization of the positioning of children. | |
/// </summary> | |
/// <remarks> | |
/// Element authors should override this method, call Arrange on each visible child element, | |
/// passing final size for each child element via finalSize parameter. | |
/// 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 ArrangeOverride(Size finalSize) | |
/// { | |
/// foreach (UIElement child in VisualChildren) | |
/// { | |
/// child.Arrange(new Rect(childX, childY, childFinalSize)); | |
/// } | |
/// return finalSize; //this can be another size if the panel actually takes smaller/larger then finalSize | |
/// } | |
/// ]]> | |
/// </code> | |
/// </example> | |
/// </remarks> | |
/// <param name="finalSize">The final size that element should use to arrange itself and its children.</param> | |
/// <returns>The size that element actually is going to use for rendering. If this size is not the same as finalSize | |
/// input parameter, the AlignmentX/AlignmentY properties will position the ink rect of the element | |
/// appropriately.</returns> | |
protected virtual Size ArrangeOverride(Size finalSize) | |
{ | |
return finalSize; | |
} | |
/// <summary> | |
/// Override for <seealso cref="UIElement.MeasureCore" />. | |
/// </summary> | |
protected sealed override Size MeasureCore(Size availableSize) | |
{ | |
Debug.Assert(MeasureData == null || availableSize == MeasureData.AvailableSize, "MeasureData needs to be passed down in [....] with size"); | |
// If using layout rounding, check whether rounding needs to compensate for high DPI | |
bool useLayoutRounding = this.UseLayoutRounding; | |
if (useLayoutRounding) | |
{ | |
if (!CheckFlagsAnd(VisualFlags.UseLayoutRounding)) | |
{ | |
this.SetFlags(true, VisualFlags.UseLayoutRounding); | |
} | |
} | |
//build the visual tree from styles first | |
ApplyTemplate(); | |
if (BypassLayoutPolicies) | |
{ | |
return MeasureOverride(availableSize); | |
} | |
else | |
{ | |
Thickness margin = Margin; | |
double marginWidth = margin.Left + margin.Right; | |
double marginHeight = margin.Top + margin.Bottom; | |
MeasureData measureData = MeasureData; | |
// parent size is what parent want us to be | |
Size frameworkAvailableSize = new Size( | |
Math.Max(availableSize.Width - marginWidth, 0), | |
Math.Max(availableSize.Height - marginHeight, 0)); | |
MinMax mm = new MinMax(this); | |
LayoutTransformData ltd = LayoutTransformDataField.GetValue(this); | |
{ | |
Transform layoutTransform = this.LayoutTransform; | |
// check that LayoutTransform is non-trivial | |
if (layoutTransform != null && !layoutTransform.IsIdentity) | |
{ | |
if (ltd == null) | |
{ | |
// allocate and store ltd if needed | |
ltd = new LayoutTransformData(); | |
LayoutTransformDataField.SetValue(this, ltd); | |
} | |
ltd.CreateTransformSnapshot(layoutTransform); | |
ltd.UntransformedDS = new Size(); | |
if (useLayoutRounding) | |
{ | |
ltd.TransformedUnroundedDS = new Size(); | |
} | |
} | |
else if (ltd != null) | |
{ | |
// clear ltd storage | |
ltd = null; | |
LayoutTransformDataField.ClearValue(this); | |
} | |
} | |
if (ltd != null) | |
{ | |
// Find the maximal area rectangle in local (child) space that we can fit, post-transform | |
// in the decorator's measure constraint. | |
frameworkAvailableSize = FindMaximalAreaLocalSpaceRect(ltd.Transform, frameworkAvailableSize); | |
} | |
frameworkAvailableSize.Width = Math.Max(mm.minWidth, Math.Min(frameworkAvailableSize.Width, mm.maxWidth)); | |
frameworkAvailableSize.Height = Math.Max(mm.minHeight, Math.Min(frameworkAvailableSize.Height, mm.maxHeight)); | |
// If layout rounding is enabled, round available size passed to MeasureOverride. | |
if (useLayoutRounding) | |
{ | |
frameworkAvailableSize = UIElement.RoundLayoutSize(frameworkAvailableSize, FrameworkElement.DpiScaleX, FrameworkElement.DpiScaleY); | |
} | |
// call to specific layout to measure | |
if (measureData != null) | |
{ | |
measureData.AvailableSize = frameworkAvailableSize; | |
} | |
Size desiredSize = MeasureOverride(frameworkAvailableSize); | |
if (measureData != null) | |
{ | |
// MeasureData should be treated like a parameter to Measure and thus not modified when returning from this call. | |
measureData.AvailableSize = availableSize; | |
} | |
// maximize desiredSize with user provided min size | |
desiredSize = new Size( | |
Math.Max(desiredSize.Width, mm.minWidth), | |
Math.Max(desiredSize.Height, mm.minHeight)); | |
//here is the "true minimum" desired size - the one that is | |
//for sure enough for the control to render its content. | |
Size unclippedDesiredSize = desiredSize; | |
if (ltd != null) | |
{ | |
//need to store unclipped, untransformed desired size to be able to arrange later | |
ltd.UntransformedDS = unclippedDesiredSize; | |
//transform unclipped desired size | |
Rect unclippedBoundsTransformed = Rect.Transform(new Rect(0, 0, unclippedDesiredSize.Width, unclippedDesiredSize.Height), ltd.Transform.Value); | |
unclippedDesiredSize.Width = unclippedBoundsTransformed.Width; | |
unclippedDesiredSize.Height = unclippedBoundsTransformed.Height; | |
} | |
bool clipped = false; | |
// User-specified max size starts to "clip" the control here. | |
//Starting from this point desiredSize could be smaller then actually | |
//needed to render the whole control | |
if (desiredSize.Width > mm.maxWidth) | |
{ | |
desiredSize.Width = mm.maxWidth; | |
clipped = true; | |
} | |
if (desiredSize.Height > mm.maxHeight) | |
{ | |
desiredSize.Height = mm.maxHeight; | |
clipped = true; | |
} | |
//transform desired size to layout slot space | |
if (ltd != null) | |
{ | |
Rect childBoundsTransformed = Rect.Transform(new Rect(0, 0, desiredSize.Width, desiredSize.Height), ltd.Transform.Value); | |
desiredSize.Width = childBoundsTransformed.Width; | |
desiredSize.Height = childBoundsTransformed.Height; | |
} | |
// because of negative margins, clipped desired size may be negative. | |
// need to keep it as doubles for that reason and maximize with 0 at the | |
// very last point - before returning desired size to the parent. | |
double clippedDesiredWidth = desiredSize.Width + marginWidth; | |
double clippedDesiredHeight = desiredSize.Height + marginHeight; | |
// In overconstrained scenario, parent wins and measured size of the child, | |
// including any sizes set or computed, can not be larger then | |
// available size. We will clip the guy later. | |
if (clippedDesiredWidth > availableSize.Width) | |
{ | |
clippedDesiredWidth = availableSize.Width; | |
clipped = true; | |
} | |
if (clippedDesiredHeight > availableSize.Height) | |
{ | |
clippedDesiredHeight = availableSize.Height; | |
clipped = true; | |
} | |
// Set transformed, unrounded size on layout transform, if any. | |
if (ltd != null) | |
{ | |
ltd.TransformedUnroundedDS = new Size(Math.Max(0, clippedDesiredWidth), Math.Max(0, clippedDesiredHeight)); | |
} | |
// If using layout rounding, round desired size. | |
if (useLayoutRounding) | |
{ | |
clippedDesiredWidth = UIElement.RoundLayoutValue(clippedDesiredWidth, DpiScaleX); | |
clippedDesiredHeight = UIElement.RoundLayoutValue(clippedDesiredHeight, DpiScaleY); | |
} | |
// Note: unclippedDesiredSize is needed in ArrangeCore, | |
// because due to the layout protocol, arrange should be called | |
// with constraints greater or equal to child's desired size | |
// returned from MeasureOverride. But in most circumstances | |
// it is possible to reconstruct original unclipped desired size. | |
// In such cases we want to optimize space and save 16 bytes by | |
// not storing it on each FrameworkElement. | |
// | |
// The if statement conditions below lists the cases when | |
// it is NOT possible to recalculate unclipped desired size later | |
// in ArrangeCore, thus we save it into Uncommon Fields... | |
// | |
// Note 2: use SizeBox to avoid CLR boxing of Size. | |
// measurements show it is better to allocate an object once than | |
// have spurious boxing allocations on every resize | |
SizeBox sb = UnclippedDesiredSizeField.GetValue(this); | |
if (clipped | |
|| clippedDesiredWidth < 0 | |
|| clippedDesiredHeight < 0) | |
{ | |
if (sb == null) //not yet allocated, allocate the box | |
{ | |
sb = new SizeBox(unclippedDesiredSize); | |
UnclippedDesiredSizeField.SetValue(this, sb); | |
} | |
else //we already have allocated size box, simply change it | |
{ | |
sb.Width = unclippedDesiredSize.Width; | |
sb.Height = unclippedDesiredSize.Height; | |
} | |
} | |
else | |
{ | |
if (sb != null) | |
UnclippedDesiredSizeField.ClearValue(this); | |
} | |
return new Size(Math.Max(0, clippedDesiredWidth), Math.Max(0, clippedDesiredHeight)); | |
} | |
} | |
/// <summary> | |
/// Override for <seealso cref="UIElement.ArrangeCore" />. | |
/// </summary> | |
protected sealed override void ArrangeCore(Rect finalRect) | |
{ | |
// If using layout rounding, check whether rounding needs to compensate for high DPI | |
bool useLayoutRounding = this.UseLayoutRounding; | |
LayoutTransformData ltd = LayoutTransformDataField.GetValue(this); | |
Size transformedUnroundedDS = Size.Empty; | |
if (useLayoutRounding) | |
{ | |
if (!CheckFlagsAnd(VisualFlags.UseLayoutRounding)) | |
{ | |
SetFlags(true, VisualFlags.UseLayoutRounding); | |
} | |
} | |
if (BypassLayoutPolicies) | |
{ | |
Size oldRenderSize = RenderSize; | |
Size inkSize = ArrangeOverride(finalRect.Size); | |
RenderSize = inkSize; | |
SetLayoutOffset(new Vector(finalRect.X, finalRect.Y), oldRenderSize); | |
} | |
else | |
{ | |
// If LayoutConstrained==true (parent wins in layout), | |
// we might get finalRect.Size smaller then UnclippedDesiredSize. | |
// Stricltly speaking, this may be the case even if LayoutConstrained==false (child wins), | |
// since who knows what a particualr parent panel will try to do in error. | |
// In this case we will not actually arrange a child at a smaller size, | |
// since the logic of the child does not expect to receive smaller size | |
// (if it coudl deal with smaller size, it probably would accept it in MeasureOverride) | |
// so lets replace the smaller arreange size with UnclippedDesiredSize | |
// and then clip the guy later. | |
// We will use at least UnclippedDesiredSize to compute arrangeSize of the child, and | |
// we will use layoutSlotSize to compute alignments - so the bigger child can be aligned within | |
// smaller slot. | |
// This is computed on every ArrangeCore. Depending on LayoutConstrained, actual clip may apply or not | |
NeedsClipBounds = false; | |
// Start to compute arrange size for the child. | |
// It starts from layout slot or deisred size if layout slot is smaller then desired, | |
// and then we reduce it by margins, apply Width/Height etc, to arrive at the size | |
// that child will get in its ArrangeOverride. | |
Size arrangeSize = finalRect.Size; | |
Thickness margin = Margin; | |
double marginWidth = margin.Left + margin.Right; | |
double marginHeight = margin.Top + margin.Bottom; | |
arrangeSize.Width = Math.Max(0, arrangeSize.Width - marginWidth); | |
arrangeSize.Height = Math.Max(0, arrangeSize.Height - marginHeight); | |
// First, get clipped, transformed, unrounded size. | |
if (useLayoutRounding) | |
{ | |
if (ltd != null && ltd.TransformedUnroundedDS != null) | |
{ | |
transformedUnroundedDS = ltd.TransformedUnroundedDS; | |
transformedUnroundedDS.Width = Math.Max(0, transformedUnroundedDS.Width - marginWidth); | |
transformedUnroundedDS.Height = Math.Max(0, transformedUnroundedDS.Height- marginHeight); | |
} | |
} | |
// Next, compare against unclipped, transformed size. | |
SizeBox sb = UnclippedDesiredSizeField.GetValue(this); | |
Size unclippedDesiredSize; | |
if (sb == null) | |
{ | |
unclippedDesiredSize = new Size(Math.Max(0, this.DesiredSize.Width - marginWidth), | |
Math.Max(0, this.DesiredSize.Height - marginHeight)); | |
// There is no unclipped desired size, so check against clipped, but unrounded DS. | |
if (transformedUnroundedDS != Size.Empty) | |
{ | |
unclippedDesiredSize.Width = Math.Max(transformedUnroundedDS.Width, unclippedDesiredSize.Width); | |
unclippedDesiredSize.Height = Math.Max(transformedUnroundedDS.Height, unclippedDesiredSize.Height); | |
} | |
} | |
else | |
{ | |
unclippedDesiredSize = new Size(sb.Width, sb.Height); | |
} | |
if (DoubleUtil.LessThan(arrangeSize.Width, unclippedDesiredSize.Width)) | |
{ | |
NeedsClipBounds = true; | |
arrangeSize.Width = unclippedDesiredSize.Width; | |
} | |
if (DoubleUtil.LessThan(arrangeSize.Height, unclippedDesiredSize.Height)) | |
{ | |
NeedsClipBounds = true; | |
arrangeSize.Height = unclippedDesiredSize.Height; | |
} | |
// Alignment==Stretch --> arrange at the slot size minus margins | |
// Alignment!=Stretch --> arrange at the unclippedDesiredSize | |
if (HorizontalAlignment != HorizontalAlignment.Stretch) | |
{ | |
arrangeSize.Width = unclippedDesiredSize.Width; | |
} | |
if (VerticalAlignment != VerticalAlignment.Stretch) | |
{ | |
arrangeSize.Height = unclippedDesiredSize.Height; | |
} | |
//if LayoutTransform is set, arrange at untransformed DS always | |
//alignments apply to the BoundingBox after transform | |
if (ltd != null) | |
{ | |
// Repeat the measure-time algorithm for finding a best fit local rect. | |
// This essentially implements Stretch in case of LayoutTransform | |
Size potentialArrangeSize = FindMaximalAreaLocalSpaceRect(ltd.Transform, arrangeSize); | |
arrangeSize = potentialArrangeSize; | |
// If using layout rounding, round untransformed desired size - in MeasureCore, this value is first transformed and clipped | |
// before rounding, and hence saved unrounded. | |
unclippedDesiredSize = ltd.UntransformedDS; | |
//only use max area rect if both dimensions of it are larger then | |
//desired size - replace with desired size otherwise | |
if (!DoubleUtil.IsZero(potentialArrangeSize.Width) | |
&& !DoubleUtil.IsZero(potentialArrangeSize.Height)) | |
{ | |
//Use less precise comparision - otherwise FP jitter may cause drastic jumps here | |
if (LayoutDoubleUtil.LessThan(potentialArrangeSize.Width, unclippedDesiredSize.Width) | |
|| LayoutDoubleUtil.LessThan(potentialArrangeSize.Height, unclippedDesiredSize.Height)) | |
{ | |
arrangeSize = unclippedDesiredSize; | |
} | |
} | |
//if pre-transformed into local space arrangeSize is smaller in any dimension then | |
//unclipped local DesiredSize of the element, extend the arrangeSize but | |
//remember that we potentially need to clip the result of such arrange. | |
if (DoubleUtil.LessThan(arrangeSize.Width, unclippedDesiredSize.Width)) | |
{ | |
NeedsClipBounds = true; | |
arrangeSize.Width = unclippedDesiredSize.Width; | |
} | |
if (DoubleUtil.LessThan(arrangeSize.Height, unclippedDesiredSize.Height)) | |
{ | |
NeedsClipBounds = true; | |
arrangeSize.Height = unclippedDesiredSize.Height; | |
} | |
} | |
MinMax mm = new MinMax(this); | |
//we have to choose max between UnclippedDesiredSize and Max here, because | |
//otherwise setting of max property could cause arrange at less then unclippedDS. | |
//Clipping by Max is needed to limit stretch here | |
double effectiveMaxWidth = Math.Max(unclippedDesiredSize.Width, mm.maxWidth); | |
if (DoubleUtil.LessThan(effectiveMaxWidth, arrangeSize.Width)) | |
{ | |
NeedsClipBounds = true; | |
arrangeSize.Width = effectiveMaxWidth; | |
} | |
double effectiveMaxHeight = Math.Max(unclippedDesiredSize.Height, mm.maxHeight); | |
if (DoubleUtil.LessThan(effectiveMaxHeight, arrangeSize.Height)) | |
{ | |
NeedsClipBounds = true; | |
arrangeSize.Height = effectiveMaxHeight; | |
} | |
// If using layout rounding, round size passed to children. | |
if (useLayoutRounding) | |
{ | |
arrangeSize = UIElement.RoundLayoutSize(arrangeSize, DpiScaleX, DpiScaleY); | |
} | |
Size oldRenderSize = RenderSize; | |
Size innerInkSize = ArrangeOverride(arrangeSize); | |
//Here we use un-clipped InkSize because element does not know that it is | |
//clipped by layout system and it shoudl have as much space to render as | |
//it returned from its own ArrangeOverride | |
RenderSize = innerInkSize; | |
if (useLayoutRounding) | |
{ | |
RenderSize = UIElement.RoundLayoutSize(RenderSize, DpiScaleX, DpiScaleY); | |
} | |
//clippedInkSize differs from InkSize only what MaxWidth/Height explicitly clip the | |
//otherwise good arrangement. For ex, DS<clientSize but DS>MaxWidth - in this | |
//case we should initiate clip at MaxWidth and only show Top-Left portion | |
//of the element limited by Max properties. It is Top-left because in case when we | |
//are clipped by container we also degrade to Top-Left, so we are consistent. | |
Size clippedInkSize = new Size(Math.Min(innerInkSize.Width, mm.maxWidth), | |
Math.Min(innerInkSize.Height, mm.maxHeight)); | |
if (useLayoutRounding) | |
{ | |
clippedInkSize = UIElement.RoundLayoutSize(clippedInkSize, DpiScaleX, DpiScaleY); | |
} | |
//remember we have to clip if Max properties limit the inkSize | |
NeedsClipBounds |= | |
DoubleUtil.LessThan(clippedInkSize.Width, innerInkSize.Width) | |
|| DoubleUtil.LessThan(clippedInkSize.Height, innerInkSize.Height); | |
//if LayoutTransform is set, get the "outer bounds" - the alignments etc work on them | |
if (ltd != null) | |
{ | |
Rect inkRectTransformed = Rect.Transform(new Rect(0, 0, clippedInkSize.Width, clippedInkSize.Height), ltd.Transform.Value); | |
clippedInkSize.Width = inkRectTransformed.Width; | |
clippedInkSize.Height = inkRectTransformed.Height; | |
if (useLayoutRounding) | |
{ | |
clippedInkSize = UIElement.RoundLayoutSize(clippedInkSize, DpiScaleX, DpiScaleY); | |
} | |
} | |
//Note that inkSize now can be bigger then layoutSlotSize-margin (because of layout | |
//squeeze by the parent or LayoutConstrained=true, which clips desired size in Measure). | |
// The client size is the size of layout slot decreased by margins. | |
// This is the "window" through which we see the content of the child. | |
// Alignments position ink of the child in this "window". | |
// Max with 0 is neccessary because layout slot may be smaller then unclipped desired size. | |
Size clientSize = new Size(Math.Max(0, finalRect.Width - marginWidth), | |
Math.Max(0, finalRect.Height - marginHeight)); | |
if (useLayoutRounding) | |
{ | |
clientSize = UIElement.RoundLayoutSize(clientSize, DpiScaleX, DpiScaleY); | |
} | |
//remember we have to clip if clientSize limits the inkSize | |
NeedsClipBounds |= | |
DoubleUtil.LessThan(clientSize.Width, clippedInkSize.Width) | |
|| DoubleUtil.LessThan(clientSize.Height, clippedInkSize.Height); | |
Vector offset = ComputeAlignmentOffset(clientSize, clippedInkSize); | |
offset.X += finalRect.X + margin.Left; | |
offset.Y += finalRect.Y + margin.Top; | |
// If using layout rounding, round offset. | |
if (useLayoutRounding) | |
{ | |
offset.X = UIElement.RoundLayoutValue(offset.X, DpiScaleX); | |
offset.Y = UIElement.RoundLayoutValue(offset.Y, DpiScaleY); | |
} | |
SetLayoutOffset(offset, oldRenderSize); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment