Lock the scrolling in a ScrollViewer? - wpf

How can I lock vertical scrolling of a ScrollViewer by using the mouse wheel ?

If I understood you well, you wish to be able to scroll using the vertical scroll bar but not using the mouse wheel.
In this case just catch the mouse wheel event on your ScrollViewer content and mark it as handled:
<ScrollViewer>
<StackPanel MouseWheel="MyContent_MouseWheel">
...
</StackPanel>
</ScrollViewer>
and in code behind:
private void MyContent_MouseWheel(object sender, MouseWheelEventArgs e)
{
e.Handled = true;
}

Related

Story board for smooth touch scroll in WPF

Please provide storyboard for smooth touch scrolling for tablet in wpf. In my case touch response is not smooth and is shifting like it is going to a different page.
No need for storyBoard...
just add scrollviewer to your code
<ScrollViewer
VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Disabled" PanningMode="Both" ManipulationBoundaryFeedback="ScrollViewerCanvas_ManipulationBoundaryFeedback">
//Add stuff here
</ScrollViewer>
in codeBehind:
private void ScrollViewerCanvas_ManipulationBoundaryFeedback(object sender, ManipulationBoundaryFeedbackEventArgs e)
{
e.Handled = true;
}

Alternate between "Stretch.Uniform" and "Stretching.None" for ViewBox with ScrollViewer

I want to achieve a very well known behavior seen in the browser when you have an image to display that is larger then the monitor:
Originally, the image is displayed fitting inside the window area, and the mouse cursor is a magnifying glass with a "+" icon;
If you click, two things happen:
a. The image is displayed with its native pixel size;
b. Scroll bars appear;
I want this effect with a larger-than-screen UniformGrid. For that, I can use ViewBox. I have already got what I want putting the control inside a ViewBox with Stretch.Uniform property, and upon MouseLeftButtonDown event it toggles between Stretch.None and Stretch.Uniform, just like the large image in browser analogy, only without scroll bars.
Now if I add the ScrollViewer (ViewBox -> ScrollViewer -> UniformGrid), the effect doesn't work anymore, because the ScrollViewer always displays the (larger than window) MyUserControl with its native resolution, that is, clipped and with scroll bars activated, while I would like to alternate between this and a "fitting in ViewBox" version.
Here is how I get the resizing, but the ScrollViewer never displays:
<Viewbox x:Name="vbox" Stretch="None">
<ScrollViewer x:Name="scroll" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" >
<UniformGrid x:Name="ugrid" Columns="2" MouseLeftButtonDown="UniformGrid_MouseLeftButtonDown">
<local:AtlasMasculinoAnterior/>
<local:AtlasMasculinoPosterior/>
</UniformGrid>
</ScrollViewer>
</Viewbox>
And if change the order, then the Scroll bars always display and the zoom doesn't toggle upon mouse click (although the event fires):
<ScrollViewer x:Name="scroll" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" >
<Viewbox x:Name="vbox" Stretch="None">
<UniformGrid x:Name="ugrid" Columns="2" MouseLeftButtonDown="UniformGrid_MouseLeftButtonDown">
<local:AtlasMasculinoAnterior/>
<local:AtlasMasculinoPosterior/>
</UniformGrid>
</Viewbox>
</ScrollViewer>
And here the code behind event:
private void UniformGrid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (vbox.Stretch == Stretch.None)
{
vbox.Stretch = Stretch.Uniform;
}
else
vbox.Stretch = Stretch.None;
}
So what am I doing wrong, or what should I do so that the intended behavior works?
The way I see it, I would like to alternate between having the control in a ViewBox (Stretch.Uniform) and having the control inside a ScrollViewer, but I wonder how to have the same effect with both elements being part of the layout tree (one inside another), or even if I should, move the UniformGrid in and out of containers I would manipulate programmatically in code behind.
Got it to work in sort of a hackish way, by having a Grid with both a ViewBox and a ScrollViewer, and putting the UniformGrid inside one of them in XAML. Then, in code-behind, I programmatically detach the UniformGrid from its present container, and attach it to the other (using a boolean flag to control where it is, but that is debatable):
<Grid x:Name="grid">
<ScrollViewer x:Name="scroll" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
<Viewbox x:Name="viewbox" Stretch="Uniform">
<UniformGrid x:Name="ugrid" Columns="2" MouseLeftButtonDown="UniformGrid_MouseLeftButtonDown">
<local:AtlasMasculinoAnterior/>
<local:AtlasMasculinoPosterior/>
</UniformGrid>
</Viewbox>
</Grid>
and
bool atlasfullscreen = false;
private void UniformGrid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
UniformGrid ug = sender as UniformGrid;
if (atlasfullscreen)
{
scroll.Content = null;
viewbox.Child = ug;
atlasfullscreen = false;
}
else
{
viewbox.Child = null;
scroll.Content = ug;
atlasfullscreen = true;
}
}
I had a similar use case where I had an item that I needed to alternate between Stretch.None and Stretch.Uniform, and when Stretch.None, I needed the scrollbars to be visible.
What I finally figured out was that when I set Stretch.None, I needed to set the ScrollViewer's Width & Height to the ViewBox's parent ActualWidth / Height, and when Stretch.Uniform, I needed to clear the ScollViewer's width and height.
So using your original XAML, plus the new Grid, here's the new XAML:
<Grid x:Name="grid">
<Viewbox x:Name="vbox"
Stretch="Uniform">
<ScrollViewer x:Name="scroll"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<UniformGrid x:Name="ugrid"
Columns="2"
MouseLeftButtonDown="UniformGrid_MouseLeftButtonDown">
<local:AtlasMasculinoAnterior />
<local:AtlasMasculinoPosterior />
</UniformGrid>
</ScrollViewer>
</Viewbox>
</Grid>
New code behind:
private void UniformGrid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (vbox.Stretch == Stretch.None)
{
vbox.Stretch = Stretch.Uniform;
scroll.Width = double.NaN;
scroll.Height = double.NaN;
}
else
{
vbox.Stretch = Stretch.None;
scroll.Width = grid.ActualWidth;
scroll.Height = grid.ActualHeight;
}
}
You might need to tweak the above example for how the Viewbox now being in a grid - but for my use case with similar XAML / code I got mine working without having to constantly move the child from the Viewbox to another control and back again.
So in summary: when Viewbox.Stretch = Uniform, set scrollviewer's width / height to double.NaN, and when Viewbox.Stretch = None, set scrollviewer's width / height to Viewbox.Parent.ActualWidth / Height.

