Making slide animation in wpf - c#

I have a grid, which used as container. Grid consist of UserControl, each one has 600px height and 800px width. I want to make slide animation like presentation by switching visible controls.
Here is my xaml code of mainWindow:
<Window x:Class="MessengerWindowsClient.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MessengerWindowsClient"
xmlns:pages="clr-namespace:MessengerWindowsClient.Pages"
mc:Ignorable="d"
Title="MainWindow" Height="600" Width="800" Closed="Window_Closed">
<Window.Background>
<ImageBrush ImageSource="Resources/background.jpg"></ImageBrush>
</Window.Background>
<Grid x:Name="Container" RenderTransformOrigin="0.5,0.5" SizeChanged="Container_SizeChanged">
<pages:WelcomePage x:Name ="WelcomePage" Visibility="Visible" RegisterPage="{Binding ElementName=RegisterPage}" LoginPage="{Binding ElementName=LoginPage}"/>
<pages:MessagesPage Visibility="Collapsed"/>
<pages:LoginPage x:Name="LoginPage" Visibility="Collapsed" WelcomePage="{Binding ElementName=WelcomePage}"/>
<pages:RegisterPage x:Name="RegisterPage" Visibility="Collapsed" WelcomePage="{Binding ElementName=WelcomePage}"/>
</Grid>
Here is code behind:
public partial class MainWindow : Window
{
private ServiceManager _serviceManager;
private UIElement _currentPage;
public MainWindow()
{
InitializeComponent();
_currentPage = this.Container.Children[0];
this.RegisterPage.RegisterReady += RegisterUser;
this.RegisterPage.ChangePage += ChangePage;
this.WelcomePage.ChangePage += ChangePage;
this.LoginPage.ChangePage += ChangePage;
_serviceManager = new ServiceManager();
}
private void ChangePage(object sender, ChangePageEventArgs e)
{
switch (e.Direction)
{
case ChangePageDirection.Forward:
AnimationManager.AnimateForwardPage(e.NewPage, e.OldPage, Container, this.ActualWidth);
break;
case ChangePageDirection.Backward:
AnimationManager.AnimateBackwardPage(e.NewPage, e.OldPage, Container, this.ActualWidth);
break;
}
}
private async void RegisterUser(object sender, RegisterEventArgs e)
{
var isSucceed = await _serviceManager.RegisterUser(e.Name, e.Username, e.Password.ToString(), e.Email);
e.Password.Dispose();
}
private void Window_Closed(object sender, EventArgs e)
{
_serviceManager.Dispose();
}
private void Container_SizeChanged(object sender, SizeChangedEventArgs e)
{
UpdateLayout();
}
}
}
I tried to use this.ActualWidth, but it gives value that is more than my display resolution. So part of my control goes behind the screen. And after the animation completes it returns back. Using any width property of grid gives wrong value, even with UpdateLayout() on resize event.
Edit:
Screenshots
After animation completed and after _container.HorizontalAlignment = HorizontalAlignment.Stretch;.

