views:

3222

answers:

3

I am using a ScrollViewer as part of my Silverlight application. It has a horizontal orientation, and I would like it to appear such that only the scroll buttons appear, but not the scroll bar itself. Something like this crude ASCII rendering:

------------------------------------------------------
|   |                                            |   |
| < |                Content Here                | > |
|   |                                            |   |
------------------------------------------------------

I know I could use the templating functionality, but all the samples I've seen only change the look of all the elements, and not their raw positioning, or whether they even appear. Is it possible to do this, and could someone provide an outline of what the template might look like?

A: 

I've done something similar and the best way I found to do this was to put your content in a scroll viewer and just turn off the scrollbars. Then code your buttons to scroll the scrollviewer.

Edit: Responding to comment about no way to deal with sizing.

First off, you would build this control as a ContentControl. It should have a template defined in generic.xaml that has your button controls plus the scroll viewer. Something like:

<Canvas x:Name="root">
  <Button x:Name="left" Content="<"/>
  <Button x:Name="right" Content=">"/>
  <ScrollViewer x:Name="viewer" BorderThickness="0" VerticalScrollBarVisibility="Hidden">
    <ContentPresenter />
  </ScrollViewer>
</Canvas>

Then in your control you would need to override OnApplyTemplate:

public override void OnApplyTemplate()
{
  base.OnApplyTemplate();

  left = GetTemplateChild("left") as Button;
  left.Click += new RoutedEvent(YourHandler);
  right = GetTemplateChild("right") as Button;
  right.Click += new RoutedEvent(YourHandler);
  // position your scroll buttons here, not writing that code
  scroll = GetTemplateChild("viewer") as ScrollViewer;
  root = GetTemplateChild("root") as Canvas;

  var fe = this.Content as FrameworkElement;
  if (fe != null)
  {
    fe.SizeChanged += new SizeChangedEventHandler(fe_SizeChanged);
  }
}

void  fe_SizeChanged(object sender, SizeChangedEventArgs e)
{
  this.InvalidateMeasure();
}

protected override Size ArrangeOverride(Size finalSize)
{
  if (!double.IsInfinity(scroll.ViewportHeight))
  {
     left.Visibility = (scroll.HorizontalOffset > 0);
     right.Visibility = (scroll.HorizontalOffset < ScrollableHeight);
  }
  return base.ArrangeOverride(finalSize);
}

protected override Size MeasureOverride(Size availableSize)
{
  scroll.Measure(availableSize);
  return scroll.DesiredSize;
}

In your button click handlers you would need to (1) scroll the viewer and (2) check the new value of the HorizontalOffset to see if you need to hide or show either of the button.

Disclaimer: This code probably doesn't work as is since it was written by hand and based on a different example.

Bryant
The problem with that method is that I don't see an event I could attach to, which would notify me when the viewport has changed in such a way that the buttons need to be made visible. So in effect, I'd have to make the buttons always visible, which I don't like.
Nick
A: 

Here is another option. Override the default template for SCrollviewer and handle the buttons as PageUp/PageDown. My example below is a scrollviewer that scrolls vertically. You can easily change to to horizontal scrolling and change the handlers from PageUp/PageDown to Left and Right handlers.

<ControlTemplate TargetType="{x:Type ScrollViewer}" x:Key="ButtonOnlyScrollViewer">
        <ControlTemplate.Resources>
            <!-- Add style here for repeat button seen below -->
        </ControlTemplate.Resources>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>

            <RepeatButton Grid.Row="0"
                          Foreground="White" 
                          Background="Yellow" 
                          HorizontalAlignment="Stretch" 
                          Command="ScrollBar.PageUpCommand"
                          Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}">
            </RepeatButton>

            <ScrollContentPresenter
                CanContentScroll="{TemplateBinding CanContentScroll}"
                Grid.Row="1" 
                Content="{TemplateBinding Content}"  
                Width="{TemplateBinding Width}"
                Height="{TemplateBinding Height}" 
                Margin="{TemplateBinding Margin}"/>

            <RepeatButton Grid.Row="2" Background="Black" Foreground="White" Command="ScrollBar.PageDownCommand">
            </RepeatButton>
        </Grid>
    </ControlTemplate>
Louis
StackOverflow is being lame. My edits are not showing up. Obviously the above example is missing some info at the top of the Control Template but in the edit view it is all there. It is possible to just override the ScrollViewer template to get the button only effect.
Louis
Post it to pastebin?
Anders Rune Jensen
A: 

Hello, i found the solution here :)

http://weblogs.asp.net/fredriknormen/archive/2009/09/18/create-an-automatic-scrollable-image-slider-in-silverlight.aspx

This is made using a DispatcherTimer, really nice example :)

Fernando