2

One great feature of Microsoft Outlook is its spacebar reading mode (with the reading pane turned on). Say there are 5 messages in your inbox and the first one is displayed. The displayed message does not entirely fit on the screen, so when you press the spacebar, that is like pagedown within the message. You hit spacebar again, and it pages down again. When you've reached the bottom of the page, and you press spacebar again, it goes to the next message.

What is a good way to do this in WPF (where the application is built using the MVVM pattern)? With MVVM, I use a bunch of DataTemplates instead of usercontrols.

Edit: I should mention that I am using a ListBox for the messages and a FlowDocumentScrollViewer for the message body.

agent-j
  • 27,335
  • 5
  • 52
  • 79
  • While @Hasan Kahn proposes a good suggestion for the command side of things, what about causing the FlowDocumentScrollViewer to scroll down a page, if possible. If it is already at the end, it should execute the command. – agent-j Jun 24 '11 at 22:23

2 Answers2

0

Use Expression Blend's KeyTrigger to invoke the Command in your view model

http://msdn.microsoft.com/en-us/library/microsoft.expression.interactivity.input.keytrigger%28v=expression.40%29.aspx

OR

Use CommandReference from MVVM Toolkit How do I associate a keypress with a DelegateCommand in Composite WPF?

Community
  • 1
  • 1
Muhammad Hasan Khan
  • 34,648
  • 16
  • 88
  • 131
0

For posterity, here's my solution to the scrolling part of the question. This code handles the space first, then, if the scroll bar is already at the bottom, it doesn't handle the KeyDown. @Hasan's recomended commend fires at that point.

internal class FlowDocumentScrollViewer2 : FlowDocumentScrollViewer
{
  private static bool PageDown<T>(T listView)
     where T : DependencyObject
  {
     var scrollViewer = GetVisualChild<ScrollViewer>(listView, null);
     var scrollBar = GetVisualChild<ScrollBar>(listView,
                                               bar => bar.Orientation == Orientation.Vertical);
     var formerOffset = scrollBar.Track.Value;
     scrollViewer.PageDown();
     scrollBar.Track.UpdateLayout();
     return formerOffset < scrollBar.Track.Value;
  }

  private static T GetVisualChild<T>(DependencyObject parent, Predicate<T> predicate)
     where T : Visual
  {
     T child = default(T);
     int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
     for (int i = 0; i < numVisuals; i++)
     {
        Visual v = (Visual) VisualTreeHelper.GetChild(parent, i);
        child = v as T;
        if (child == null)
        {
           child = GetVisualChild(v, predicate);
        }
        if (child != null && (predicate == null || predicate(child)))
        {
           break;
        }
     }
     return child;
  }

  public FlowDocumentScrollViewer2()
  {
     PreviewKeyDown += PreviewSpaceDown;
  }

  private void PreviewSpaceDown(object sender, KeyEventArgs e)
  {
     if (e.Handled)
        return;
     if (e.Key == Key.Space)
     {
        e.Handled = PageDown(this);
     }
  }
}
agent-j
  • 27,335
  • 5
  • 52
  • 79