Are you trying to animate the width or height of a certain UI element? You need to make a custom animation class that extends AnimationTimeline and define an animation inside a Storyboard in XAML.
You will need to create a custom class GridLengthAnimation:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media.Animation;
namespace Infrastructure.Animations
{
public class GridLengthAnimation : AnimationTimeline
{
protected override Freezable CreateInstanceCore()
{
return new GridLengthAnimation();
}
public override Type TargetPropertyType => typeof(GridLength);
static GridLengthAnimation()
{
FromProperty = DependencyProperty.Register("From", typeof(GridLength),
typeof(GridLengthAnimation));
ToProperty = DependencyProperty.Register("To", typeof(GridLength),
typeof(GridLengthAnimation));
}
public static readonly DependencyProperty FromProperty;
public GridLength From
{
get => (GridLength)GetValue(GridLengthAnimation.FromProperty);
set => SetValue(GridLengthAnimation.FromProperty, value);
}
public static readonly DependencyProperty ToProperty;
public GridLength To
{
get => (GridLength)GetValue(GridLengthAnimation.ToProperty);
set => SetValue(GridLengthAnimation.ToProperty, value);
}
public override object GetCurrentValue(object defaultOriginValue,
object defaultDestinationValue, AnimationClock animationClock)
{
double fromVal = ((GridLength)GetValue(GridLengthAnimation.FromProperty)).Value;
double toVal = ((GridLength)GetValue(GridLengthAnimation.ToProperty)).Value;
if (fromVal > toVal)
{
return new GridLength((1 - animationClock.CurrentProgress.Value) *
(fromVal - toVal) + toVal, GridUnitType.Pixel);
}
else
{
return new GridLength(animationClock.CurrentProgress.Value *
(toVal - fromVal) + fromVal, GridUnitType.Pixel);
}
}
}
}
You can then use this in XAML inside a Storyboard like this:
<Storyboard x:Key="storyboardName">
<support:GridLengthAnimation Storyboard.TargetName="YourElementToAnimate" Storyboard.TargetProperty="Width" From="{Binding StartAnimationWidth}" To="{Binding EndAnimationWidth}" DecelerationRatio="0.9" Duration="0:0:0.6"/>
</Storyboard>
The StartAnimationWidth and EndAnimationWidth are properties of type GridLength and are defined in ViewModel
private GridLength _endAnimationWidth = new GridLength(100);
public GridLength EndAnimationWidth
{
get => _endAnimationWidth;
set => SetProperty(ref _endAnimationWidth,value);
}
You can then trigger the animation from the code behind:
Storyboard sb = Resources["storyboardName"] as Storyboard;
sb.Begin();

Related

Adding an InfoBadge to NavigationViewItem

For some reason, InfoBadge can't be found in the NavigationViewItem type. I'm using the WinUI 3 template project and the documentation at https://learn.microsoft.com/en-us/windows/apps/design/controls/info-badge makes it seem like it's supported in WinUI 3.
<NavigationViewItem
x:Uid="Shell_PurchaseOrders"
helpers:NavigationHelper.NavigateTo="NewUITest.ViewModels.PurchaseOrdersViewModel">
<NavigationViewItem.Icon>
<FontIcon
FontFamily="{StaticResource SymbolThemeFontFamily}"
Glyph=""/>
</NavigationViewItem.Icon>
<NavigationViewItem.InfoBadge>
<InfoBadge
x:Name="bg1"
Value="1"
Visibility="true"/>
</NavigationViewItem.InfoBadge>
</NavigationViewItem>
Do I have to add any other references?
x:Class="NewUITest.Views.ShellPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:helpers="using:NewUITest.Helpers"
xmlns:behaviors="using:NewUITest.Behaviors"
xmlns:i="using:Microsoft.Xaml.Interactivity"
Loaded="OnLoaded">
UPDATE
As I mentioned in the comments, unfortunately InfoBadge is not available in WinUI 3 (yet?).
InfoBadge is available since WinAppSDKv1.2. So you don't need the workaround below.
Workaround for WinAppSDKv1.1
Meanwhile, you can create a customized NavigationViewItem:
using Microsoft.UI;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Shapes;
using System;
using Windows.UI;
namespace CustomControls;
[TemplatePart(Name = nameof(NVIRootGrid), Type = typeof(Grid))]
public sealed class NavigationViewItemWithInfoBadge : NavigationViewItem
{
public static readonly DependencyProperty ShowDotInfoBadgeProperty = DependencyProperty.Register(
nameof(ShowDotInfoBadge),
typeof(bool),
typeof(NavigationViewItemWithInfoBadge),
new PropertyMetadata(default, OnShowDotInfoBadgePropertyChanged));
public static readonly DependencyProperty InfoBadgeBackgroundProperty = DependencyProperty.Register(
nameof(InfoBadgeBackground),
typeof(Brush),
typeof(NavigationViewItemWithInfoBadge),
new PropertyMetadata(default, OnInfoBadgeBackgroundPropertyChanged));
public bool ShowDotInfoBadge
{
get => (bool)GetValue(ShowDotInfoBadgeProperty);
set => SetValue(ShowDotInfoBadgeProperty, value);
}
public Brush InfoBadgeBackground
{
get => (Brush)GetValue(InfoBadgeBackgroundProperty);
set => SetValue(InfoBadgeBackgroundProperty, value);
}
private Grid? NVIRootGrid { get; set; }
private Ellipse InfoBadge { get; set; } = new()
{
Width = 10,
Height = 10,
Margin = new Thickness(10),
Visibility = Visibility.Collapsed,
HorizontalAlignment = HorizontalAlignment.Right,
VerticalAlignment = VerticalAlignment.Top,
};
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
// Get the root Grid of the NavigationViewItem and
// add the InfoBadge as its children.
if (GetTemplateChild(nameof(NVIRootGrid)) is Grid nviRootGrid)
{
NVIRootGrid = nviRootGrid;
NVIRootGrid.Children.Add(InfoBadge);
}
}
private static void OnInfoBadgeBackgroundPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is NavigationViewItemWithInfoBadge control)
{
control.InfoBadge.Fill = e.NewValue as Brush;
}
}
private static void OnShowDotInfoBadgePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is NavigationViewItemWithInfoBadge control)
{
control.InfoBadge.Visibility = (e.NewValue is bool newValue && newValue is true)
? Visibility.Visible
: Visibility.Collapsed;
}
}
}
And use it like this:
<local:NavigationViewItemWithInfoBadge
Content="Some content"
InfoBadgeBackground="SkyBlue"
ShowDotInfoBadge="true" />

