Domande semplici a cui non corrispondono risposte altrettanto semplici.
Estrapolo da un mio custom control questi tre metodi che risolvono i quesiti.
Considerazioni:
- Mi è piaciuto il VirtualizingStackPanel che rappresenta la viewport o "finestra" visibile della lista
 
- Altrettanto bello che VerticalOffset e ViewportHeight siano double così da soddisfare anche liste con elementi non omogenei
 
- Trovo folle che queste basilari informazioni non siano più facilmente accessibili direttamente dalle classi come la ListBox.
 
 
protected bool IsItemVisible(ListBox lb, int Index)
{
  if(lb.Items.Count == 0)
    return false;
  ListBoxItem lbi = lb.ItemContainerGenerator.ContainerFromIndex(0) as ListBoxItem;
  VirtualizingStackPanel vsp = VisualTreeHelper.GetParent(lbi) as VirtualizingStackPanel;
  int FirstVisibleItem = (int)vsp.VerticalOffset;
  int VisibleItemCount = (int)vsp.ViewportHeight;
  if(Index >= FirstVisibleItem && Index <= FirstVisibleItem + VisibleItemCount)
    return true;
  return false;
}
protected bool IsListScrolledUp(ListBox lb)
{
  if(lb.Items.Count == 0)
    return false;
  ListBoxItem lbi = lb.ItemContainerGenerator.ContainerFromIndex(0) as ListBoxItem;
  if(lbi == null)
  {
    Console.WriteLine("lbi is null :-(");
    return false;
  }
  VirtualizingStackPanel vsp = VisualTreeHelper.GetParent(lbi) as VirtualizingStackPanel;
  int FirstVisibleItem = (int)vsp.VerticalOffset;
  if(FirstVisibleItem > 0)
    return false;
  return true;
}
protected bool IsListScrolledDown(ListBox lb)
{
  if(lb.Items.Count == 0)
    return false;
  ListBoxItem lbi = lb.ItemContainerGenerator.ContainerFromIndex(0) as ListBoxItem;
  if(lbi == null)
  {
    Console.WriteLine("lbi is null :-(");
    return false;
  }
  VirtualizingStackPanel vsp = VisualTreeHelper.GetParent(lbi) as VirtualizingStackPanel;
  int FirstVisibleItem = (int)vsp.VerticalOffset;
  int VisibleItemCount = (int)vsp.ViewportHeight;
  if(VisibleItemCount + FirstVisibleItem == lb.Items.Count)
    return true;
  return false;
}