Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions components/Marquee/samples/Dependencies.props
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,21 @@
<Project>
<!-- WinUI 2 / UWP -->
<ItemGroup Condition="'$(IsUwp)' == 'true'">
<!-- <PackageReference Include="Microsoft.Toolkit.Uwp.UI.Controls.Primitives" Version="7.1.2"/> -->
<PackageReference Include="CommunityToolkit.Uwp.Behaviors" Version="8.2.250402"/>
</ItemGroup>

<!-- WinUI 2 / Uno -->
<ItemGroup Condition="'$(IsUno)' == 'true' AND '$(WinUIMajorVersion)' == '2'">
<!-- <PackageReference Include="Uno.Microsoft.Toolkit.Uwp.UI.Controls.Primitives" Version="7.1.11"/> -->
<PackageReference Include="Uno.Microsoft.Toolkit.Uwp.UI.Behaviors" Version="7.1.11"/>
</ItemGroup>

<!-- WinUI 3 / WinAppSdk -->
<ItemGroup Condition="'$(IsWinAppSdk)' == 'true'">
<!-- <PackageReference Include="CommunityToolkit.WinUI.UI.Controls.Primitives" Version="7.1.2"/> -->
<PackageReference Include="CommunityToolkit.WinUI.Behaviors" Version="8.2.250402"/>
</ItemGroup>

<!-- WinUI 3 / Uno -->
<ItemGroup Condition="'$(IsUno)' == 'true' AND '$(WinUIMajorVersion)' == '3'">
<!-- <PackageReference Include="Uno.CommunityToolkit.WinUI.UI.Controls.Primitives" Version="7.1.100-dev.15.g12261e2626"/> -->
<PackageReference Include="Uno.CommunityToolkit.WinUI.UI.Behaviors" Version="7.1.204"/>
</ItemGroup>
</Project>
26 changes: 25 additions & 1 deletion components/Marquee/samples/Marquee.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ icon: Assets/Marquee.png

The Marquee control allows text or other content to scroll in a marquee fashion. The control is heavily templated and many changes can be made by modifying the style. The control can also be adjusted using the Speed, Behavior, RepeatBehavior, and Direction properties.

> [!Sample MarqueeTextSample]

## Speed

The speed property determines how quickly the content moves in pixels per second. The speed can be adjusted mid-animation and handled continously.
Expand All @@ -23,6 +25,8 @@ The speed property determines how quickly the content moves in pixels per second

The Marquee control has 3 behaviors

Default: `Ticker`

### Ticker

Ticker mode starts with all content off the screen then scrolls the content across across the screen. When the animation finishes in the mode no content will be on screen.
Expand All @@ -39,14 +43,34 @@ Looping mode will begin with the start of the content fully in frame then scroll

The repeat behavior determines how many times the marquee will loop before the animation finishes. It can be a number of iteration, a duration, or forever.

Default: `1x`

## Direction

The default direction is left, meaning the content will move leftwards, but this can be changed to right, up, or down. Direction changed between left and right or up and down are handled continously, meaning that the animation will resume from its current position if changed between these directions.

> [!Sample MarqueeTextSample]
Default: `Left`

## Speed

The speed property determines how quickly the content moves in pixels-per-second. The speed can be adjusted mid-animation and handled continously. Because the speed is in pixels per second the actual time it takes to scroll will depend on the size of the content. The content can also change size dynamically and the speed will remain constant.

Default: `32`

## AutoPlay

The AutoPlay property determines if the marquee will start animating when it is loaded, upon on size changes, or when the content changes. If set to false the marquee can be started manually by calling the `StartMarquee` method.

Default: `false`

## Non-Text Content

It is possible to use non-text content in the Marquee control. However templating must be used because the control will need to be duplicated for the looping animation.

> [!Sample MarqueeSample]

# Level Up with Behaviors

Use behaviors to triggers the Marquee on events

> [!Sample MarqueeBehaviorSample]
71 changes: 71 additions & 0 deletions components/Marquee/samples/MarqueeBehaviorSample.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<!-- Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license. See the LICENSE file in the project root for more information. -->
<Page x:Class="MarqueeExperiment.Samples.MarqueeBehaviorSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:local="MarqueeExperiment.Samples"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:win="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
mc:Ignorable="d">

<win:StackPanel Padding="16">
<TextBlock FontWeight="Bold"
Text="This Marquee will loop once when hovered." />
<controls:Marquee x:Name="Loop1Marquee"
Background="Transparent"
Behavior="Ticker"
Content="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
FontSize="18"
RepeatBehavior="1x"
Speed="256">
<interactivity:Interaction.Behaviors>
<interactivity:EventTriggerBehavior EventName="PointerEntered"
SourceObject="{Binding ElementName=Loop1Marquee}">
<interactivity:CallMethodAction MethodName="StartMarquee"
TargetObject="{Binding ElementName=Loop1Marquee}" />
</interactivity:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</controls:Marquee>