Having trouble with 2-way binding using a dependency property and another property in a WPF control

My solution has two projects: one is called ChartControl and the other is LineGridViewCh4. LineGridViewCh4 contains the view and viewmodel that uses the control ChartControl. I can change the property Lines and the view gets updated and updates the Lines dependency property and Lines property in ChartStyle.cs. This is working fine. What I want to make happen is communication in the other direction. For example. If the Lines property were to change (by mousedown in the red square to set the Lines property to 3) in ChartStyle.cs, I'd like to have the property Lines (in ChartStyle.cs) update the dependency property in LineChart.xaml.cs and the change reflected in the view LineGridControlView.xaml. I tried creating a binding statement in the ctor of LineChart.xaml.cs but I'm not sure how to do this or if this is the right approach. In LineChart.xaml.cs method SetLineChart() has a statement cs.Line = Line; and can only work in one direction. It seems like I need Line = cs.Line; somewhere for the reverse direction. Any help would be appreciated.
ChartStyle.cs
using Prism.Mvvm;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
namespace ChartControl.ChartModel
{
public class ChartStyle : BindableBase
{
public Canvas ChartCanvas { get; set; }
public double Lines { get; set; }
public void AddChartStyle()
{
for (int i = 1; i <= Lines; i++)
{
double dx = i * 10;
Line gridline = new Line();
gridline.Stroke = Brushes.Black;
gridline.StrokeThickness = 4;
gridline.X1 = new Point(dx, 0).X;
gridline.Y1 = new Point(dx, 0).Y;
gridline.X2 = new Point(dx, 100).X;
gridline.Y2 = new Point(dx, 100).Y;
ChartCanvas.Children.Add(gridline);
}
Rectangle setLines = new Rectangle
{
Fill = Brushes.Red,
StrokeThickness = 2
};
setLines.Width = 30;
setLines.Height = 30;
Canvas.SetTop(setLines, 110);
ChartCanvas.Children.Add(setLines);
setLines.MouseDown += new MouseButtonEventHandler(SetLines_MouseDown);
}
private void SetLines_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.Source is Rectangle)
{
Lines = 3;
}
}
}
}
LineChart.xaml
<UserControl x:Class="ChartControl.Views.LineChart"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True">
<Grid >
<Canvas Name="chartCanvas"/>
</Grid>
</UserControl>
LineChart.xaml.cs
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using ChartControl.ChartModel;
namespace ChartControl.Views
{
public partial class LineChart : UserControl
{
private ChartStyle cs;
public LineChart()
{
InitializeComponent();
cs = new ChartStyle();
cs.ChartCanvas = chartCanvas;
//Binding csLine = new Binding("Lines");
//csLine.Source = cs.Lines;
//Lines.SetBinding(LinesProperty, csLine);
}
protected override void OnRender(DrawingContext drawingContext)
{
SetLineChart();
}
private void SetLineChart()
{
cs.Lines = Lines;
RedrawLineChart();
}
private void RedrawLineChart()
{
chartCanvas.Children.Clear();
cs.AddChartStyle();
}
public static DependencyProperty LinesProperty =
DependencyProperty.Register("Lines", typeof(int),
typeof(LineChart),
new FrameworkPropertyMetadata(12, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault |
FrameworkPropertyMetadataOptions.AffectsRender));
public int Lines
{
get { return (int)GetValue(LinesProperty); }
set { SetValue(LinesProperty, value); }
}
}
}
LineGridControlView.xaml
<Window x:Class="LineGridViewCh4.Views.LineGridControlView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
xmlns:local="clr-namespace:ChartControl.Views;assembly=ChartControl"
prism:ViewModelLocator.AutoWireViewModel="True"
Height="220" Width="400">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Vertical" Grid.Column="1" Margin="5">
<StackPanel Orientation="Horizontal">
<Label Content="Lines" Width="60" Height="25"/>
<TextBox x:Name="tbLines" Width="50" Height="25" Text="{Binding Lines, ElementName=myChart}"/>
<Button Content="Set" Command="{Binding ChangeLinesCommand}" CommandParameter="{Binding Text, ElementName=tbLines}"/>
</StackPanel>
</StackPanel>
<local:LineChart x:Name="myChart" Margin="10"/>
</Grid>
</Window>
LineGridControlViewModel.cs
using Prism.Commands;
using Prism.Mvvm;
using System;
namespace LineGridViewCh4.ViewModels
{
public class LineGridControlViewModel : BindableBase
{
public DelegateCommand<string> ChangeLinesCommand { get; set; }
public LineGridControlViewModel()
{
ChangeLinesCommand = new DelegateCommand<string>(ChangeLines);
}
private int lines;
public int Lines
{
get { return lines; }
set { SetProperty(ref lines, value); }
}
public void ChangeLines(string numberOfLines)
{
Lines = Convert.ToInt32(numberOfLines);
}
}
}
Just add a PropertyChangedCallback Delegate, which represents the callback that is invoked when the effective property value of a dependency property changes.
https://learn.microsoft.com/en-us/dotnet/desktop/wpf/advanced/dependency-property-callbacks-and-validation?view=netframeworkdesktop-4.8
I was able to address this problem through inheritance. My base class is UserControl which ChartStyle now inherits from. Below are the changes:
ChartStyle.cs
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
namespace ChartControl.ChartModel
{
public class ChartStyle : UserControl
{
public Canvas ChartCanvas { get; set; }
public static DependencyProperty LinesProperty =
DependencyProperty.Register("Lines", typeof(int),
typeof(ChartStyle),
new FrameworkPropertyMetadata(
12,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault |
FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(OnPropertyChanged)));
public int Lines
{
get { return (int)GetValue(LinesProperty); }
set { SetValue(LinesProperty, value); }
}
private static void OnPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
ChartStyle cs = sender as ChartStyle;
if (e.Property == LinesProperty)
cs.Lines = (int)e.NewValue;
}
public void AddChartStyle()
{
for (int i = 1; i <= Lines; i++)
{
double dx = i * 10;
Line gridline = new Line();
gridline.Stroke = Brushes.Black;
gridline.StrokeThickness = 4;
gridline.X1 = new Point(dx, 0).X;
gridline.Y1 = new Point(dx, 0).Y;
gridline.X2 = new Point(dx, 100).X;
gridline.Y2 = new Point(dx, 100).Y;
ChartCanvas.Children.Add(gridline);
}
Rectangle setLines = new Rectangle
{
Fill = Brushes.Red,
StrokeThickness = 2
};
setLines.Width = 30;
setLines.Height = 30;
Canvas.SetTop(setLines, 110);
ChartCanvas.Children.Add(setLines);
setLines.MouseDown += new MouseButtonEventHandler(SetLines_MouseDown);
}
private void SetLines_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.Source is Rectangle)
{
Lines = 3;
}
}
}
}
LineChart.xaml.cs
using ChartControl.ChartModel;
using System.Windows.Media;
namespace ChartControl.Views
{
public partial class LineChart : ChartStyle
{
public LineChart()
{
InitializeComponent();
ChartCanvas = chartCanvas;
}
protected override void OnRender(DrawingContext drawingContext)
{
RedrawLineChart();
}
private void RedrawLineChart()
{
chartCanvas.Children.Clear();
AddChartStyle();
}
}
}
LineChart.xaml
<local:ChartStyle x:Class="ChartControl.Views.LineChart"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
xmlns:prism="http://prismlibrary.com/"
xmlns:local="clr-namespace:ChartControl.ChartModel"
prism:ViewModelLocator.AutoWireViewModel="True">
<Grid >
<Canvas Name="chartCanvas"/>
</Grid>
</local:ChartStyle>

