Why doesn't start MeasureOverride? - wpf

I have two custom controls for displaying collections of comments and signatures. Each of these controls has a button for changing display style - show entire collection or just first 4 elements.
When I put these controls in a default WrapPanel - they work fine: expand or collapse depending on user's actions.
In my own custom WrapPanel with new ArrangeOverride function they also work fine.
Now I need WrapPanel with option "LastChildFill" (I found example here) and with custom "WrapDirection" (similar to FlowDirection). First or last element in WrapPanel have to fill all remaining space while it's more than MinWidth of the element.
So I've created third WrapPanel with custom MeasureOverride and ArrangeOverride. And it works as I want, but when I change control display style a collection inside a control changes, but MeasureOverride in my WrapPanel doesn't start until I change window size.
Update: MeasureOverride doesn't start for expanding first or last element which fills remaining space. Collapsing for this element works fine.
I don't understand what thing I have broken with custom MeasureOverride?
Update 2:
I fugured that my problem is similar to this: MeasureOverride not always called on children's property changes.
And attach MeasureOverride code.
public class WrapPanelFlowDirection : WrapPanel
{
public bool LastChildFill
{
get { return (bool)GetValue(LastChildFillProperty); }
set { SetValue(LastChildFillProperty, value); }
}
public static readonly DependencyProperty LastChildFillProperty =
DependencyProperty.Register("LastChildFill", typeof(bool), typeof(WrapPanelFlowDirection), new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.AffectsMeasure));
public WrapDirection WrapDirection
{
get { return (WrapDirection)GetValue(WrapDirectionProperty); }
set { SetValue(WrapDirectionProperty, value); }
}
public static readonly DependencyProperty WrapDirectionProperty =
DependencyProperty.Register("WrapDirection", typeof(WrapDirection), typeof(WrapPanelFlowDirection), new FrameworkPropertyMetadata(WrapDirection.LeftToRight, FrameworkPropertyMetadataOptions.AffectsMeasure));
#region Measure Override
protected override Size MeasureOverride(Size availableSize)
{
var panelSize = new Size(availableSize.Width, 0);
double minWidth = 0;
foreach (FrameworkElement child in this.InternalChildren)
{
child.Measure(availableSize);
minWidth += child.DesiredSize.Width;
}
int firstChildInLine = 0;
double currLineHeight = 0;
double currLineWidth = 0;
UIElementCollection children = this.InternalChildren;
for (int i = 0; i < children.Count; i++)
{
FrameworkElement child = children[i] as FrameworkElement;
if (child == null)
continue;
double w = 0;
if (child.MinWidth == 0)
w = child.DesiredSize.Width;
else
w = child.MinWidth;
if (currLineWidth + w <= availableSize.Width)
{
currLineWidth += w;
currLineHeight = Math.Max(currLineHeight, child.DesiredSize.Height);
}
else
{
MeasureOneLine(firstChildInLine, i - 1, ref currLineHeight, availableSize.Width);
panelSize.Height += currLineHeight;
currLineWidth = w;
currLineHeight = child.DesiredSize.Height;
if (w > availableSize.Width)
{
MeasureOneLine(i, i, ref currLineHeight, availableSize.Width);
panelSize.Height += currLineHeight;
currLineHeight = currLineWidth = 0;
firstChildInLine = i + 1;
}
else
{
firstChildInLine = i;
}
}
}
if (firstChildInLine < children.Count)
{
MeasureOneLine(firstChildInLine, children.Count - 1, ref currLineHeight, availableSize.Width);
panelSize.Height += currLineHeight;
}
if (double.IsInfinity(panelSize.Width))
panelSize.Width = minWidth;
return panelSize;
}
private void MeasureOneLine(int start, int end, ref double height, double totalWidth)
{
UIElementCollection children = this.InternalChildren;
if (WrapDirection == WrapDirection.LeftToRight)
{
for (int i = start; i < end; i++)
{
FrameworkElement child = children[i] as FrameworkElement;
if (child == null)
continue;
double w = 0;
if (child.MinWidth == 0)
w = child.DesiredSize.Width;
else
w = child.MinWidth;
if (i < end)
{
child.Measure(new Size(w, height));
totalWidth -= w;
}
else
{
child.Measure(new Size(totalWidth, double.PositiveInfinity));
height = Math.Max(height, child.DesiredSize.Height);
}
}
}
else
{
for (int i = end; i >= start; i--)
{
FrameworkElement child = children[i] as FrameworkElement;
if (child == null)
continue;
double w = 0;
if (child.MinWidth == 0)
w = child.DesiredSize.Width;
else
w = child.MinWidth;
if (i > start)
{
child.Measure(new Size(w, height));
totalWidth -= w;
}
else
{
child.Measure(new Size(totalWidth, double.PositiveInfinity));
height = Math.Max(height, child.DesiredSize.Height);
}
}
}
}
#endregion

I found solution here : How does a container know when a child has called InvalidateArrange?
And it worked for me.
public static void OnPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
UIElement panel= VisualTreeHelper.GetParent(source) as UIElement;
if(panel != null)
{
panel.InvalidateMeasure();
panel.InvalidateArrange();
}
}

Related

Can WPF Listview has an element with different height that is embed in layout?

I've a listview that is dynamically populated by code.
In some case I need to put a note that has different height respect other components.
My goal is to have Note and listview items wrap all around, like in Word with image and Wrap Text set to "Square"
I tried to use UniformGrid and WrapPanel.
I tried to understand how write my own WrapPanel, but I don't find a way to solve it.
I think that I can have an external component for Note and leave listview items that are under Note like "Hidden"
<ListView x:Name="PluginListView"
HorizontalAlignment="Left"
VerticalAlignment="Bottom"
SelectionMode="Single"
VerticalContentAlignment="Top"
BorderThickness="0"
Margin="0,10,0,0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<!--<UniformGrid Columns="4" HorizontalAlignment="Stretch" Rows="10"/>-->
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ListView>
ADDED
I created a custom UniformGrid, I added a property NoteRowToSkip that rapresent the rows to skip in the last column on the left. I override ArrangeOverride and added the the skip behavior that I want.
Debugging seems that values are correct, but has the same behavior of UniformGrid.
public class UniformNoteWrapGrid : Panel
{
private int m_Rows;
private int m_Columns;
public static readonly DependencyProperty NoteRowToSkipProperty = DependencyProperty.Register(nameof(NoteRowToSkip), typeof(int), typeof(UniformGrid), new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.AffectsMeasure), ValidateNoteRowToSkip);
private static bool ValidateNoteRowToSkip(object o)
{
return (int)o >= 0;
}
public int Columns
{
get => (int)GetValue(ColumnsProperty);
set => SetValue(ColumnsProperty, value);
}
public static readonly DependencyProperty ColumnsProperty =
DependencyProperty.Register(
"Columns",
typeof(int),
typeof(UniformGrid),
new FrameworkPropertyMetadata(
0,
FrameworkPropertyMetadataOptions.AffectsMeasure),
ValidateColumns);
private static bool ValidateColumns(object o)
{
return (int)o >= 0;
}
public int Rows
{
get => (int)GetValue(RowsProperty);
set => SetValue(RowsProperty, value);
}
public static readonly DependencyProperty RowsProperty =
DependencyProperty.Register("Rows", typeof(int),
typeof(UniformGrid),
new FrameworkPropertyMetadata(
0,
FrameworkPropertyMetadataOptions.AffectsMeasure),
ValidateRows);
private static bool ValidateRows(object o)
{
return (int)o >= 0;
}
public int NoteRowToSkip
{
get => (int)GetValue(NoteRowToSkipProperty);
set => SetValue(NoteRowToSkipProperty, value);
}
protected override Size MeasureOverride(Size constraint)
{
UpdateComputedValues();
var childConstraint = new Size(constraint.Width / m_Columns, constraint.Height / m_Rows);
double maxChildDesiredWidth = 0.0;
double maxChildDesiredHeight = 0.0;
for (int i = 0, count = InternalChildren.Count; i < count; ++i)
{
var child = InternalChildren[i];
child.Measure(childConstraint);
var childDesiredSize = child.DesiredSize;
if (maxChildDesiredWidth < childDesiredSize.Width)
maxChildDesiredWidth = childDesiredSize.Width;
if (maxChildDesiredHeight < childDesiredSize.Height)
maxChildDesiredHeight = childDesiredSize.Height;
}
return new Size(maxChildDesiredWidth * m_Columns, maxChildDesiredHeight * m_Rows);
}
protected override Size ArrangeOverride(Size arrangeSize)
{
var childBounds = new Rect(0, 0, arrangeSize.Width / m_Columns, arrangeSize.Height / m_Rows);
double xStep = childBounds.Width;
double xBound = arrangeSize.Width - 1.0;
var row = 1;
var column = 1;
foreach (UIElement child in InternalChildren)
{
child.Arrange(childBounds);
if (child.Visibility == Visibility.Collapsed)
continue;
childBounds.X += xStep;
column++;
var testXBound = xBound;
if (IsCellForNote(row, column))
testXBound -= childBounds.Height;
if (!(childBounds.X >= testXBound))
continue;
childBounds.Y += childBounds.Height;
childBounds.X = 0;
row++;
column = 1;
}
return arrangeSize;
}
private bool IsCellForNote(int row, int column)
{
if (row > NoteRowToSkip)
return true;
return column != Columns;
}
private void UpdateComputedValues()
{
m_Columns = Columns;
m_Rows = Rows;
//if (FirstColumn >= m_Columns)
// FirstColumn = 0;
if (m_Rows != 0 && m_Columns != 0)
return;
int nonCollapsedCount = 0;
for (int i = 0, count = InternalChildren.Count; i < count; ++i)
{
var child = InternalChildren[i];
if (child.Visibility != Visibility.Collapsed)
nonCollapsedCount++;
}
if (nonCollapsedCount == 0)
nonCollapsedCount = 1;
if (m_Rows == 0)
{
if (m_Columns > 0)
m_Rows = (nonCollapsedCount + (m_Columns - 1)) / m_Columns;
else
{
m_Rows = (int)Math.Sqrt(nonCollapsedCount);
if ((m_Rows * m_Rows) < nonCollapsedCount)
m_Rows++;
m_Columns = m_Rows;
}
}
else if (m_Columns == 0)
m_Columns = (nonCollapsedCount + (m_Rows - 1)) / m_Rows;
}
}
Any idea in what's not working in my code?
SOLVED
Was a problem with if conditions.
The working version is below.
public class UniformNoteWrapGrid : Panel
{
private int m_Rows;
private int m_Columns;
public static readonly DependencyProperty NoteRowToSkipProperty = DependencyProperty.Register(nameof(NoteRowToSkip), typeof(int), typeof(UniformGrid), new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.AffectsMeasure), ValidateNoteRowToSkip);
private static bool ValidateNoteRowToSkip(object o)
{
return (int)o >= 0;
}
public int Columns
{
get => (int)GetValue(ColumnsProperty);
set => SetValue(ColumnsProperty, value);
}
public static readonly DependencyProperty ColumnsProperty =
DependencyProperty.Register(
"Columns",
typeof(int),
typeof(UniformGrid),
new FrameworkPropertyMetadata(
0,
FrameworkPropertyMetadataOptions.AffectsMeasure),
ValidateColumns);
private static bool ValidateColumns(object o)
{
return (int)o >= 0;
}
public int Rows
{
get => (int)GetValue(RowsProperty);
set => SetValue(RowsProperty, value);
}
public static readonly DependencyProperty RowsProperty =
DependencyProperty.Register("Rows", typeof(int),
typeof(UniformGrid),
new FrameworkPropertyMetadata(
0,
FrameworkPropertyMetadataOptions.AffectsMeasure),
ValidateRows);
private static bool ValidateRows(object o)
{
return (int)o >= 0;
}
public int NoteRowToSkip
{
get => (int)GetValue(NoteRowToSkipProperty);
set => SetValue(NoteRowToSkipProperty, value);
}
protected override Size MeasureOverride(Size constraint)
{
UpdateComputedValues();
var childConstraint = new Size(constraint.Width / m_Columns, constraint.Height / m_Rows);
double maxChildDesiredWidth = 0.0;
double maxChildDesiredHeight = 0.0;
for (int i = 0, count = InternalChildren.Count; i < count; ++i)
{
var child = InternalChildren[i];
child.Measure(childConstraint);
var childDesiredSize = child.DesiredSize;
if (maxChildDesiredWidth < childDesiredSize.Width)
maxChildDesiredWidth = childDesiredSize.Width;
if (maxChildDesiredHeight < childDesiredSize.Height)
maxChildDesiredHeight = childDesiredSize.Height;
}
return new Size(maxChildDesiredWidth * m_Columns, maxChildDesiredHeight * m_Rows);
}
protected override Size ArrangeOverride(Size arrangeSize)
{
var childBounds = new Rect(0, 0, arrangeSize.Width / m_Columns, arrangeSize.Height / m_Rows);
double xStep = childBounds.Width;
double xBound = arrangeSize.Width - 1.0;
var row = 1;
var column = 1;
foreach (UIElement child in InternalChildren)
{
child.Arrange(childBounds);
if (child.Visibility == Visibility.Collapsed)
continue;
childBounds.X += xStep;
column++;
var testXBound = xBound;
if (IsCellForNote(row, column))
testXBound -= xStep;
if (!(childBounds.X >= testXBound))
continue;
childBounds.Y += childBounds.Height;
childBounds.X = 0;
row++;
column = 1;
}
return arrangeSize;
}
private bool IsCellForNote(int row, int column)
{
if (row > NoteRowToSkip)
return false;
return column == Columns;
}
private void UpdateComputedValues()
{
m_Columns = Columns;
m_Rows = Rows;
//if (FirstColumn >= m_Columns)
// FirstColumn = 0;
if (m_Rows != 0 && m_Columns != 0)
return;
int nonCollapsedCount = 0;
for (int i = 0, count = InternalChildren.Count; i < count; ++i)
{
var child = InternalChildren[i];
if (child.Visibility != Visibility.Collapsed)
nonCollapsedCount++;
}
if (nonCollapsedCount == 0)
nonCollapsedCount = 1;
if (m_Rows == 0)
{
if (m_Columns > 0)
m_Rows = (nonCollapsedCount + (m_Columns - 1)) / m_Columns;
else
{
m_Rows = (int)Math.Sqrt(nonCollapsedCount);
if ((m_Rows * m_Rows) < nonCollapsedCount)
m_Rows++;
m_Columns = m_Rows;
}
}
else if (m_Columns == 0)
m_Columns = (nonCollapsedCount + (m_Rows - 1)) / m_Rows;
}
}