<TextBlock Margin="0,16,0,0"
FontWeight="Bold"
Text="This Marquee contains hyperlinks and will pause while hovered." />
<controls:Marquee x:Name="MarqueeControl"
AutoPlay="True"
Background="Transparent"
Behavior="Looping"
FontSize="18"
RepeatBehavior="Forever"
Speed="96">
<controls:Marquee.ContentTemplate>
<DataTemplate>
<StackPanel Spacing="4">
<RichTextBlock>
<Paragraph>
<Run Text="Lorem ipsum dolor sit amet, " />
<Hyperlink>consectetur adipiscing elit</Hyperlink>
<Run Text=", sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. " />
<Hyperlink>Ut enim ad minim veniam</Hyperlink>
<Run Text=", quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." />
</Paragraph>
</RichTextBlock>
</StackPanel>
</DataTemplate>
</controls:Marquee.ContentTemplate>
<interactivity:Interaction.Behaviors>
<interactivity:EventTriggerBehavior EventName="PointerEntered"
SourceObject="{Binding ElementName=MarqueeControl}">
<interactivity:CallMethodAction MethodName="PauseMarquee"
TargetObject="{Binding ElementName=MarqueeControl}" />
</interactivity:EventTriggerBehavior>
<interactivity:EventTriggerBehavior EventName="PointerExited"
SourceObject="{Binding ElementName=MarqueeControl}">
<interactivity:CallMethodAction MethodName="StartMarquee"
TargetObject="{Binding ElementName=MarqueeControl}" />
</interactivity:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</controls:Marquee>
</win:StackPanel>
</Page>
14 changes: 14 additions & 0 deletions components/Marquee/samples/MarqueeBehaviorSample.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace MarqueeExperiment.Samples;

[ToolkitSample(id: nameof(MarqueeBehaviorSample), "Marquee", description: "A control for scrolling content in a marquee fashion.")]
public sealed partial class MarqueeBehaviorSample : Page
{
public MarqueeBehaviorSample()
{
this.InitializeComponent();
}
}
5 changes: 3 additions & 2 deletions components/Marquee/samples/MarqueeSample.xaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!-- Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license. See the LICENSE file in the project root for more information. -->
<!-- Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license. See the LICENSE file in the project root for more information. -->
<Page x:Class="MarqueeExperiment.Samples.MarqueeSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Expand All @@ -9,7 +9,8 @@
mc:Ignorable="d">

<StackPanel Padding="16">
<controls:Marquee Behavior="{x:Bind ConvertStringToMarqueeBehavior(MQBehavior), Mode=OneWay}"
<controls:Marquee AutoPlay="True"
Behavior="{x:Bind ConvertStringToMarqueeBehavior(MQBehavior), Mode=OneWay}"
Content="{x:Bind Data}"
Direction="{x:Bind ConvertStringToMarqueeDirection(MQDirection), Mode=OneWay}"
FontSize="18"
Expand Down
10 changes: 0 additions & 10 deletions components/Marquee/samples/MarqueeText.Samples.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,5 @@

<!-- Sets this up as a toolkit component's sample project -->
<Import Project="$(ToolingDirectory)\ToolkitComponent.SampleProject.props" />
<ItemGroup>
<Compile Update="MarqueeTextSample.xaml.cs">
<DependentUpon>MarqueeTextSample.xaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Update="MarqueeSample.xaml.cs">
<DependentUpon>MarqueeSample.xaml</DependentUpon>
</Compile>
</ItemGroup>

</Project>
18 changes: 15 additions & 3 deletions components/Marquee/samples/MarqueeTextSample.xaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!-- Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license. See the LICENSE file in the project root for more information. -->
<!-- Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license. See the LICENSE file in the project root for more information. -->
<Page x:Class="MarqueeExperiment.Samples.MarqueeTextSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Expand All @@ -8,12 +8,24 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">

<StackPanel Padding="16">
<controls:Marquee Behavior="{x:Bind ConvertStringToMarqueeBehavior(MQBehavior), Mode=OneWay}"
<StackPanel Padding="16"
Spacing="8">
<controls:Marquee x:Name="MarqueeControl"
AutoPlay="{x:Bind MQAuto, Mode=OneWay}"
Behavior="{x:Bind ConvertStringToMarqueeBehavior(MQBehavior), Mode=OneWay}"
Content="{x:Bind MQText, Mode=OneWay}"
Direction="{x:Bind ConvertStringToMarqueeDirection(MQDirection), Mode=OneWay}"
FontSize="18"
RepeatBehavior="Forever"
Speed="{x:Bind MQSpeed, Mode=OneWay}" />

