frochet38
Topic Author
Posts: 12
Joined: 14 Jun 2020, 11:14

Animate a Scrollviewer without using scrollbar

18 Aug 2020, 10:29

Hi,

I have a lisbox with a collection of items, I use two buttons to control the listbox's scolling so I hide the listbox default scroller. using a custom template style instancied in a XAML file like this :
           <Canvas x:Name="PanelMakeupBottomLeft" Grid.Row="1" Grid.Column="4" Opacity="1.0" Background="#80FFFFFF" VerticalAlignment="Top" HorizontalAlignment="Right" Width="136" Height="540">
                <Button x:Name="BtnScrollUpBottomLeft" Command="{Binding ScrollUp4LooksBottomLeft}" Canvas.Top="5" Canvas.Left="65" Width="24" Height="24" Foreground="{StaticResource BandeauColor}" BorderBrush="{StaticResource BandeauColor}" Background="{StaticResource BandeauColor}" Panel.ZIndex="1">
                    <Image Source="ArrowUp.png" Stretch="Fill" VerticalAlignment="Center" HorizontalAlignment="Center">
                    </Image>
                </Button>

                <ListBox x:Name="BottomLeftMakeupSelector" 
                         Style="{StaticResource Car4LooksListStyle}"
                         ItemContainerStyle="{StaticResource Car4LooksItemStyle}" 
                         ItemsSource="{Binding LooksBottomLeft}"
                         DisplayMemberPath="Name" 
                         Height="540"
                         Width="136">
                </ListBox>

                <Button x:Name="BtnScrollDownBottomLeft" Command="{Binding ScrollDown4LooksBottomLeft}" Canvas.Left="65" Canvas.Bottom="5" Width="24" Height="24" Foreground="{StaticResource BandeauColor}" BorderBrush="{StaticResource BandeauColor}" Background="{StaticResource BandeauColor}" Panel.ZIndex="1">
                    <Image Source="ArrowDown.png" Stretch="Fill" VerticalAlignment="Center" HorizontalAlignment="Center" Panel.ZIndex="1">
                    </Image>
                </Button>
            </Canvas>
 
The two buttons controls the ListBox's Scrollviewer scrolling using this kind of code:
void ViewModel::ScrollListBoxAnimated(const char* szComponentName, float fScrolValue)
{
	Noesis::ListBox* pListBox = FindControl<Noesis::ListBox>(szComponentName);
	if (pListBox != nullptr)
	{
		Noesis::Visual* pVisual = VisualTreeHelper::GetChild(pListBox, 0);
		ScrollViewer* pScroll = static_cast<ScrollViewer*>(pVisual);
		// Scroll
		float fOffset = pScroll->GetVerticalOffset();
		pScroll->ScrollToVerticalOffset(fOffset + fScrolValue);
	}
}
This works well, but now I'd like to animate the items' scrolling to get a nice visual effect.

So I tried this kind of code :
void ViewModel::ScrollListBoxAnimated(const char* szComponentName, float fScrolValue)
{
	Noesis::ListBox* pListBox = FindControl<Noesis::ListBox>(szComponentName);
	if (pListBox != nullptr)
	{
		Noesis::Visual* pVisual = VisualTreeHelper::GetChild(pListBox, 0);
		ScrollViewer* pScroll = static_cast<ScrollViewer*>(pVisual);
		float fOffset = pScroll->GetVerticalOffset();
		
		// Scroll animated
		Ptr<PowerEase> _ease = *new PowerEase();
		_ease->SetEasingMode(EasingMode_EaseOut);
		_ease->SetPower(8.0f);

		Ptr<DoubleAnimation> scrollanim = *new DoubleAnimation();
		scrollanim->SetEasingFunction(_ease);
		Nullable<float> fFrom(pScroll->GetVerticalOffset());
		Nullable<float> fTo(pScroll->GetVerticalOffset() + fScrolValue);
		scrollanim->SetFrom(fFrom);
		scrollanim->SetTo(fTo);
		scrollanim->SetDuration(*new Duration(*new Noesis::TimeSpan(2.0)));

		pScroll->SetAnimation(pScroll->VerticalOffsetProperty, scrollanim);
	}
}
This dosn't works at all (does nothing).

