I am kind of having a hard time figuring out how to use the scrollbar.
I have created my own Canvas that displays a document. So I have a viewport (wich shows a part of the document), and a document (wich is basically another Canvas).
How do I calculate and set the parameters for the scrollbar now So that the scrollbar reflects size and postion of my two Canvases
For example:
How do I calculate size scrollbar's viewport (ScrollBar.Viewport) so that it reflects the size of my Docment and my Viewport.
What do I set for Scrollbar.Minimum and Scrollbar.Maximum
Is there maybe an example around on how to use the ScrollBar I don't want to have to reverse engineer ScrollViewer since it is pretty complex.

How do I use the ScrollBar ?
Madhukar Bhalerao
Don't create a separate canvas for the viewport. And don't stress about ScrollViewer's internal complexity.
Just use ScrollViewer, and tell it what height & width you want it to use -- it will create its own view area, with a scrollbar attached.
Example:
----------
<ScrollViewer Height="400" Width="400">
<StackPanel>
<Rectangle Height="200" Width="100" Fill="Red" />
<Rectangle Height="200" Width="200" Fill="Orange" />
<Rectangle Height="200" Width="300" Fill="Yellow" />
<Rectangle Height="200" Width="400" Fill="Green" />
</StackPanel>
</ScrollViewer>
----------
Put this in a window, and you will get a 400x400 region that scrolls, showing the "document". Here, the document is given inline, it is an 800 pixel high StackPanel.
In your case, if you had a custom document class "MyDoc" in namespace "MyApp", the code might look something like:
----------
< Mapping XmlNamespace="MyApp" ClrNamespace="MyApp" >
<Window x:Class="MyApp.Window1"
xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
xmlns:my="MyApp"
Title="Scroll My Document, Please"
>
<ScrollViewer Name="view1" Height="400" Width="400">
<my:MyDoc Name="doc1" />
</ScrollViewer>
</Window>
----------
which would create an empty MyDoc that you can refer to as "doc1" in the corresponding .xaml.cs code.
or you might simply create an empty ScrollViewer, later pointing its content to your document:
----- .xaml -----
<ScrollViewer Name="view1" Height="400" Width="400">
</ScrollViewer>
----- with .xaml.cs -----
MyDoc doc1 = new MyDoc();
...
view1.Content = doc1;
----------
-- ToolmakerSteve
Greg Beam
I agree - we need a good example of using ScrollBar directly.
I used "double.MaxValue" because
=> the current scrollbar viewportsize calculation requires the developer to supply values from 0 to INFINITY, in order to fill the track. This looks like a bug to me -- I think the implementor omitted a multiply by 2. <=
Here is the calculation needed currently, using "minimum and maximum":
public static double ViewportSizeGivenFraction(ScrollBar target, double fractionThatFits)
{
double extent = target.Maximum - target.Minimum;
// --- Calculation to match current DUBIOUS thumb math ---
if ((fractionThatFits >= 1.0))
return double.MaxValue; // Force thumb to fill track.
return (extent / (1.0 - fractionThatFits)) - extent;
}
...
...
handsOff.ViewportSize = ViewportSizeGivenFraction(handsOff, fractionThatFits);
[Here MIGHT BE the calculation,
fractionThatFits in [0,1] => ViewportSize in [Minimum,Maximum],
if the internal logic were corrected, so that developer were to give "minimum" to get zero-size thumb, and "maximum" to fill the track:
return target.Minimum + ((target.Maximum - target.Minimum) * fractionThatFits);
-or- corresponding to a somewhat more complicated change in the internal math,
to correctly handle non-zero Minimum, here is solution if developer is to give "0" for zero-size thumb, and "maximum" to fill the track, though thats rather mathematically strange when Minimum is non-zero, because the viewport would have extent "Maximum - 0", while the scrollbar itself has extent "Maximum - Minimum";
fractionThatFits in [0,1] => ViewportSize in [0,Maximum]:
return target.Maximum * fractionThatFits;
-or- put the whole burden on the internal math, so that the developer always provides [0,1] to ViewportSize, so that "1" always means "fill the track" --
in my opinion this makes more sense for scrollbars that permit non-zero Minimums:
return fractionThatFits;
]
Martin_TeroSolutions
It does not seem to work as described in the new WPF book by Chris Sells and Ian Griffith. Ian (I was told its was Ian's chapter):
"You can control the size of a scrollbar's thumb with the ViewPortSize property. This can be anywhere from 0 to the Maximum property value. If the ViewPortSize is equal to Maximum, the thumb will fill the track, and will not be moveable."
This behavior is not reproducable and does not match with the way the thumbs size is calculated internally:
Track's Length * ViewportSize / (Maximum - Minimum + ViewportSize)
It would be really cool to see an example how a ScrollBar is used directly. I supose IScrollInfo is a key players here. I really hoped to not having to reverse engineer the ScrollViewer.
P.S: It shouldnt matter but I'll mention in anyway: those Avalon Controls are supposed to be used in MFC projects as well.
cherriesh
Ah, forgive me; you are up to a much deeper project than I realized.
Have you explored modifying ScrollViewer's template to use your scrollbar If you could do that, then you wouldn't need to expend effort doing what ScrollViewer already does. You would just need a way to scale the document, and you would need the correct thumb size math.
=> NOTE: The example that follows does NOT actually make or use a custom scrollbar, so it is somewhat misnamed. I was unable to build & run a project using a custom scrollbar, even after quitting VS, and rebuilding the project. Strange. So I focused on other aspects of the question.
I don't know templates yet, but for my own learning I decided to tackle the doc scaling & thumb math. Maybe the example below will be useful to you. In it, I have a scrollbar at the top that you move to change scale. (NOTE: Its math is a bit tricky, because I wanted a LOGARITHMIC change of scale; the result is a control that usefully changes from 1/100 to 100 x).
That scrollbar changes the value of a ScaleTransform applied to a ScaledDocument, a custom ContentControl.
To determine the correct thumb math, I put a second scrollbar to the right of the scrollviewer's vertical bar. Once I got the math right, they move in tandem. HACK: I didn't know how to find out how much height was stolen from the view pane by the horizontal scrollbar at the bottom, so I hardcoded in 17 pixels for the one that is there.
For simplicity of this example, "document" is a StackPanel placed directly into the ScaledDocument.
Three files listed below. To use, make an Avalon Application, with name "_08_CustomScrollBar",
then replace the default "Window1.xaml" and "Window1.xaml.cs" files with the contents below.
Also add the "ScaledDocument.cs" file to your project.
-------------------- Window1.xaml --------------------
< Mapping XmlNamespace="local" ClrNamespace="_08_CustomScrollBar" >
<Window x:Class="_08_CustomScrollBar.Window1"
xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
xmlns:local="local"
Title="_08_CustomScrollBar"
>
<StackPanel>
<ScrollBar Name="slider" Minimum="-20" Maximum="20" Width="400"
Orientation="Horizontal" ValueChanged="slider_ValueChanged" />
<StackPanel Orientation="Horizontal" Width="420">
<ScrollViewer Name="viewer" Width="400" Height="400" Background="Azure" HorizontalScrollBarVisibility="Visible">
<local:ScaledDocument x:Name="view">
<StackPanel Name="document" Width="500" Height="1000" Background="Gray">
<TextBlock FontSize="24" Text="Start text." />
<Rectangle Height="200" Width="100" Fill="Red" />
<Rectangle Height="200" Width="200" Fill="Orange" />
<TextBlock FontSize="24" TextAlignment="Right" Text="Middle text." />
<Rectangle Height="200" Width="300" Fill="Yellow" />
<Rectangle Height="200" Width="500" Fill="Green" />
<TextBlock FontSize="24" Text="End text." />
</StackPanel>
</local:ScaledDocument>
</ScrollViewer>
<ScrollBar Name="handsOff" Height="383" VerticalAlignment="Top"/>
</StackPanel>
</StackPanel>
</Window>
-------------------- Window1.xaml.cs --------------------
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Shapes;
namespace _08_CustomScrollBar
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
public static double ViewportSizeGivenFraction(double fractionThatFits)
{
// --- Calculation to match current thumb math ---
if ((fractionThatFits >= 1.0))
return double.MaxValue; // Force thumb to fill track.
return (1.0 / (1.0 - fractionThatFits)) - 1.0;
}
private void slider_ValueChanged(object sender, RoutedEventArgs e)
{
//Slider slider = (Slider)sender;
//object valueOb = slider.GetValue(Slider.ValueProperty);
ScrollBar slider = (ScrollBar)sender;
object valueOb = slider.GetValue(ScrollBar.ValueProperty);
if (valueOb != null)
{
double value1 = (double)valueOb;
if ((view != null) & (value1 != 0.0))
view.Log10Scale = value1;
// Update thumb size wrt scaled doc size.
double unscaledDocHeight = document.Height;
if (!double.IsNaN(unscaledDocHeight))
{
double scaledDocHeight = view.Scale * unscaledDocHeight;
double bottomScrollBarHeight = 17; // HACK HACK HACK
double contentVisibleHeight = viewer.ActualHeight - bottomScrollBarHeight;
double fractionThatFits = contentVisibleHeight / scaledDocHeight;
handsOff.ViewportSize = ViewportSizeGivenFraction(fractionThatFits);
}
}
}
}
}
-------------------- ScaledDocument.cs --------------------
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace _08_CustomScrollBar
{
public class ScaledDocument : ContentControl
{
double _scale = 1.0; // 1.0 to display at original size.
public ScaledDocument()
{
}
public double Log10Scale
{
set
{
Scale = Math.Pow(10.0, value / 10);
}
}
public double Scale
{
get { return _scale; }
set
{
if ((_scale != value))
{
_scale = value;
this.LayoutTransform = (new ScaleTransform(_scale, _scale));
this.InvalidateMeasure(); // Size has (implicitly) changed.
}
}
}
}
}
-------------------- --------------------
-- ToolmakerSteve
kdlady1974
So I really need to find out how a ScrollBar itself is used. How ScrollBar.Minimum, ScrollBar.Maximum, ScrollBar.ViewPortsize, the Thumb's length, Members of IScrollInfo etc relate to each other.
ToolmakerSteve, nevertheless your example is a step in the right direction and it helped me to get the the viewport calculations right. Thank you for that. It would be interesting to see how ScrollBar.Minimum and ScrollBar.Maximum would fit into this :
return (1.0 / (1.0 - fractionThatFits)) - 1.0;
Also I find it somewhat odd how you fill the track by setring the viewport to double.MaxValue. Is that really the way it is done
A "best practise"-example on how to use the scrollbar WITHOUT the ScrollViewer would still be very nice.
I have made a little example project for us to experiment with and to show what I am up to. You can dowload it here.
gswitz
Canvas is a no-policy layout container, which means it doesn't do any smart size-to-content behavior either.
Try switching to Grid, and you may find your scenario will just work.
If this isn't the case, could you explain what you're trying to do in more detail Are you subclassing Canvas Are you doing any drawing yourself, or just host ing elements
DeepScratch
Track's Length * ViewportSize / (Maximum - Minimum + ViewportSize)
This is what Neil Cronlage (some Microsoft Avalon guy ) told me.
Morten Nielsen
Hi Fortes,
I have also same problem.
I want to associate ScrollViewer to Custom Canvas that is inherited from canvas Control of WPF.
scenario is like --- I have Custom Canvas and I am moving some element(Control,shapes) on Canvas.
I think canvas is not smartlly changing it's own size ,I think that is because I could not add scrollViewer to custom Canvas.
Is there any way to add ScrollViewer to Custom Canvas.
looking for reply.. :)