上一回实现了一个宽度不均匀的Panel,这次我们编写一个简单的BigbangView主体。

首先创建一个模板化控件,删掉Themes/Generic.xaml中的<Style TargetType="BigbangView">...</Style>段。

然后打开C:\Program Files (x86)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\(SDK版本)\Generic\generic.xaml,在里面找到

<Style TargetType="ListViewItem" x:Key="ListViewItemExpanded">
...
</Style>
<Style TargetType="ListView">
...
</Style>

这两段,复制到项目中Themes/Generic.xaml中,将TargetType="ListView"修改为TargetType="BigbangView",添加Setter:

<Setter Property="SelectionMode" Value="Multiple"></Setter>
<Setter Property="HorizontalAlignment" Value="Stretch"></Setter>
<Setter Property="VerticalAlignment" Value="Center"></Setter>
<Setter Property="IsTabStop" Value="False" />
<Setter Property="TabNavigation" Value="Once" />
<Setter Property="IsSwipeEnabled" Value="True" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
<Setter Property="ScrollViewer.HorizontalScrollMode" Value="Disabled" />
<Setter Property="ScrollViewer.IsHorizontalRailEnabled" Value="False" />
<Setter Property="ScrollViewer.VerticalScrollMode" Value="Enabled" />
<Setter Property="ScrollViewer.IsVerticalRailEnabled" Value="True" />
<Setter Property="ScrollViewer.ZoomMode" Value="Disabled" />
<Setter Property="ScrollViewer.IsDeferredScrollingEnabled" Value="False" />
<Setter Property="ScrollViewer.BringIntoViewOnFocusChange" Value="True" />
<Setter Property="UseSystemFocusVisuals" Value="True" />
<Setter Property="ItemContainerTransitions">
<Setter.Value>
<TransitionCollection>
<AddDeleteThemeTransition />
<ContentThemeTransition />
<ReorderThemeTransition />
<EntranceThemeTransition IsStaggeringEnabled="False" />
</TransitionCollection>
</Setter.Value>
</Setter>
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="ListViewItem">
前面复制的ListViewItemExpanded的内容剪贴到这里
</Style>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<control:BigbangPanel >
<control:BigbangPanel.ChildrenTransitions>
<TransitionCollection>
<AddDeleteThemeTransition />
</TransitionCollection>
</control:BigbangPanel.ChildrenTransitions>
</control:BigbangPanel>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>

其中BigbangPanel是咱们上回书写的面板。

然后打开BigbangView.cs,修改基类:

public sealed class BigbangView : ListView
{
public BigbangView()
{
this.DefaultStyleKey = typeof(BigbangView);
}
}

  

接下来就是整个过程中最复杂,最枯燥的部分,编写按下滑动选中。

先打开安卓版的大爆炸(不是锤子的可以拿个安卓手机下载IT之家客户端看效果),对整个过程进行分析发现,有以下几种状态。

1、点击选中;

2、Panel高度小于控件高度,也就是ScrollViewer不启用时,按下向四周滑动可以更改选中状态;

3、Panel高度大于控件高度,上下滑动可以滚动ScrollViewer,左右滑动禁用ScrollViewer的滚动并且更改选中状态 。

这篇文章先实现鼠标的操作。由于鼠标在页面上下滑动并不会触发ScrollViewer的滚动,所以在此不考虑第三项。

首先修改Style中的Template段,

<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="control:BigbangView">
<Border x:Name="RootBorder" BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer x:Name="ScrollViewer"
TabNavigation="{TemplateBinding TabNavigation}"
HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}"
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
IsHorizontalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsHorizontalScrollChainingEnabled}"
VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}"
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
IsVerticalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsVerticalScrollChainingEnabled}"
IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}"
IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}"
ZoomMode="{TemplateBinding ScrollViewer.ZoomMode}"
IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}"
BringIntoViewOnFocusChange="{TemplateBinding ScrollViewer.BringIntoViewOnFocusChange}"
AutomationProperties.AccessibilityView="Raw">
<Grid x:Name="ItemsGrid" Background="Transparent" ManipulationMode="System">
<ItemsPresenter
Header="{TemplateBinding Header}"
HeaderTemplate="{TemplateBinding HeaderTemplate}"
HeaderTransitions="{TemplateBinding HeaderTransitions}"
Footer="{TemplateBinding Footer}"
FooterTemplate="{TemplateBinding FooterTemplate}"
FooterTransitions="{TemplateBinding FooterTransitions}"
Padding="{TemplateBinding Padding}"/>
</Grid>
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>

