I was busy exploring this problem on my own for some time now, and was not satisfied with any of the answers. They both work for some extent, but, concerning Szabolcs's answer, I would like to avoid image-processing and in case of Mr.Wizard's answer there are slight problems with it during startup (see comments) and it breaks down when the scroll position is defined as a dynamic variable itself. Thus I set out to find a better solution.
The problem
First, I want to clearly state the problem (at least what my problem was originally): given a Pane that listens to a dynamic variable text, I have to update it such that whenever text changes by a controller (e.g. a button), the Pane is automatically scrolled down to the actual end of content. But I also want to keep both the manipulable scrollbar of the Pane and the possibility to manipulate the vertical scroll position pos via another external controler (e.g. a slider). Thus the resulting Pane must be updated differently, depending on whether text or pos triggers an update.
I present two solutions, the second one being the better one in my opinion. For both cases, I exploit the fact that if the initial vertical scroll position value (pos) is set large enough, Mathematica automatically finds and sets the scroll position to the end of content.
1. Injecting code in the external controller
The simpler method is to separate the different updates by assigning them to the external controllers. Here, a button is provided that updates text externally (external to the dynamic Pane object), but it also directly redraws the Pane to find the end-of-content position.
pos = 1000; (* vertical scroll position *)
c = 5; (* text update counter *)
text = "1\n2\n3333333333333333\n44";
(* note that this button explicitly contains the update[] code *)
Button["Update text", cc = c++; text = text <> "\n" <>
StringJoin@Table[ToString@cc, {RandomInteger@{1, 15}}] <> "<"; pane = update[]]
update[] := (
pos = pos + 1000;
Framed@Pane[Dynamic@text, ImageSize -> {120, 60},
Scrollbars -> {False, True},
ScrollPosition -> Dynamic[{0, pos}, (pos = Last@#) &]]
);
pane = update[]; (* initialize Pane *)
{Dynamic@pane, Dynamic@pos}
Note that updating text via the button or via the scrollbar correctly updates pane and pos. The only problem here is that one has to manually put the pane = update[] code into the controller, which is unwanted (at least in my case), as one only wants to change text via the button.
2. Separating updates via selective triggers
The beautiful answer by @Leonid provides a method to selectively separate different update methods of a dynamic expression depending on which "parent" variable triggers an update.
SetAttributes[makeTrigger, HoldAll];
makeTrigger[res_, var_, updateCode_] := Module[{varOld = var, trigger},
trigger /; varOld =!= var := (varOld = var; res = updateCode);
trigger];
pos = 1000;
c = 5;
text = "1\n2\n3333333333333333\n44";
size = {120, 60};
Button["Update text", cc = c++; text = text <> "\n" <>
StringJoin@Table[ToString@cc, {RandomInteger@{1, 15}}] <> "<";]
Row@{"Update pos: ", Slider[Dynamic@pos, {0, 200}]}
update[] := Framed@Pane[text, ImageSize -> Dynamic@size,
Scrollbars -> {False, True},
ScrollPosition -> Dynamic[{0, pos}, (pos = Last@#) &]];
(* triggers ONLY if text is changed! *)
trigger = makeTrigger[pane, text, (pos = pos + 1000; update[])];
pane = update[]; (* initialize pane *)
{With[{tr = trigger}, Dynamic[tr; pane, TrackedSymbols :> {text, pos}]], Dynamic@pos}

(The "<" character indicates the end of newly added content.)
First, a trigger is set up in the final line, that listens to whether text or pos is changed. If only pos is changed, then the resulting expression becomes Dynamic[pane], updating the vertical scroll position as required. However, if text is changed, then trigger triggers and update[] kicks in, redrawing the whole Pane object from scratch, finding and setting the correct end-of-content position.
Now it became possibly to have various controllers only updating independent variables (like text, pos, etc.) and a dependent expression that listens to the independent variables and updates according to which of them was triggered.
Printfunction? – Sjoerd C. de Vries Feb 14 '12 at 22:42