So I also tried using a storyboard like this:
void ViewModel::ScrollListBoxAnimated(const char* szComponentName, float fScrolValue)
{
	Noesis::ListBox* pListBox = FindControl<Noesis::ListBox>(szComponentName);
	if (pListBox != nullptr)
	{
		Noesis::Visual* pVisual = VisualTreeHelper::GetChild(pListBox, 0);
		ScrollViewer* pScroll = static_cast<ScrollViewer*>(pVisual);
		float fOffset = pScroll->GetVerticalOffset();
		
		// Scroll animated
		Ptr<PowerEase> _ease = *new PowerEase();
		_ease->SetEasingMode(EasingMode_EaseOut);
		_ease->SetPower(8.0f);

		Ptr<DoubleAnimation> scrollanim = *new DoubleAnimation();
		scrollanim->SetEasingFunction(_ease);
		Nullable<float> fFrom(pScroll->GetVerticalOffset());
		Nullable<float> fTo(pScroll->GetVerticalOffset() + fScrolValue);
		scrollanim->SetFrom(fFrom);
		scrollanim->SetTo(fTo);
		scrollanim->SetDuration(*new Duration(*new Noesis::TimeSpan(2.0)));

		// With sotryboard
		Ptr<Storyboard> storyboard = *new Storyboard();
		storyboard->GetChildren()->Add(scrollanim);
		Storyboard::SetTargetName(scrollanim, pScroll->GetName());
		Storyboard::SetTargetProperty(scrollanim, MakePtr<PropertyPath>(pScroll->VerticalOffsetProperty));
		storyboard->Begin(pScroll);
	}
}
Dos nothing, like the previous code.

I'm still a very beginer in Noesis / WPF and I'm surely missing something. Do you have any suggestion to make this work ?

Thanks a lot,

Frank
 
andreas_sodergren
Posts: 1
Joined: 20 Jun 2019, 14:51

Re: Animate a Scrollviewer without using scrollbar

18 Aug 2020, 11:41

Hello,

I'm basically trying to do the same thing, and stuck.
The reason it doesn't work is because we can't animate VerticalScrollOffset because it's "Read Only"
I tried to follow these posts:

viewtopic.php?t=450
viewtopic.php?f=3&t=2066&p=11563&hilit= ... rty#p11563
https://www.noesisengine.com/docs/2.2/G ... Index.html
void cContentScroller::addAnimation( float _to_scroll )
{
	Noesis::DoubleAnimation* animation = new Noesis::DoubleAnimation();

	animation->SetFrom( Noesis::Nullable< float >( m_scroll_viewer->GetVerticalOffset() ) );
	animation->SetTo( Noesis::Nullable< float >( _to_scroll ) );
	animation->SetDuration( Noesis::Duration( 1.0f ) );

	Noesis::Storyboard* storyboard = new Noesis::Storyboard();
	Noesis::Storyboard::SetTarget( animation, m_scroll_viewer );
	Noesis::Storyboard::SetTargetProperty( animation, new Noesis::PropertyPath( Noesis::ScrollViewer::VerticalOffsetProperty ) );
	storyboard->GetChildren()->Add( animation );
	storyboard->Begin( m_scroll_viewer );
}
So i tried to create this "Extensions" thing, and i've gotten this far and now I'm not sure how to proceed.
class Extensions: public Noesis::BaseComponent
{
public:

	static const Noesis::DependencyProperty* VerticalScrollProperty;

	static float GetVerticalOffset( Noesis::DependencyObject* d )
	{
		return d->GetValue< float >( VerticalScrollProperty );
	}
	
	static void SetVerticalOffset( Noesis::DependencyObject* d, float offset )
	{
		d->SetValue< float >( VerticalScrollProperty, offset );
	}
	
	static void OnVerticalOffsetChanged( Noesis::DependencyObject* d, const Noesis::DependencyPropertyChangedEventArgs& /*e*/ )
	{
		Noesis::ScrollViewer* scroll = Noesis::DynamicCast< Noesis::ScrollViewer*, Noesis::DependencyObject* >( d );
		if( scroll )
		{
			float offset = scroll->GetValue< float >( VerticalScrollProperty );
			scroll->ScrollToVerticalOffset( offset );
		}
	}

private:

	NS_IMPLEMENT_INLINE_REFLECTION( Extensions, Noesis::BaseComponent )
	{
		NsMeta< Noesis::TypeId >( "Extensions" );
		Noesis::DependencyData* data = NsMeta< Noesis::DependencyData >( Noesis::TypeOf< SelfClass >() );
		data->RegisterProperty< float >( VerticalScrollProperty, "VerticalScroll", Noesis::PropertyMetadata::Create( 0.0f, Noesis::PropertyChangedCallback( OnVerticalOffsetChanged ) ) );
	}
};
I'm not sure if i should but when I try to NsRegisterComponent< Extensions >();
I get the following error:
Error LNK2001 unresolved external symbol "public: static class Noesis::DependencyProperty const * const Extensions::VerticalScrollProperty" (?VerticalScrollProperty@Extensions@@2PEBVDependencyProperty@Noesis@@EB)