How to create a drag and drop interface for audio files

I'm trying to create an interface that allows the user to drag in an mp3 or mp4 file and get the file path.
I created a rectangle to represent the drop area, but I'm struggling with the code for the View Model
<Rectangle x:Name="MyRectangle"
Width="200"
Height="200"
Fill="Gray"
Drop="MyRectangle_Drop"
AllowDrop="True"/>
If you are using MVVM structure with Dependency Injection, create a public class. Here is an example of what I did.
using System.Windows;
using System.Windows.Input;
namespace Test.Common
{
public class Behaviors
{
public static readonly DependencyProperty DropFileCommandProperty =
DependencyProperty.RegisterAttached("DropFileCommand", typeof(ICommand),
typeof(Behaviors), new FrameworkPropertyMetadata(
new PropertyChangedCallback(DropFileCommandChanged)));
private static void DropFileCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FrameworkElement element = (FrameworkElement)d;
element.Drop += Element_DropFile;
}
private static void Element_DropFile(object sender, DragEventArgs e)
{
FrameworkElement element = (FrameworkElement)sender;
ICommand command = GeDropFileCommand(element);
command.Execute(e);
}
public static void SetDropFileCommand(UIElement element, ICommand value)
{
element.SetValue(DropFileCommandProperty, value);
}
public static ICommand GeDropFileCommand(UIElement element)
{
return (ICommand)element.GetValue(DropFileCommandProperty);
}
}
}
you can now in your view reference your class like this.
<Window x:Class="Test.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:common="clr-namespace:Test.Common"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
AllowDrop="True"
common:Behaviors.DropFileCommand="{Binding DropFile}"
Title="{Binding Title}">
<Grid>
</Grid>
</Window>
Now on your ViewModel you can do the following.
using Prism.Commands;
using Prism.Mvvm;
using System.Windows;
namespace Test.Views
{
public class MainWindowViewModel : BindableBase
{
private string _title = "TestDrop";
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
public MainWindowViewModel()
{
DropFile = new DelegateCommand<DragEventArgs>(dropFile);
}
public DelegateCommand<DragEventArgs> DropFile { get; }
private void dropFile(DragEventArgs obj)
{
var files = obj.Data.GetData(DataFormats.FileDrop, true) as string[];
//implement rest of code here
}
}
}
In your MyRectangle_Drop EventHandler, try this statement to get the directories of the dropped files.
var directories = (string[])e.Data.GetData(DataFormats.FileDrop);