FinalSize in ArrangeOverride is coming wrong

I'm doing a custom panel, based(heriting) the WrapPanel.
I'm doing this because some UserControls I'm using in the WrapPanel don't wrap because they are able to use an "infinite" width, so a WrapPanel with a vertical orientation will never wrap.
I wanted to do one WrapPanel that ignore the width of the elements when having orientation = Vertical, and ignore the height of the elements when having an orientation = Horizontal.
Currently I've the following code:
protected override Size ArrangeOverride(Size finalSize)
{
int numberOfColumns, numberOfRows;
UIElement uiElement = InternalChildren.OfType<UIElement>().First();
if (Orientation == Orientation.Horizontal)
{
double desiredWidth = uiElement.DesiredSize.Width;
numberOfColumns = (int)Math.Round(finalSize.Width / desiredWidth);
numberOfRows = (int)Math.Ceiling(InternalChildren.Count / (double)numberOfColumns);
}
else
{
double desiredHeight = uiElement.DesiredSize.Height;
numberOfRows = (int)Math.Round(finalSize.Height / desiredHeight);
numberOfColumns = (int)Math.Ceiling(InternalChildren.Count / (double)numberOfRows);
}
int row = 0, column = 0;
Size elementSize = new Size(Math.Floor(finalSize.Width / numberOfColumns), Math.Floor(finalSize.Height / numberOfRows));
foreach (UIElement child in InternalChildren)
{
child.Arrange(new Rect(new Point(row * elementSize.Width, column * elementSize.Height), elementSize));
if (Orientation == Orientation.Horizontal)
{
column++;
if (column >= numberOfColumns)
{
column = 0;
row++;
}
}
else
{
row++;
if (row >= numberOfRows)
{
row = 0;
column++;
}
}
}
return finalSize;
}
The issue is that the finalSize that I receives is bigger than the "ActualSize" of the component(Width 2x the actualSize in fact).
What did I miss?

