简易的DragDropCarousel 拖拽轮播控件
阅读原文时间:2023年07月10日阅读:1

上一篇文章有写到 自动轮播的控件  简易的AutoPlayCarousel 轮播控件 - 黄高林 - 博客园 (cnblogs.com)

本章是基于自动轮播的一种衍生,通过拖拽鼠标进切换

直接上代码

DragDropCarousel核心代码

[ContentProperty(nameof(ItemSources))]
[TemplatePart(Name = "PART_StackPanel", Type = typeof(StackPanel))]
public class DragDropCarousel : Control
{
#region 字段
private StackPanel _stkMain;
///

/// 标记是否点击 ///
private bool _isMouseDown;
/// /// 标记是否动画中,动画过程中不允许操作 ///
private bool _isAnimating;
/// /// 最后一次点击的位置 ///
private Point _lastDownPoint;
/// /// 移动中 ///
private bool _isMoving;
#endregion

    #region 构造  
    static DragDropCarousel()  
    {  
        DefaultStyleKeyProperty.OverrideMetadata(typeof(DragDropCarousel), new FrameworkPropertyMetadata(typeof(DragDropCarousel)));  
    }  
    public DragDropCarousel()  
    {  
        Loaded += Carousel\_Loaded;  
        SizeChanged += Carousel\_SizeChanged;  
    }  
    #endregion

    #region 属性  
    /// <summary>  
    /// 子控件集合  
    /// </summary>  
    public ObservableCollection<FrameworkElement> ItemSources  
    {  
        get => (ObservableCollection<FrameworkElement>)GetValue(ItemSourcesProperty);  
        private set => SetValue(ItemSourcesProperty, value);  
    }

    public static readonly DependencyProperty ItemSourcesProperty =  
        DependencyProperty.Register("ItemSources", typeof(ObservableCollection<FrameworkElement>), typeof(DragDropCarousel), new PropertyMetadata(new ObservableCollection<FrameworkElement>()));  
    /// <summary>  
    /// 方向  
    /// </summary>  
    public Orientation Orientation  
    {  
        get => (Orientation)GetValue(OrientationProperty);  
        set => SetValue(OrientationProperty, value);  
    }

    public static readonly DependencyProperty OrientationProperty =  
        DependencyProperty.Register("Orientation", typeof(Orientation), typeof(DragDropCarousel), new PropertyMetadata(Orientation.Horizontal));

    /// <summary>  
    /// 下标  
    /// </summary>  
    public int Index  
    {  
        get => (int)GetValue(IndexProperty);  
        set => SetValue(IndexProperty, value);  
    }

    public static readonly DependencyProperty IndexProperty =  
        DependencyProperty.Register("Index", typeof(int), typeof(DragDropCarousel), new PropertyMetadata(0));

    /// <summary>  
    /// 抬起手时,滑动切页动画持续时间  
    /// </summary>  
    public TimeSpan AnimateDuration  
    {  
        get => (TimeSpan)GetValue(AnimateDurationProperty);  
        set => SetValue(AnimateDurationProperty, value);  
    }

    public static readonly DependencyProperty AnimateDurationProperty =  
        DependencyProperty.Register("AnimateDuration", typeof(TimeSpan), typeof(DragDropCarousel), new PropertyMetadata(TimeSpan.FromSeconds(0.5)));

    /// <summary>  
    /// 移动阈值  
    /// </summary>  
    public double MoveThreshold  
    {  
        get => (double)GetValue(MoveThresholdProperty);  
        set => SetValue(MoveThresholdProperty, value);  
    }

    // Using a DependencyProperty as the backing store for MoveThreshold.  This enables animation, styling, binding, etc...  
    public static readonly DependencyProperty MoveThresholdProperty =  
        DependencyProperty.Register("MoveThreshold", typeof(double), typeof(DragDropCarousel), new PropertyMetadata(10d));

    #endregion