How to create left and right swipes in WPF using Mouse? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I'm very new to C#.So, i'm trying create a simple swipe function in my WPF where if i swipe left or right, it goes to another wpf window. Please help me! I cannot find much resources online.
So my question is how to swipe using mouse in wpf application, so that i can switch between pages/window using mouse swipe.
I'm just trying to do like an image Carousel. I have so far followed this WPF image swipe to change image like in iOS
But, it doesn't swipe but zooms in and out when mouse is moved.
I am Using Pages but you can use window also.
1st. Create two Pages LeftPage.xaml, and RightPage.Xaml
and following code to MainWindow.xaml and MainWindows.xaml.cs
XAML
MainWindow
<Window x:Class="SOWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SOWPF"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
MouseDown="Window_MouseDown" MouseMove="Window_MouseMove">
<Grid>
<Frame x:Name="MainFrame" NavigationUIVisibility="Hidden" />
</Grid>
C#
public partial class MainWindow : Window
{
protected Point SwipeStart;
public MainWindow()
{
InitializeComponent();
MainFrame.Source = new Uri("LeftPage.xaml", UriKind.RelativeOrAbsolute);
}
private void Window_MouseDown(object sender, MouseEventArgs e)
{
SwipeStart = e.GetPosition(this);
}
private void Window_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
var Swipe = e.GetPosition(this);
//Swipe Left
if (SwipeStart != null && Swipe.X > (SwipeStart.X + 200))
{
// OR Use Your Logic to switch between pages.
MainFrame.Source = new Uri("LeftPage.xaml", UriKind.RelativeOrAbsolute);
}
//Swipe Right
if (SwipeStart != null && Swipe.X < (SwipeStart.X - 200))
{
// OR Use Your Logic to switch between pages.
MainFrame.Source = new Uri("RightPage.xaml", UriKind.RelativeOrAbsolute);
}
}
e.Handled = true;
}
}
I've created a Behavior so that the whole thing can be done without the need for any code behind. The good thing of using a Behavior is that you can reuse it wherever in your solution, unit test it to ensure it does as you want or extend it's functionality.
Main Window
<Window x:Class="TestWpfApplication.MainWindowView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:testWpfApplication="clr-namespace:TestWpfApplication"
mc:Ignorable="d"
Title="MainWindow"
Height="450"
Width="800">
<i:Interaction.Behaviors>
<testWpfApplication:SwipeBehavior TargetContentControl="{Binding ElementName=MainContentControl}" LeftUserControl="{Binding Path=LeftControl}" RightUserControl="{Binding Path=RightControl}" />
</i:Interaction.Behaviors>
<Grid>
<ContentControl Name="MainContentControl" />
</Grid>
</Window>
Main Window Code Behind
using System.Windows;
namespace TestWpfApplication
{
public partial class MainWindowView : Window
{
private readonly MainWindowViewModel _mainWindowViewModel = new MainWindowViewModel();
public MainWindowView()
{
InitializeComponent();
DataContext = _mainWindowViewModel;
}
}
}
Swipe Behavior
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interactivity;
namespace TestWpfApplication
{
public class SwipeBehavior : Behavior<Window>
{
public static readonly DependencyProperty TargetContentControlProperty = DependencyProperty.RegisterAttached("TargetContentControl", typeof(ContentControl), typeof(SwipeBehavior), new UIPropertyMetadata(null));
public static readonly DependencyProperty LeftUserControlProperty = DependencyProperty.RegisterAttached("LeftUserControl", typeof(UserControl), typeof(SwipeBehavior), new UIPropertyMetadata(null));
public static readonly DependencyProperty RightUserControlProperty = DependencyProperty.RegisterAttached("RightUserControl", typeof(UserControl), typeof(SwipeBehavior), new UIPropertyMetadata(null));
public static ContentControl GetTargetContentControl(DependencyObject dependencyObject)
{
return (ContentControl) dependencyObject.GetValue(TargetContentControlProperty);
}
public static void SetTargetContentControl(DependencyObject dependencyObject, ContentControl value)
{
dependencyObject.SetValue(TargetContentControlProperty, value);
}
public static ContentControl GetLeftUserControl(DependencyObject dependencyObject)
{
return (UserControl) dependencyObject.GetValue(LeftUserControlProperty);
}
public static void SetLeftUserControl(DependencyObject dependencyObject, UserControl value)
{
dependencyObject.SetValue(LeftUserControlProperty, value);
}
public static ContentControl GetRightUserControl(DependencyObject dependencyObject)
{
return (UserControl) dependencyObject.GetValue(RightUserControlProperty);
}
public static void SetRightUserControl(DependencyObject dependencyObject, UserControl value)
{
dependencyObject.SetValue(RightUserControlProperty, value);
}
private Point _swipeStart;
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.MouseDown += OnMouseDown;
AssociatedObject.MouseMove += OnMouseMove;
}
private void OnMouseDown(object sender, MouseButtonEventArgs e)
{
_swipeStart = e.GetPosition(AssociatedObject);
}
private void OnMouseMove(object sender, MouseEventArgs e)
{
var targetContentControl = GetValue(TargetContentControlProperty) as ContentControl;
if (targetContentControl == null)
{
return;
}
if (e.LeftButton == MouseButtonState.Pressed)
{
var swipe = e.GetPosition(AssociatedObject);
//Swipe Left
if (swipe.X > (_swipeStart.X + 200))
{
// OR Use Your Logic to switch between pages.
targetContentControl.Content = new LeftControl();
}
//Swipe Right
if (swipe.X < (_swipeStart.X - 200))
{
// OR Use Your Logic to switch between pages.
targetContentControl.Content = new RightControl();
}
}
e.Handled = true;
}
}
}
Main Window View Model
using System.Windows.Controls;
namespace TestWpfApplication
{
internal class MainWindowViewModel
{
public UserControl LeftControl { get; } = new LeftControl();
public UserControl RightControl { get; } = new RightControl();
}
}
Note: The LeftControl & RightControl are WPF User Controls in this example. Also you must reference System.Window.Interactivity in your project in order to use the Behavior class