How to change DataGridCell value programatically

I have a DataGrid with some rows.Now I want change some cell style, eg:
|red| Hel |/red| |blue| lo |/blue| Everybody.
Here is my GetCell function.
public static T GetVisualChild<T>(Visual parent) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
var v = (Visual) VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChild<T>(v);
}
if (child != null)
{
break;
}
}
return child;
}
public static DataGridRow GetSelectedRow(this DataGrid grid)
{
return (DataGridRow) grid.ItemContainerGenerator.ContainerFromItem(grid.SelectedItem);
}
public static DataGridRow GetRow(this DataGrid grid, int index)
{
var row = (DataGridRow) grid.ItemContainerGenerator.ContainerFromIndex(index);
if (row != null) return row;
// May be virtualized, bring into view and try again.
grid.UpdateLayout();
grid.ScrollIntoView(grid.Items[index]);
row = (DataGridRow) grid.ItemContainerGenerator.ContainerFromIndex(index);
return row;
}
public static DataGridCell GetCell(this DataGrid grid, DataGridRow row, int column)
{
if (row == null) return null;
var presenter = GetVisualChild<DataGridCellsPresenter>(row);
if (presenter == null)
{
grid.ScrollIntoView(row, grid.Columns[column]);
presenter = GetVisualChild<DataGridCellsPresenter>(row);
}
var cell = (DataGridCell) presenter.ItemContainerGenerator.ContainerFromIndex(column);
if (cell == null)
{
grid.ScrollIntoView(row, grid.Columns[column]);
cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column);
}
return cell;
}
public static DataGridCell GetCell(this DataGrid grid, int row, int column)
{
DataGridRow rowContainer = grid.GetRow(row);
return grid.GetCell(rowContainer, column);
}
When I set DataGridCell content with some value by grid.GetCell(1, 0).Content = "PpppPppp", and then scroll DataGrid, the value is gone!
Why?
Even the value jumps to another cell.So strange.
I want to set the cell with style, so not use the funtion about:
(this.dataGrid1.Items[0] as DataRowView)[0] = "new value";
But:
private void SetBlockStyle(TextBlock block, Dictionary<int, int> section)
{
Brush foreground = Brushes.Red;
int start = 0;
string text = block.Text;
block.Text = "";
foreach (int i in section.Keys)
{
if (text.Length <= i)
{
break;
}
block.Inlines.Add(text.Substring(start, i - start));
start = i;
block.Inlines.Add(new Run(text.Substring(i, section[i]))
{
Foreground = foreground,
FontWeight = FontWeights.Bold
});
start += section[i];
}
if (start < text.Length)
{
block.Inlines.Add(text.Substring(start, text.Length - start));
}
}
Can you give me some advice?

