So I want the user to change the displayed window by dragging it sideways. It shouldn't matter where exactly on the screen is his cursor (so only the dragging action matter) I draw a little representation of what I have in mind. I want the user to see the 2nd screen coming after the cursor, even retracting when he moves his cursor back to the right (basically following his cursor). This action should be both ways: from main window to 2nd window AND from 2nd window back to main window How would you approach this?
EDIT: Picture 1; The user places his cursor on point A and clicks. While holding click he drags it across the green arrow.
Picture2; This picture represents and intermediate state when the "slide" is still in progress (you can see the "Statistics" screen still hasn't fully taken over the initial screen)
Picture3; Represents the final state; after the user got at the end of the (imaginary) green arrow. The Statistics screen is now fully displayed and the user can read the information that is on it.
The opposite of what happened now should be allowed (dragging from left to right in order to go back to the initial screen)
CodePudding user response:
You need to apply a translate transform on your content.
The following example shows how to drag the content (or the image of the content). For simplicity, the example only shows how to swipe from right to left. It also doesn't show how to implement a history to navigate back. You would need a Queue
to store the next pages and a Stack
for the previous pages (based on the swipe direction).
Although the below example does it, I don't recommend to use controls directly to handle pages and their navigation. Instead create page data models and render the controls using a DataTemplate
for each page model.
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public object CurrentPage
{
get => (object)GetValue(CurrentPageProperty);
set => SetValue(CurrentPageProperty, value);
}
public static readonly DependencyProperty CurrentPageProperty = DependencyProperty.Register(
"CurrentPage",
typeof(object),
typeof(MainWindow),
new PropertyMetadata(default));
private StackPanel DraggableContent { get; }
// Accept swipe after 25% swipe delta based on two displayed pages
private double SwipeAcceptDragThreshold => this.PageHost.ActualWidth / 2 * 0.25;
private UserControl PreviousPage { get; set; }
private UserControl NextPage { get; set; }
private UserControl GreenPage { get; }
private UserControl OrangePage { get; }
private bool IsSwiping { get; set; }
private double HorizontalDragStart { get; set; }
public MainWindow()
{
InitializeComponent();
this.GreenPage = new PageControl() { Background = Brushes.Green };
this.OrangePage = new PageControl() { Background = Brushes.Orange };
this.CurrentPage = this.GreenPage;
this.NextPage = this.OrangePage;
this.DraggableContent = new StackPanel() { Orientation = Orientation.Horizontal };
}
protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnPreviewMouseLeftButtonDown(e);
this.HorizontalDragStart = e.GetPosition(this).X;
}
protected override void OnPreviewMouseMove(MouseEventArgs e)
{
base.OnPreviewMouseMove(e);
if (e.LeftButton == MouseButtonState.Released)
{
return;
}
double dragDelta = e.GetPosition(this).X - this.HorizontalDragStart;
if (!this.IsSwiping)
{
bool isSwipeRightToLeft = dragDelta < HorizontalDragStart;
UserControl swipeInPage = null;
UserControl swipeOutPage = null;
if (isSwipeRightToLeft)
{
if (this.NextPage == null)
{
return;
}
swipeInPage = this.NextPage;
swipeOutPage = this.CurrentPage as UserControl;
this.PreviousPage = this.CurrentPage as UserControl;
}
this.IsSwiping = true;
/* Create an image of the content that will be dragged, using VisualBrush */
swipeInPage.Height = this.PageHost.ActualHeight;
swipeInPage.Width = this.PageHost.ActualWidth;
swipeOutPage.Height = this.PageHost.ActualHeight;
swipeOutPage.Width = this.PageHost.ActualWidth;
this.CurrentPage = null;
this.DraggableContent.Children.Add(swipeOutPage);
this.DraggableContent.Children.Add(swipeInPage);
this.CurrentPage = new Grid()
{
Background = new VisualBrush(DraggableContent),
Width = this.PageHost.ActualWidth * 2 // Host two pages
};
}
this.TranslateTransform.X = dragDelta;
}
protected override void onm ouseLeftButtonUp(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonUp(e);
if (this.IsSwiping)
{
this.IsSwiping = false;
double dragDelta = Math.Abs(e.GetPosition(this).X - this.HorizontalDragStart);
bool isDragAccepted = dragDelta > this.SwipeAcceptDragThreshold;
this.DraggableContent.Children.Clear();
this.CurrentPage = isDragAccepted
? NextPage
: this.PreviousPage;
this.TranslateTransform.X = 0;
// TODO::Generate and set next page or null if last page reached
// this.NextPage = null;
}
}
}
MainWindow.xaml
<Window>
<!-- Allow content to exceed the Window -->
<ScrollViewer HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden">
<ContentPresenter x:Name="PageHost"
Content="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=CurrentPage}">
<ContentPresenter.RenderTransform>
<TranslateTransform x:Name="TranslateTransform" />
</ContentPresenter.RenderTransform>
</ContentPresenter>
</ScrollViewer>
</Window>
PageControl.xaml
<UserControl>
<Grid Background="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=Background}" />
</UserControl>