Scroller-like animation on a Label in WPF

I have a simple app where after clicking a button the value of a label is updated every second.I'm doing this as a POC for a progress bar control that I want to develop.
I would like to know if there is a way to apply some kind of scroller animation to the label which will:
1) When the content of a label is updated it will scroll the new value from the top and the old one will be scrolled down and disappear from view(Hope this makes sence).
I know that this could probably be achieved with some kind of animation but I couldn't find any helpful examples on the web if anyone knows how this can be done please share your expertise:
View:
<Window x:Class="WpfApplication1.ScrollerView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Scroller" DataContext="{StaticResource scrollerVM}" Height="150" Width="300">
<Grid>
<ListBox ItemsSource="{Binding Messages}" Width="200" Height="50" BorderThickness="0" VerticalAlignment="Top" HorizontalAlignment="Left">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Text}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListBox>
<Button Width="70" Height="24" Content="Add new" Command="{Binding AddNew}" HorizontalAlignment="Left" Margin="0,56,0,30" />
</Grid>
</Window>
View model:
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Threading;
namespace WpfApplication1.Scroller
{
public class Message
{
public Message(string _text)
{
text = _text;
}
private string text;
public string Text
{
get { return text; }
set {text = value;}
}
}
public class ScrollerViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public DelegateCommand AddNew { get; protected set; }
ObservableCollection<Message> _messages = new ObservableCollection<Message>();
public ObservableCollection<Message> Messages
{
get { return _messages; }
set
{
_messages = value;
OnPropertyChanged("Messages");
}
}
public ScrollerViewModel()
{
AddNew = new DelegateCommand(Add);
}
private void Add(object parameter)
{
DispatcherTimer timer = new DispatcherTimer();
timer.Tick += new System.EventHandler(timer_Tick);
timer.Interval = new System.TimeSpan(0, 0, 1);
timer.Start();
}
private void timer_Tick(object sender, EventArgs e)
{
Messages.Clear();
Messages.Add(new Message(DateTime.Now.ToString("ss")));
}
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
More comprehensive/different examples here.
The following will result in a basic vertical marquee (scrolling text block).
XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Loaded="Window_Loaded">
<Canvas Name="canvas1" >
<TextBlock Name="textBlock1">Hello</TextBlock>
</Canvas>
</Window>
Code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void BeginAnimation()
{
DoubleAnimation doubleAnimation = new DoubleAnimation();
doubleAnimation.From = -textBlock1.ActualHeight;
doubleAnimation.To = canvas1.ActualHeight;
doubleAnimation.RepeatBehavior = RepeatBehavior.Forever;
doubleAnimation.Duration = new Duration(TimeSpan.FromSeconds(3));
textBlock1.BeginAnimation(Canvas.TopProperty, doubleAnimation);
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
BeginAnimation();
}
}
First, you'll want "smooth scrolling" on the ListBox:
ScrollViewer.CanContentScroll="False"
Then, you could create a custom Attached Property to specify the vertical offset you want to scroll. Then create a custom Behavior that hooks up to the ListBox's ItemsSource's "ItemsSourceChanged" event, which would fire off an animation that you can define inside the behavior. That should at least be a start. I'm not sure what the specific animation would be...some DoubleAnimation using a calculation of your offset plus new item's height.

Categories

Resources