After our last installment, which was written many moons ago (literally), the only remaining thing left to do (apart from clean up the code a bit) was to implement the IScrollInfo.MakeVisible method. Somebody has requested that I address this, so I am going to attempt to fulfill that.
The idea of MakeVisible is that it is called to ensure the visiblity of one of the children in the Panel. When will this be called? Well, if you fire up the AnnoyingPanel sample that has been written in the past three chapters, and click one of the buttons, you will notice that the application will crash because of the exception we are firing from MakeVisible. Pushing the tab key will also have this result. This makes sense - when you push a button or tab to it, WPF will ensure that the button is visible so that you can see what you have tabbed to or clicked on.
So how do we implement the method? The method takes a Visual and a Rect. The Visual is the thing that we need to make visible - it will be one of the children of the Panel. The Rect is the rectangle of the Visual that needs to be visible. The return value, another Rect, is the rectangle of the Visual that you managed to make visible.
So to implement this method for our sample should be fairly simple. We will search our internal child list for the Visual to see if it is there. Based on where it is in the child list, and the size of the children, we can calculate the offset that we need to scroll to in order to have the child at the top of the viewport. Then we can set the new vertical offset, and return the rectangle we were given, since we know that the children are always smaller than the viewport.
public Rect MakeVisible(Visual visual, Rect rectangle)
for (int i = 0; i < this.InternalChildren.Count; i++)
if ((Visual)this.InternalChildren[i] == visual)
// we found the visual! Let's scroll it into view. First we need to know how big
// each child is.
Size finalSize = this.RenderSize;
Size childSize = new Size(
(finalSize.Height * 2) / this.InternalChildren.Count);
// now we can calculate the vertical offset that we need and set it
SetVerticalOffset(childSize.Height * i);
// child size is always smaller than viewport, because that is what makes the Panel
// an AnnoyingPanel.
throw new ArgumentException("Given visual is not in this Panel");
If you build this and run it then things will look the same, but now when you click or tab to a button it will be forced to the top of the viewport (or as close as it can get to there, anyway). So now we have a minimal working version of MakeVisible.
There are several issues with our implementation, and they are things to think about if you are implementing this for real in a real world application:
All of the four things above are incremental, and typically change depending on your scenario, which is why I am not being too specific about what to do or demonstrating it. IScrollInfo is an extension point designed to allow a maximum of flexibility, and there is a huge number of ways that you can use it. All that I am trying to do is let you be aware of the possibilities.
I have attached the final AnnoyingPanel.cs file for you to look at if you want all of the code. You may need to change the namespace to use it in your app, but I figure that this small implementation might be a starting point that somebody would want to use.