disable mouse wheel scrolling in scrollviewer wpf

In xaml code
<StackPanel>
<ScrollViewer>
<local:CustomCanvas>
</local:CustomCanvas>
</ScrollViewer>
</StackPanel>
CustomCanvs has a zoom in/out function. But when I spin the mouse wheel in the CustomCanvas area, ScrollViewer's scrollbar works and zoom in/out don't work. And when I scroll the scrollbar of the ScrollViewer, not only CustomCanvas' zoom in/out work but also scrolling of the ScrollViewer work well.
When I spin the mouse wheel, I want only zoom in/out. And when I scroll the scrollbar, I want only scrolling to work.
How I can prevent mouse wheel event of ScrollViewer from spining mouse wheel?
And how I can prevent zoom in/out from scrolling of ScrollViewer's scrollbar?
Please help
you could handle the MouseWheel Event of Custom Canvas so that when the mouse is pointed in your canvas area and the wheeling event accured you set the Handled property of the MouseWheelEventArgs to true :
private void UIElement_OnMouseWheel(object sender, MouseWheelEventArgs e)
{
e.Handled = true;
//handler your zoomIn/Out here
}
and in the Xaml
<StackPanel>
<ScrollViewer>
<local:CustomCanvas MouseWheel="UIElement_OnMouseWheel">
</local:CustomCanvas>
</ScrollViewer>
</StackPanel>

WPF scrolling parent container through child container