<Button Click="StartMarquee_Click"
Content="Start Marquee" />
<Button Click="StopMarquee_Click"
Content="Stop Marquee" />
<Button Click="PauseMarquee_Click"
Content="Pause Marquee" />
<Button Click="ResumeMarquee_Click"
Content="Resume Marquee" />
</StackPanel>
</Page>
9 changes: 9 additions & 0 deletions components/Marquee/samples/MarqueeTextSample.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ namespace MarqueeExperiment.Samples;
#else
[ToolkitSampleMultiChoiceOption("MQBehavior", "Ticker", "Looping", Title = "Marquee Behavior")]
#endif
[ToolkitSampleBoolOption("MQAuto", true, Title = "Auto Play")]
public sealed partial class MarqueeTextSample : Page
{
public MarqueeTextSample()
Expand All @@ -41,4 +42,12 @@ public MarqueeTextSample()
"Down" => MarqueeDirection.Down,
_ => throw new System.NotImplementedException(),
};

private void StartMarquee_Click(object sender, RoutedEventArgs e) => MarqueeControl.StartMarquee();

private void StopMarquee_Click(object sender, RoutedEventArgs e) => MarqueeControl.StopMarquee();

private void ResumeMarquee_Click(object sender, RoutedEventArgs e) => MarqueeControl.ResumeMarquee();

private void PauseMarquee_Click(object sender, RoutedEventArgs e) => MarqueeControl.PauseMarquee();
}
69 changes: 54 additions & 15 deletions components/Marquee/src/Marquee.Events.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,23 @@ public partial class Marquee
/// <summary>
/// Event raised when the Marquee begins scrolling.
/// </summary>
public event EventHandler? MarqueeBegan;
public event EventHandler? MarqueeStarted;

/// <summary>
/// Event raised when the Marquee stops scrolling for any reason.
/// Event raised when the Marquee is stopped manually or completed.
/// </summary>
public event EventHandler? MarqueeStopped;

/// <summary>
/// Event raised when the Marquee is resumed from a pause.
/// </summary>
public event EventHandler? MarqueeResumed;

/// <summary>
/// Event raised when the Marquee is paused.
/// </summary>
public event EventHandler? MarqueePaused;

/// <summary>
/// Event raised when the Marquee completes scrolling.
/// </summary>
Expand All @@ -28,18 +38,36 @@ private void Marquee_Loaded(object sender, RoutedEventArgs e)
{
// While loaded, detach the loaded event and attach the unloaded event
this.Loaded -= this.Marquee_Loaded;
this.Unloaded += Marquee_Unloaded;
this.Unloaded += this.Marquee_Unloaded;

// Attach other events
if (_marqueeContainer is not null)
{
_marqueeContainer.SizeChanged += Container_SizeChanged;
}

if (_segment1 is not null)
{
_segment1.SizeChanged += Segment_SizeChanged;
}

if (_marqueeStoryboard is not null)
{
_marqueeStoryboard.Completed += StoryBoard_Completed;
}

// The size may have channged while unloaded.
// Clip the marquee
ClipMarquee();

// Setup the animation
UpdateMarquee(false);

// The marquee should run when loaded if auto play is enabled
if (AutoPlay)
{
StartMarquee();
}
}

private void Marquee_Unloaded(object sender, RoutedEventArgs e)
Expand All @@ -53,6 +81,11 @@ private void Marquee_Unloaded(object sender, RoutedEventArgs e)
_marqueeContainer.SizeChanged -= Container_SizeChanged;
}

if (_segment1 is not null)
{
_segment1.SizeChanged -= Segment_SizeChanged;
}

if (_marqueeStoryboard is not null)
{
_marqueeStoryboard.Completed -= StoryBoard_Completed;
Expand All @@ -62,35 +95,41 @@ private void Marquee_Unloaded(object sender, RoutedEventArgs e)
private void Container_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (_marqueeContainer is null)
{
return;
}

// Clip the marquee within its bounds
_marqueeContainer.Clip = new RectangleGeometry
{
Rect = new Rect(0, 0, e.NewSize.Width, e.NewSize.Height)
};
// Clip the marquee
ClipMarquee(e.NewSize.Width, e.NewSize.Height);

// Update animation on the fly
UpdateMarquee(true);

// The marquee should run when the size changes in case the text gets cutoff
StartMarquee();
// and auto play is enabled.
if (AutoPlay)
{
StartMarquee();
}
}

private void Segment_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (_segment1 is null)
{
return;
}

if (_marqueeContainer is null)
return;

// Cap the height of the container to the segment height
_marqueeContainer.Height = _segment1.ActualHeight;

// If the segment size changes, we need to update the storyboard,
// and seek to the correct position to maintain a smooth animation.
UpdateAnimation(true);
UpdateMarquee(true);
}

private void StoryBoard_Completed(object? sender, object e)
{
StopMarquee(true);
StopMarquee();
MarqueeCompleted?.Invoke(this, EventArgs.Empty);
}
}
Loading
Loading