Wrap panel with "kind of" two way wraping

So I have a wrappanel which is wrapped vertically. The items are added at run-time, but all those items (user controls) have different widths and because the wrappanel is wrapped vertically, it stacks them dowm and when they cover the vertical space they wrap to the next column. BUT what I need is "kind of" two way wrapping, i.e. I added a first item which is 200px in width, then I added a second item which is like 50px in width, but when I add a third item which is like 100px in width I want it to not to go to the next row, but place itself in that free spot the 50px control left there depending on that 200px control on top (that leaves a 150px space and a 100px control clearly fits). Of course when it doesn't fit, it wraps to the next row, and that's all OK.
Here's an images to clarify this (Can't upload'em here):
That's what happens:
image 1
And that's what I want:
image 2
Sorry for my english, it's not my primary language. I hope you'll understand my question.
You definitely can't use a single panel to accomplish that! You can use a stackpanel where to insert multiple and dynamic wrappanel with horizontal orientation to have "column" behavior you need
Well, I did it. Just wrote a custom wrappanel with the behavior I wanted.
Here it is:
public class TwoWayWrapPanel : Panel
{
int _rowCount = 0;
public int RowCount
{
get { return _rowCount; }
set { _rowCount = value; }
}
protected override Size MeasureOverride(Size availableSize)
{
Size resultSize = new Size(0, 0);
double columnWidth = 0;
double usedSpace = 0;
double nullX = 0;
double currentX = 0;
double currentY = 0;
bool isFirst = true;
int row = 0;
foreach (UIElement child in Children)
{
child.Measure(availableSize);
if (isFirst)
{
columnWidth = child.DesiredSize.Width;
resultSize.Width += columnWidth;
currentY += child.DesiredSize.Height;
row++;
isFirst = false;
}
else
{
if (columnWidth >= usedSpace + child.DesiredSize.Width & _rowCount > 1)
{
currentX = nullX + usedSpace;
usedSpace += child.DesiredSize.Width;
}
else
{
row++;
if (row + 1 > _rowCount | child.DesiredSize.Width > columnWidth)
{
row = 0;
currentX = nullX + columnWidth;
nullX = currentX;
usedSpace = 0;
columnWidth = child.DesiredSize.Width;
currentY = child.DesiredSize.Height;
row++;
resultSize.Width += columnWidth;
}
else
{
currentY += child.DesiredSize.Height;
currentX = nullX;
usedSpace = child.DesiredSize.Width;
}
}
}
}
return resultSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
double columnWidth = 0;
double usedSpace = 0;
double nullX = 0;
double currentX = 0;
double currentY = 0;
bool isFirst = true;
int row = 0;
foreach (UIElement child in Children)
{
//First item in the collection
if (isFirst)
{
child.Arrange(new Rect(currentX, currentY, child.DesiredSize.Width, child.DesiredSize.Height));
columnWidth = child.DesiredSize.Width;
currentY += child.DesiredSize.Height;
row++;
isFirst = false;
}
else
{
//Current item fits so place it in the same row
if (columnWidth >= usedSpace + child.DesiredSize.Width & _rowCount > 1)
{
currentX = nullX + usedSpace;
child.Arrange(new Rect(currentX, currentY, child.DesiredSize.Width, child.DesiredSize.Height));
usedSpace += child.DesiredSize.Width;
}
else
{
row++;
//The row limit is reached or the item width is greater than primary item width. Creating new column
if (row + 1 > _rowCount | child.DesiredSize.Width > columnWidth)
{
row = 0;
currentY = 0;
currentX = nullX + columnWidth;
nullX = currentX;
usedSpace = 0;
child.Arrange(new Rect(currentX, currentY, child.DesiredSize.Width, child.DesiredSize.Height));
columnWidth = child.DesiredSize.Width;
currentY += child.DesiredSize.Height;
row++;
}
//Item doesn't fit. Adding to the new row in the same column
else
{
usedSpace = 0;
currentY += child.DesiredSize.Height;
currentX = nullX;
child.Arrange(new Rect(currentX, currentY, child.DesiredSize.Width, child.DesiredSize.Height));
usedSpace += child.DesiredSize.Width;
}
}
}
}
return finalSize;
}
}

