Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trying to programmatically scroll a scrollviewer

I'm developing a WPF app in which I have a ScrollViewer with grid (16 ColumnDefinitions all auto width) with an image assigned to each column. The effect I want to create is when the mouse enters a area on the left of the ScrollViewer the images will scroll to the left... and when the mouse enters an area on the right of the ScrollViewer they scroll to the right. The scrollbars will be hidden. The areas to the left and right are defined by two rectangles with mouseEnter and mouseLeave events. My code behind utilises a timer to programatically scroll the ScrollViewer leftor right.

Here is my code...

Imports System
Imports System.IO
Imports System.Net
Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Data
Imports System.Windows.Media
Imports System.Windows.Media.Animation
Imports System.Windows.Navigation


Partial Public Class Crime

    Dim ScrollLeft As Boolean = True
    Dim atimer As New System.Timers.Timer()

    Public Sub New()
        MyBase.New()

        Me.InitializeComponent()

        ' Insert code required on object creation below this point.

        ' Hook up the Elapsed event for the timer.
        AddHandler atimer.Elapsed, AddressOf Me.timer_Tick

        atimer.Interval = 100
        atimer.Enabled = True

    End Sub

    'CODE TO SCROLL SCROLLVIEWER PROGRAMATICALLY
    Private Sub timer_Tick(sender As Object, e As EventArgs)
        If ScrollLeft Then
            svImages.ScrollToHorizontalOffset(svImages.HorizontalOffset - 1)
        Else
            svImages.ScrollToHorizontalOffset(svImages.HorizontalOffset + 1)
        End If
    End Sub

    Private Sub Left_MouseEnter(sender As Object, e As MouseEventArgs)
        ScrollLeft = True
        atimer.Start()
    End Sub

    Private Sub Right_MouseEnter(sender As Object, e As MouseEventArgs)
        ScrollLeft = False
        atimer.Start()
    End Sub

    Private Sub Left_MouseLeave(sender As Object, e As MouseEventArgs)
        atimer.Stop()
        ScrollLeft = True
    End Sub

    Private Sub Right_MouseLeave(sender As Object, e As MouseEventArgs)
        atimer.Stop()
    End Sub

End Class

If I run this from Expressions Blend the project builds OK and displays, but the scroll action is not working.

If I run this from Visual Studio I get an error with the line...

svImages.ScrollToHorizontalOffset(svImages.HorizontalOffset - 0.1)

saying InvalidOperationException was unhandled by user code... The calling thread cannot access this object because a different thread owns it. I get this error before the window even loads.

Where have I gone wrong.

like image 316
Mych Avatar asked Mar 23 '12 20:03

Mych


1 Answers

The error "The calling thread cannot access this object because a different thread owns it." is quite standard for WinForms and WPF controls.

In WPF and WinForms windows are rendered on the screen using one specific thread, usually called UI thread. Every update/change action regarding controls should take place on that thread to have success.

Usual way to go with WinForms is to create a special delegate and call Control.Invoke as shown in this link.

As of WPF, same effect is achieved with the use of a Dispatcher. Your code should look like this:

this.Dispatcher.Invoke( () => svImages.ScrollToHorizontalOffset(svImages.HorizontalOffset - 0.1));

UPDATE:

I've got the following code to work in VB.NET:

Private Delegate Sub ScrollDelegate(ByVal offset As Double)

Private Sub ScrollLeft(ByVal offset As Double)
    svImages.ScrollToHorizontalOffset(svImages.HorizontalOffset + offset)
End Sub

// ... calling from background thread

Dim slt As ScrollDelegate
slt = New ScrollDelegate(AddressOf ScrollLeft)
Me.Dispatcher.Invoke(slt)

Update 2

Code changed as to the question.

Dim ScrollLeft As Boolean = True
Dim atimer As New System.Timers.Timer()
Dim scrollMethod As ScrollDelegate
Private Delegate Sub ScrollDelegate(ByVal offset As Double)

// ...
    Me.InitializeComponent()
    slt = New ScrollDelegate(AddressOf DoScroll)

// ...

 Private Sub timer_Tick(sender As Object, e As EventArgs)
    If ScrollLeft Then
        Me.Dispatcher.Invoke(slt, -1)
    Else
        Me.Dispatcher.Invoke(slt, 1)
    End If
End Sub

// ...

Private Sub DoScroll(ByVal offset As Double)
    svImages.ScrollToHorizontalOffset(svImages.HorizontalOffset + offset)
End Sub
like image 183
Dmitry Reznik Avatar answered Nov 13 '22 00:11

Dmitry Reznik