    #region 对外方法  
    public override void OnApplyTemplate()  
    {  
        base.OnApplyTemplate();  
        \_stkMain = GetTemplateChild("PART\_StackPanel") as StackPanel;  
    }  
    #endregion

    #region Event Handler

    private void Carousel\_SizeChanged(object sender, SizeChangedEventArgs e)  
    {  
        foreach (FrameworkElement children in ItemSources)  
        {  
            children.Width = ActualWidth;  
            children.Height = ActualHeight;  
            UnRegisterMouseEvent(children);  
            RegisterMouseEvent(children);  
        }  
    }  
    private void UnRegisterMouseEvent(FrameworkElement children)  
    {  
        children.MouseDown -= Children\_MouseDown;  
        children.MouseMove -= Children\_MouseMove;  
        children.MouseUp -= Children\_MouseUp;  
    }  
    private void RegisterMouseEvent(FrameworkElement children)  
    {  
        children.MouseDown += Children\_MouseDown;  
        children.MouseMove += Children\_MouseMove;  
        children.MouseUp += Children\_MouseUp;  
    }

    private void Children\_MouseUp(object sender, MouseButtonEventArgs e)  
    {  
        if (!\_isMouseDown || \_isAnimating||!\_isMoving)  
        {

_isMoving = false;
            _isMouseDown = false;

return;

_}
_isMoving=false;
_isMouseDown = false;
var targetIndex = GetTargetIndex(e, _lastDownPoint);
Index = targetIndex;
var newThickness = new Thickness(-1 * ActualWidth * targetIndex, 0, 0, 0);
var oldThickness = _stkMain.Margin;
var thicknessAnimation = new ThicknessAnimation()
{
From = oldThickness,
To = newThickness,
Duration = AnimateDuration,
EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut },
FillBehavior = FillBehavior.Stop,
};
thicknessAnimation.Completed += (s, args) =>
{
_isAnimating = false;
_stkMain.Margin = newThickness;
};
_isAnimating = true;
_stkMain.BeginAnimation(MarginProperty, thicknessAnimation);
}

    private int GetTargetIndex(MouseButtonEventArgs e, Point lastDownPoint)  
    {  
        var position = e.GetPosition(this);  
        var vector = position - lastDownPoint;  
        int targetIndex;  
        if (vector.X < 0)  
        {  
            targetIndex = Index + 1 > (ItemSources.Count - 1) ? Index : Index + 1;  
        }  
        else  
        {  
            targetIndex = Index - 1 >= 0 ? Index - 1 : 0;  
        }  
        return targetIndex;  
    }

    private void Children\_MouseMove(object sender, MouseEventArgs e)  
    {

        var position = e.GetPosition(this);  
        if (!IsCanMove(\_lastDownPoint, position))  
        {  
            \_stkMain.Margin = new Thickness((-1 \* ActualWidth \* Index), 0, 0, 0);  
            return;  
        }  
        var moveLength = \_lastDownPoint.X - position.X;  
        \_stkMain.Margin = new Thickness((-1 \* ActualWidth \* Index - moveLength), 0, 0, 0);  
    }

    private bool IsCanMove(Point lastPoint, Point currentPoint)  
    {  
        if (!\_isMouseDown)  
        {  
            return false;  
        }

        //以下控制不允许超出边界,想要回弹效果,可以去掉这部分代码  
        var vector = currentPoint - lastPoint;  
        if (!\_isMoving && Math.Abs(vector.X) <= MoveThreshold)  
        {  
            return false;  
        }

        \_isMoving = true;  
        if (vector.X < 0)  
        {  
            return Index != ItemSources.Count - 1;  
        }

        return Index != 0;

    }

    private void Children\_MouseDown(object sender, MouseButtonEventArgs e)  
    {  
        if (!(MoveElementAttach.GetAllowMove(e.Source as UIElement) ?? false) || \_isAnimating)  
        {  
            \_isMouseDown = false;  
            return;  
        }  
        \_isMouseDown = true;  
        \_lastDownPoint = e.GetPosition(this);  
    }

    private void Carousel\_Loaded(object sender, RoutedEventArgs e)  
    {  
        if (ItemSources == null)  
            return;  
        Loaded -= Carousel\_Loaded;  
        foreach (FrameworkElement child in ItemSources)  
        {  
            child.Width = ActualWidth;  
            child.Height = ActualHeight;  
        }  
    }  
    #endregion

}_ 

  一些辅助的代码