How can I animate a ScrollIntoView action in Silverlight 4?

Um... that's pretty much it.
How can I animate a ScrollIntoView action in Silverlight 4?
Edits:
I know how to animated the scrolling. I just create my own DependencyProperty and scroll the ScrollViewer when it changes. What I need to do, is set up how much it should be changed? How do I calculate that? What does ScrollIntoView actually do?
From Reflector:
public static void ScrollIntoView(this ScrollViewer viewer, FrameworkElement element, double horizontalMargin, double verticalMargin, Duration duration)
{
if (viewer == null)
{
throw new ArgumentNullException("viewer");
}
if (element == null)
{
throw new ArgumentNullException("element");
}
Rect? itemRect = element.GetBoundsRelativeTo(viewer);
if (itemRect.HasValue)
{
double verticalOffset = viewer.VerticalOffset;
double verticalDelta = 0.0;
double hostBottom = viewer.ViewportHeight;
double itemBottom = itemRect.Value.Bottom + verticalMargin;
if (hostBottom < itemBottom)
{
verticalDelta = itemBottom - hostBottom;
verticalOffset += verticalDelta;
}
double itemTop = itemRect.Value.Top - verticalMargin;
if ((itemTop - verticalDelta) < 0.0)
{
verticalOffset -= verticalDelta - itemTop;
}
double horizontalOffset = viewer.HorizontalOffset;
double horizontalDelta = 0.0;
double hostRight = viewer.ViewportWidth;
double itemRight = itemRect.Value.Right + horizontalMargin;
if (hostRight < itemRight)
{
horizontalDelta = itemRight - hostRight;
horizontalOffset += horizontalDelta;
}
double itemLeft = itemRect.Value.Left - horizontalMargin;
if ((itemLeft - horizontalDelta) < 0.0)
{
horizontalOffset -= horizontalDelta - itemLeft;
}
if (duration == TimeSpan.Zero)
{
viewer.ScrollToVerticalOffset(verticalOffset);
viewer.ScrollToHorizontalOffset(horizontalOffset);
}
else
{
Storyboard storyboard = new Storyboard();
SetVerticalOffset(viewer, viewer.VerticalOffset);
SetHorizontalOffset(viewer, viewer.HorizontalOffset);
DoubleAnimation verticalOffsetAnimation = new DoubleAnimation {
To = new double?(verticalOffset),
Duration = duration
};
DoubleAnimation horizontalOffsetAnimation = new DoubleAnimation {
To = new double?(verticalOffset),
Duration = duration
};
Storyboard.SetTarget(verticalOffsetAnimation, viewer);
Storyboard.SetTarget(horizontalOffsetAnimation, viewer);
Storyboard.SetTargetProperty(horizontalOffsetAnimation, new PropertyPath(HorizontalOffsetProperty));
Storyboard.SetTargetProperty(verticalOffsetAnimation, new PropertyPath(VerticalOffsetProperty));
storyboard.Children.Add(verticalOffsetAnimation);
storyboard.Children.Add(horizontalOffsetAnimation);
storyboard.Begin();
}
}
}

Resources