I'm having some issues trying to figure out how to scroll the content of a grid which is contained inside of a scroll viewer. When trying to scroll with the mouse wheel or pan (with a touch screen), the grid scrolls fine if the mouse/touch point is over an empty area, but if it is above certain controls (ex. a group box) it won't scroll. Is there some property I'm missing to allow the child panels to allow them to scroll their parent containers?
EDIT:
I incorrectly stated my original layout. Here's a simplified version of my senario:
<Grid>
<ScrollViewer Name="MainScrollViewer">
<StackPanel>
<ListBox /> <--Doesn't Scroll-->
<Button /> <--Scrolls Fine-->
<TextBlock /> <--Scrolls Fine-->
<TextBox /> <--Scrolls Fine-->
<DataGrid /> <--Doesn't Scroll-->
</StackPanel>
</ScrollViewer>
</Grid>
A coworker pointed out that my issue is due to the fact the controls such as a ListBoxes and DataGrids contain ScrollViewers themselves, this makes sense. His suggestion (which would work but we both agree seems more complex than it should be) is to catch and rethrow the the scroll event in the code behind (and likely have to deal with calculating the smount of offset to scroll) so that it can bubble up to "MainScrollViewer".
EDIT 2:
It seems like the only way to achieve this is to use code behind to handle the PreviewMouseWheel event in the parent. That works, but how do I go about implementing the same thing for panning (scrolling by finger on a touch screen)?
Create a bubbling scrollbehavior for your scrollview:
using System.Windows;
using System.Windows.Input;
using System.Windows.Interactivity;
public sealed class BubbleScrollEvent : Behavior<UIElement>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.PreviewMouseWheel += AssociatedObject_PreviewMouseWheel;
}
protected override void OnDetaching()
{
AssociatedObject.PreviewMouseWheel -= AssociatedObject_PreviewMouseWheel;
base.OnDetaching();
}
void AssociatedObject_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
if (!e.Handled)
{
e.Handled = true;
var e2 = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta) { RoutedEvent = UIElement.MouseWheelEvent };
AssociatedObject.RaiseEvent(e2);
}
}
}
Add this behavior to your scrollviewer:
<ScrollViewer x:Name="Scroller">
<i:Interaction.Behaviors>
<ViewAddons:BubbleScrollEvent />
</i:Interaction.Behaviors>
Use ScrollViewer's PreviewMouseWheel event and ScrollToVerticalOffset method...
private void ScrollViewerOnPreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
var scv = sender as ScrollViewer;
if (scv == null) return;
scv.ScrollToVerticalOffset(scv.VerticalOffset - e.Delta);
e.Handled = true;
}
For Starters, be sure you are using the Tech that already exists. That may resolve your issue.
<Grid HorizontalAlignment="Left" Height="300" Margin="10,10,0,0" VerticalAlignment="Top" Width="497" ScrollViewer.VerticalScrollBarVisibility="Visible" />
Although if that doesn't resolve the issue, and i know this sounds strange, Set the background color of the grid AND problem object to #00000000. (if it is not already assigned a color/brush)
<Grid HorizontalAlignment="Left" Height="300" Margin="10,10,0,0" VerticalAlignment="Top" Width="497" Background="#00000000"/>
I know its strange, but when I have these problems it works every time. I have no idea why it works. Something to do with transparency.

Why I cannot drop files from explorer to FlowDocumentReader and how to fix it?

I'm trying to implement a piece of functionality that will let the user to drag files into an application to be opened in the FlowDocumentReader.
My problem is that is though I have AllowDrop=true on the FlowDocumentReader, the cursor does not change to the "drop here" icon but changes instead to "drop is not allowed" icon.
This happens only to the FlowDocumentReader, all other parts og the UI (window itself, other controls) work as expected. The FlowDocumentReader actually receives the events, and it is possible to handle the drop, but the user does not have a visual indication that he can release the mouse here.
I also cannot hide the "drop is not allowed" cursor by setting Cursor=Cursors.None
Need to handle DragOver event in FlowDocument to allow dropping here.
xaml:
<!--
<FlowDocumentReader x:Name="fdr" Background="White">
<FlowDocument x:Name="doc" AllowDrop="True" DragEnter="doc_DragOver" Drop="doc_Drop" Background="White"/>
</FlowDocumentReader>
-->
<FlowDocumentReader x:Name="fdr" Background="White">
<FlowDocument x:Name="doc" AllowDrop="True" DragOver="doc_DragOver" Drop="doc_Drop" Background="White"/>
</FlowDocumentReader>
code behind:
private void doc_DragOver(object sender, DragEventArgs e)
{
e.Effects = DragDropEffects.All;
e.Handled = true;
}
private void doc_Drop(object sender, DragEventArgs e)
{
}
I couldn't find any direct way to solve this, so here is what I have ended up with:
I placed a grid on top of the FlowDocumentReader. This grid has a sold color, opacity of 0 (transparent) and Visibility=Collapsed. The purpose of this grid is to serve as a drop target.
When FlowDocument within the FlowDocumentReader received the DragEnter event, I switch the grid's visibility to Visible. The grid starts receiving drag events and the cursor stays in the "drop here" form.
When grid receives Drop or DragLeave events, its visibility is turned back to Collapsed to allow the FlowDocument receive mouse events
<FlowDocumentReader x:Name="fdr" Grid.Row="1" Background="White">
<FlowDocument x:Name="doc" DragEnter="doc_DragEnter" Background="White"/>
</FlowDocumentReader>
<Grid x:Name="dtg" Grid.Row="1" Background="White" Opacity="0"
Drop="dtg_Drop" DragLeave="dtg_DragLeave" Visibility="Collapsed"/>

Resources