在后台代码中重载OnApplyTemplate()(实际应该做空判断,我偷懒了)

public BigbangView()
{
this.DefaultStyleKey = typeof(BigbangView);
this.Loaded += BigbangView_Loaded;
this.Unloaded += BigbangView_Unloaded;
this.SelectionChanged += BigbangView_SelectionChanged; PointerPressedHandler = new PointerEventHandler(_PointerPressed);
PointerReleasedHandler = new PointerEventHandler(_PointerReleased);
PointerMovedHandler = new PointerEventHandler(_PointerMoved);
} protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
RootBorder = GetTemplateChild("RootBorder") as Border;
ItemsGrid = GetTemplateChild("ItemsGrid") as Grid;
ScrollViewer = GetTemplateChild("ScrollViewer") as ScrollViewer; ScrollViewer.AddHandler(UIElement.PointerPressedEvent, PointerPressedHandler, true);
ScrollViewer.AddHandler(UIElement.PointerReleasedEvent, PointerReleasedHandler, true);
ScrollViewer.AddHandler(UIElement.PointerCanceledEvent, PointerReleasedHandler, true);
ScrollViewer.AddHandler(UIElement.PointerExitedEvent, PointerReleasedHandler, true);
ScrollViewer.ViewChanging += _ScrollViewer_ViewChanging;
}

  

然后我们需要把每个子元素在Panel中的位置缓存下来,在Panel中添加属性

private Dictionary<UIElement, Rect> _ChildrenRects;

public Dictionary<UIElement, Rect> ChildrenRects
{
get
{
if (_ChildrenRects == null) _ChildrenRects = new Dictionary<UIElement, Rect>();
return _ChildrenRects;
}
set => _ChildrenRects = value;
}

  

将ArrangeOverride中Children[x].Arrange(new Rect...)修改为如下

var rect = new Rect(x, y, Children[i].DesiredSize.Width, Children[i].DesiredSize.Height);
Children[i].Arrange(rect);
ChildrenRects[Children[i]] = rect;

  

编写以下几个辅助的方法:

//从Item获取在Panel中的布局信息
private Rect? GetItemRect(object Item)
{
var itemContainer = base.ContainerFromItem(Item) as UIElement;
if (itemContainer != null && _Panel != null)
{
if (_Panel.ChildrenRects.ContainsKey(itemContainer))
{
return _Panel.ChildrenRects[itemContainer];
}
}
return null;
} //从容器获取Item和Index
private int GetIndexFromContainer(UIElement Container, out object Item, IList<object> SourceList = null)
{
Item = ItemFromContainer(Container);
if (Item != null)
{
if (SourceList == null) SourceList = GetSourceList();
return SourceList.IndexOf(Item);
}
return -1;
} //获取坐标位置的Item和Index
private int GetIndexFromPosition(Point Position, out object Item)
{
var sourceList = GetSourceList(); for (int i = 0; i < sourceList.Count; i++)
{
var rect = GetItemRect(sourceList[i]);
if (!rect.HasValue) break; if (rect.Value.Contains(Position))
{
Item = sourceList[i];
return i;
}
}
Item = null;
return -1;
} //获取源列表
private IList<object> GetSourceList()
{
if (ItemsSource == null) return Items.ToList();
else
{
var tmp = new List<object>();
foreach (var item in (IEnumerable)ItemsSource)
{
tmp.Add(item);
}
return tmp;
}
}

  

然后编写三个状态方法:OnSelectionStart初始化各个变量的状态,获取开始的点;OnSelecting更新被选中的项,OnSelectionComplate做最后的清理,还原状态:

int StartIndex = -1;    //本次选择开始的位置
int EndIndex = -1; //本次选择结束的位置
bool? IsFirstItemHadSelected; //本次选择是选中后续还是取消选中后续
Point? StartPoint; //选中开始的坐标
UIElement StartContainer; //选择开始时的容器 private void OnSelectionStarted(Point Position)
{
var tmpIndex = GetIndexFromPosition(Position, out var item);
if (tmpIndex == -1)
{
return;
}
if (StartIndex < 0)
{
StartIndex = tmpIndex;
IsFirstItemHadSelected = SelectedItems.Contains(item);
StartContainer = ContainerFromItem(item) as UIElement;
}
} private void OnSelecting(UIElement Container)
{
if (IsFirstItemHadSelected.HasValue)
{
var sourceList = GetSourceList();
var tmpIndex = GetIndexFromContainer(Container, out var item, sourceList);
if (tmpIndex == -1) return;
if (StartIndex < 0)
{
StartIndex = tmpIndex;
return;
}
else
{
EndIndex = tmpIndex;
if (EndIndex >= 0)
{
for (int i = Math.Min(StartIndex, EndIndex); i <= Math.Max(StartIndex, EndIndex); i++)
{
if (IsFirstItemHadSelected.Value)
{
if (SelectedItems.Contains(sourceList[i])) SelectedItems.Remove(sourceList[i]);
}
else
{
if (!SelectedItems.Contains(sourceList[i])) SelectedItems.Add(sourceList[i]);
} }
} }
} } private void OnSelectionComplated()
{
StartIndex = -1;
EndIndex = -1;
IsFirstItemHadSelected = null;
StartPoint = null;
StartContainer = null;
}

  

然后编写事件:

private void Container_PointerEntered(object sender, PointerRoutedEventArgs e)
{
if (IsFirstItemHadSelected.HasValue)
{
if (sender is UIElement ele && ele != StartContainer)
{
if (StartContainer == null)
{
StartContainer = ele;
StartIndex = GetIndexFromContainer(StartContainer, out var item);
}
else
{
OnSelecting(ele);
}
}
}
} private void _PointerPressed(object sender, PointerRoutedEventArgs e)
{
StartPoint = e.GetCurrentPoint(ItemsGrid).Position;
OnSelectionStarted(StartPoint.Value);
IsSwipeEnable = true;
} private void _PointerReleased(object sender, PointerRoutedEventArgs e)
{
if (IsSwipeEnable.HasValue)
{
OnSelectionComplated();
}
}

  

至此,BigbangView已经可以响应鼠标的滑动选择。

下回预告:BigbangView响应触摸。