Still working on Noesis 2.2.5.
 
User avatar
sfernandez
Site Admin
Posts: 1915
Joined: 22 Dec 2011, 19:20

Re: Animate a Scrollviewer without using scrollbar

20 Aug 2020, 12:49

ScrollViewer's HorizontalOffset and VerticalOffset properties are read-only, they can only be modified by calling ScrollViewer.ScrollToHorizontalOffset() or ScrollViewer.ScrollToVerticalOffset() functions. To do so by using animations you need attached properties that when animated they call those functions on the corresponding changed callback:
namespace Animation
{
    public class ScrollViewerHelper : DependencyObject
    {
        public static DependencyProperty HorizontalScrollProperty = DependencyProperty.RegisterAttached(
            "HorizontalScroll", typeof(float), typeof(ScrollViewerHelper),
            new PropertyMetadata(0.0f, OnHorizontalScrollChanged));

        public static float GetHorizontalScroll(DependencyObject d)
        {
            return (float)d.GetValue(HorizontalScrollProperty);
        }

        public static void SetHorizontalScroll(DependencyObject d, float offset)
        {
            d.SetValue(HorizontalScrollProperty, offset);
        }

        static void OnHorizontalScrollChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ScrollViewer sv = d as ScrollViewer;
            if (sv != null)
            {
                float offset = GetHorizontalScroll(sv);
                sv.ScrollToHorizontalOffset(offset);
            }
        }
        
        public static DependencyProperty VerticalScrollProperty = DependencyProperty.RegisterAttached(
            "VerticalScroll", typeof(float), typeof(ScrollViewerHelper),
            new PropertyMetadata(0.0f, OnVerticalScrollChanged));

        public static float GetVerticalScroll(DependencyObject d)
        {
            return (float)d.GetValue(VerticalScrollProperty);
        }

        public static void SetVerticalScroll(DependencyObject d, float offset)
        {
            d.SetValue(VerticalScrollProperty, offset);
        }

        static void OnVerticalScrollChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ScrollViewer sv = d as ScrollViewer;
            if (sv != null)
            {
                float offset = GetVerticalScroll(sv);
                sv.ScrollToVerticalOffset(offset);
            }
        }
    }
}
This can be used in xaml like this:
<Grid
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:local="clr-namespace:Animation">
    <Grid.Resources>
        <Storyboard x:Key="anim">
            <DoubleAnimation Duration="0:0:2" From="0" To="100"
                Storyboard.TargetName="scroll" Storyboard.TargetProperty="(local:ScrollViewerHelper.VerticalScroll)"/>
        </Storyboard>
    </Grid.Resources>
    <ScrollViewer x:Name="scroll" Width="200" Height="200"/>
    ...
</Grid>
And the same translates to code:
DoubleAnimation a = new DoubleAnimation();
a.Duration = new TimeSpan(0, 0, 2);
a.From = 0.0f;
a.To = 100.0f;
Storyboard.SetTargetName(a, "scroll");
Storyboard.SetTargetProperty(a, Animation.ScrollViewerHelper.VerticalScrollProperty);
Storyboard s = new Storyboard();
s.Children.Add(a);
Error LNK2001 unresolved external symbol "public: static class Noesis::DependencyProperty const * const Extensions::VerticalScrollProperty" (?VerticalScrollProperty@Extensions@@2PEBVDependencyProperty@Noesis@@EB)
This means you forgot to add the definitions in the .cpp: https://stackoverflow.com/questions/195 ... ss-members
const Noesis::DependencyProperty* Extensions::VerticalScrollProperty;
 
frochet38
Topic Author
Posts: 12
Joined: 14 Jun 2020, 11:14

Re: Animate a Scrollviewer without using scrollbar

24 Aug 2020, 10:31

Thanks a lot to you guys, I finally managed to make it work thanks to your advices.

I have just a remark:

this kind of code doesn't compile any more in the lastest version of Noesis:
NsMeta< Noesis::TypeId >( "Extensions" );
I replaced it by:
NsMeta< Category >("Extensions");
It seems to work but I was wondering if this was still necessary ?
 
User avatar
sfernandez
Site Admin
Posts: 1915
Joined: 22 Dec 2011, 19:20

Re: Animate a Scrollviewer without using scrollbar

24 Aug 2020, 10:44

In Noesis 3.0 the type id, if you need to explicitly specify one, is set in the reflection macro itself:
NS_IMPLEMENT_REFLECTION(Extensions, "Testing.Extensions")
The Category is not required for this to work, you can remote it if you want.

Who is online

Users browsing this forum: Bing [Bot] and 1 guest