public class IndexChangedEventArgs : RoutedEventArgs
{
public IndexChangedEventArgs(int currentIndex, RoutedEvent routedEvent) : base(routedEvent)
{
CurrentIndex = currentIndex;
}

    public int CurrentIndex { get; set; }  
}

public delegate void IndexChangedEventHandler(object sender, IndexChangedEventArgs e);

///

/// 移动附加属性 ///
public class MoveElementAttach : DependencyObject
{
/// 允许操作
public static readonly DependencyProperty AllowMoveProperty = DependencyProperty.RegisterAttached("AllowMove", typeof(bool), typeof(MoveElementAttach), new PropertyMetadata((PropertyChangedCallback)null));

    public static bool? GetAllowMove(DependencyObject obj) => (bool?)obj?.GetValue(MoveElementAttach.AllowMoveProperty);

    public static void SetAllowMove(DependencyObject obj, bool? value) => obj.SetValue(MoveElementAttach.AllowMoveProperty, (object)value);  
}

默认样式

14
false

<Style TargetType="{x:Type local:DragDropCarousel}">  
    <Setter Property="SnapsToDevicePixels" Value="{StaticResource DefaultSnapsToDevicePixels}" />  
    <Setter Property="FontSize" Value="{StaticResource DefaultFontSize}" />  
    <Setter Property="Template">  
        <Setter.Value>  
            <ControlTemplate TargetType="{x:Type local:DragDropCarousel}">  
                <StackPanel x:Name="PART\_StackPanel" Orientation="Horizontal">  
                    <ItemsControl x:Name="PART\_ItemsControl"   ItemsSource="{TemplateBinding ItemSources}"  
                                      VerticalAlignment="Stretch"  
                                      HorizontalAlignment="Stretch">  
                        <ItemsControl.ItemsPanel>  
                            <ItemsPanelTemplate>  
                                <StackPanel Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" Orientation="{Binding Orientation,RelativeSource={RelativeSource AncestorType=local:DragDropCarousel}}"/>  
                            </ItemsPanelTemplate>  
                        </ItemsControl.ItemsPanel>  
                    </ItemsControl>  
                </StackPanel>  
            </ControlTemplate>  
        </Setter.Value>  
    </Setter>  
</Style>

界面的使用



                <TextBlock Text="1" FontSize="20" Foreground="Wheat" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>  
            </Grid>  
        </Grid>  
        <Grid Background="Green" customControl:MoveElementAttach.AllowMove="True" >

            <Grid HorizontalAlignment="Center" VerticalAlignment="Center" Height="500" Width="500" Background="Black">

                <TextBlock Text="2" FontSize="20" Foreground="Wheat" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>  
            </Grid>

        </Grid>  
        <Grid Background="Yellow" customControl:MoveElementAttach.AllowMove="True"  >  
            <Grid HorizontalAlignment="Center" VerticalAlignment="Center" Height="500" Width="500" Background="Black">

                <TextBlock Text="3" FontSize="20" Foreground="Wheat" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>  
            </Grid>  
        </Grid>  
        <Grid Background="Blue" customControl:MoveElementAttach.AllowMove="True"  >  
            <Grid HorizontalAlignment="Center" VerticalAlignment="Center" Height="500" Width="500" Background="Black">

                <TextBlock Text="4" FontSize="20" Foreground="Wheat" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>  
            </Grid>  
        </Grid>  
    </customControl:DragDropCarousel>

通过 customControl:MoveElementAttach.AllowMove="True" 来标记那些控件支持拖拽,哪些不支持拖拽,这种只是临时解决方案,其实还有其他的解决方案。后续我如果要用到再持续更新

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章