UWP中实现大爆炸效果(二)的更多相关文章

  1. UWP中实现大爆炸效果(一)

    自从老罗搞出大爆炸之后,各家安卓都内置了类似功能.UWP怎么能落下呢,在这里我们就一起撸一个简单的大爆炸实现. 闲话不说,先上效果: 因为代码太多,所以我打算写成一个系列,下面是第一篇的正文: 首先, ...

  2. UWP中的消息提示框(二)

    在UWP中的消息提示框(一)中介绍了一些常见的需要用户主动去干涉的一些消息提示框,接下来打算聊聊不需要用户主动去干涉的一些消息提示框.效果就是像双击退出的那种提示框. 先说说比较简单的吧,通过系统To ...

  3. UWP 大爆炸你个锤子

    今天看到  叫我蓝火火 s的 UWP中实现大爆炸效果(一) ,我也来说一下我的app [小薇自然语言处理]实现的大爆炸技术. 看一下效果先. 我的控件是基于wrappanel的,正如蓝火火说的,这样看 ...

  4. UWP中的消息提示框(一)

    不管什么平台,应用内难免会出现一些消息提示框,下面就来聊聊我在UWP里用到的消息提示框. 弹窗也可按是否需要用户操作促发一些逻辑进行分为两大类. 不需要用户干涉的一类: MessageDialog:操 ...

  5. 面试大总结之二:Java搞定面试中的二叉树题目

    package BinaryTreeSummary; import java.util.ArrayList; import java.util.Iterator; import java.util.L ...

  6. Android 4.3实现类似iOS在音乐播放过程中如果有来电则音乐声音渐小铃声渐大的效果

    目前Android的实现是:有来电时,音乐声音直接停止,铃声直接直接使用设置的铃声音量进行铃声播放. Android 4.3实现类似iOS在音乐播放过程中如果有来电则音乐声音渐小铃声渐大的效果. 如果 ...

  7. [一位菜鸟的COCOS-2D编程之路]打飞机中机种敌机和战机损毁时的爆炸效果

    1.第一步,添加爆炸动画 //添加玩家飞机飞行动画 id _playerFlyAction; id _playerBlowupAnimation; //战机爆炸动画 id _enemyBlowupAn ...

  8. 将 UWP 中 CommandBar 的展开方向改为向下展开

    在 UWP 中使用 CommandBar 来迅速添加一组功能按钮是非常迅速的,是 UWP 中推荐的交互方案之一.也许你能见到 CommandBar 按你所需向下展开,不过可能更多数情况会看到 Comm ...

  9. Unity3D_(Shuriken粒子系统)制作简单的烟花爆炸效果

    Unity中的粒子系统可以用于制作特效,如开枪火花效果,简单爆炸效果等.(毕竟程序员不是设计师,简单的特效都没有问题,要制作一些非常美观的特效还是需要多了解跟美术有关的知识.) 粒子系统实现一个简单的 ...

随机推荐

  1. 基于JS的WEB会议室预订拖拽式图形界面的实现

    06年的一篇blog,转到这个博客上: 很早之前写的,后来由于这个功能模块取消,最终没有上线,所以与Server交互的那部分还没有写,不过那部分方案我也已经出来了,而且现在客户端这一部分已经通过了比较 ...

  2. DX11 Without DirectX SDK--02 渲染一个三角形

    回到 DirectX11--使用Windows SDK来进行开发 目前暂时没有写HLSL具体教程的打算,而是着重于如何做到不用DirectX SDK来进行渲染.除此之外,这里也没有使用Effects框 ...

  3. js 遍历 each() 方法

    1.例子: <html><head><script type="text/javascript" src="/jquery/jquery.j ...

  4. 队列Queue和栈

    1.队列Queue是常用的数据结构,可以将队列看成特殊的线性表,队列限制了对线性表的访问方式,只能从线性表的一段添加(offer)元素, 从另一段取出(poll)元素,队列遵循先进先出的原则. 2.J ...

  5. C++相关:动态内存和智能指针

    前言 在C++中,动态内存的管理是通过运算符new和delete来完成的.但使用动态内存很容易出现问题,因为确保在正确的时间释放内存是及其困难的.有时候我们会忘记内存的的释放,这种情况下就会产生内存泄 ...

  6. PAT1088:Rational Arithmetic

    1088. Rational Arithmetic (20) 时间限制 200 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue F ...

  7. select case when与IF的用法

    case when概述 sql语句中的case语句与高级语言中的switch语句,是标准sql的语法,适用于一个条件判断有多种值的情况下分别执行不同的操作. case when示例 有一张表,里面有3 ...

  8. IDEA无法创建类,接口

    原因:模板丢失 解决方案: 在idea.exe.vmoptions 或 idea64.exe.vmoptions中加入配置-Djdk.util.zip.ensureTrailingSlash=fals ...

  9. 你应该知道的 5 个 Docker 工具

    你可以在网上找到大量炫酷的Docker 工具,并且大部分是开源的,可以通过Github访问.在过去的两年里,我开始在开发项目中大量使用Docker.当你开始使用Docker,你会发现它比你想象的还要适 ...

  10. JSONUtils.toJSONString的一个坑

    JSONUtils.toJSONString(null); //返回一个为"null"的字符串 这样会导致一个结果就是StringUtils.isBlank判断后,会为false ...