当前位置: 动力学知识库 > 问答 > 编程问答 >

c# - Creating a endless centered carousel control in wpf

问题描述:

I'm trying to create a endless, centered carousel in WPF like in this concept image. The current solution I've come up with is using a listbox, loading all images into a ObservableCollection and then modifying it to create the illusion of movement.

I have two issues with this solution. First I can't seem to center it. The listbox is aligned to the left with no way of getting it to overflow on both sides. Regardless of the size of my window it should always show one console in the middle, one on each sides and a half one to indicate that there's more to choose from.

The second issue is not as important, but I'm looking for a proper way of doing this that may allow a more fluent transition between selections later on.

This is my current code:

XAML:

<Window x:Class="SystemMenu.MainWindow"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Title="MainWindow" Height="350" Width="525">

<DockPanel>

<Button Content="left" Height="20" Click="Left_Click" DockPanel.Dock="Top" />

<Button Content="right" Height="20" Click="Right_Click" DockPanel.Dock="Top" />

<ListBox x:Name="LoopPanel" ItemsSource="{Binding Path=SampleData}" SelectedIndex="3" ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.VerticalScrollBarVisibility="Hidden" ScrollViewer.CanContentScroll="False">

<ListBox.ItemsPanel>

<ItemsPanelTemplate>

<WrapPanel IsItemsHost="True" />

</ItemsPanelTemplate>

</ListBox.ItemsPanel>

<ListBox.ItemTemplate>

<DataTemplate>

<Image Source="{Binding}"/>

</DataTemplate>

</ListBox.ItemTemplate>

</ListBox>

</DockPanel>

Code behind:

 public partial class MainWindow : Window

{

public MainWindow()

{

InitializeComponent();

this.DataContext = this;

}

ObservableCollection<string> sampleData = new ObservableCollection<string>();

public ObservableCollection<string> SampleData

{

get

{

if (sampleData.Count <= 0)

{

sampleData.Add(@"Nintendo 64.png");

sampleData.Add(@"Nintendo Famicom.png");

sampleData.Add(@"Super Nintendo Entertainment System.png");

sampleData.Add(@"Nintendo Entertainment System.png");

sampleData.Add(@"Sony PlayStation.png");

}

return sampleData;

}

}

private void Right_Click(object sender, RoutedEventArgs e)

{

var firstItem = SampleData.First();

SampleData.Remove(firstItem);

SampleData.Insert(SampleData.Count, firstItem);

}

private void Left_Click(object sender, RoutedEventArgs e)

{

var lastItem = SampleData.Last();

SampleData.Remove(lastItem);

SampleData.Insert(0, lastItem);

}

}

Edit:

I found the following extension solving the issue I had with centering the listbox. Calling LoopPanel.ScrollToCenterOfView(sampleData[2]); seems to do the trick of centering the images... Any idea now on how to animate the transition? :)

 public static class ItemsControlExtensions

{

public static void ScrollToCenterOfView(this ItemsControl itemsControl, object item)

{

// Scroll immediately if possible

if (!itemsControl.TryScrollToCenterOfView(item))

{

// Otherwise wait until everything is loaded, then scroll

if (itemsControl is ListBox) ((ListBox)itemsControl).ScrollIntoView(item);

itemsControl.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() =>

{

itemsControl.TryScrollToCenterOfView(item);

}));

}

}

private static bool TryScrollToCenterOfView(this ItemsControl itemsControl, object item)

{

// Find the container

var container = itemsControl.ItemContainerGenerator.ContainerFromItem(item) as UIElement;

if (container == null) return false;

// Find the ScrollContentPresenter

ScrollContentPresenter presenter = null;

for (Visual vis = container; vis != null && vis != itemsControl; vis = VisualTreeHelper.GetParent(vis) as Visual)

if ((presenter = vis as ScrollContentPresenter) != null)

break;

if (presenter == null) return false;

// Find the IScrollInfo

var scrollInfo =

!presenter.CanContentScroll ? presenter :

presenter.Content as IScrollInfo ??

FirstVisualChild(presenter.Content as ItemsPresenter) as IScrollInfo ??

presenter;

// Compute the center point of the container relative to the scrollInfo

Size size = container.RenderSize;

Point center = container.TransformToAncestor((Visual)scrollInfo).Transform(new Point(size.Width / 2, size.Height / 2));

center.Y += scrollInfo.VerticalOffset;

center.X += scrollInfo.HorizontalOffset;

// Adjust for logical scrolling

if (scrollInfo is StackPanel || scrollInfo is VirtualizingStackPanel)

{

double logicalCenter = itemsControl.ItemContainerGenerator.IndexFromContainer(container) + 0.5;

Orientation orientation = scrollInfo is StackPanel ? ((StackPanel)scrollInfo).Orientation : ((VirtualizingStackPanel)scrollInfo).Orientation;

if (orientation == Orientation.Horizontal)

center.X = logicalCenter;

else

center.Y = logicalCenter;

}

// Scroll the center of the container to the center of the viewport

if (scrollInfo.CanVerticallyScroll) scrollInfo.SetVerticalOffset(CenteringOffset(center.Y, scrollInfo.ViewportHeight, scrollInfo.ExtentHeight));

if (scrollInfo.CanHorizontallyScroll) scrollInfo.SetHorizontalOffset(CenteringOffset(center.X, scrollInfo.ViewportWidth, scrollInfo.ExtentWidth));

return true;

}

private static double CenteringOffset(double center, double viewport, double extent)

{

return Math.Min(extent - viewport, Math.Max(0, center - viewport / 2));

}

private static DependencyObject FirstVisualChild(Visual visual)

{

if (visual == null) return null;

if (VisualTreeHelper.GetChildrenCount(visual) == 0) return null;

return VisualTreeHelper.GetChild(visual, 0);

}

}

网友答案:

I don't think I would do it how you're doing it. I.e. adding and removing items in a ListBox. Doesn't give you enough control on the positioning and you won't be able to do smooth animations of it rotating which with that kind of UI, I think that would be kind of expected :).

I'd probably have a Canvas instead with ClipToBounds=true. Then just calculate the positions, you aren't doing a rounded carousel, so positions are trivial and there is no zooming.

Lets say your images are all 100 x 100. So item0 will be @ -50,0, item1 @ 50,0 (well, technically probably 75,0 or whatever because you'd want some spacing between them, but you get the idea), etc. Because you are calculating the positions and have them absolute against the Canvas, the ClipToBound=true will clip the two on either end and you'll be able to animate the rotation.

分享给朋友:
您可能感兴趣的文章:
随机阅读: