上一回实现了一个宽度不均匀的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. Day5_递归_二分法

    递归调用: 在调用一个函数的过程中,直接或间接的调用函数本身. def func(): print('from func') 间接调用: def foo(): print('form foo') ba ...

  2. javaWeb安全漏洞修复总结

    1 Web安全介绍1 2 SQL注入.盲注1 2.1 SQL注入.盲注概述 1 2.2 安全风险及原因 2 2.3 AppScan扫描建议 2 2.4 应用程序解决方案 4 3 会话标识未更新7 3. ...

  3. 线上Django项目python2到3升级日记

    这两天干了一个几斤疯狂的事情,花不到一个工作日的时间把一个线上Django项目语言版本从python2升级到Python31.字典的一个语法变化 Python2.7: if dict1.haskey( ...

  4. 基于one2team框架的Highcharts图表图片导出方案

    这篇文章已经没有什么意义了,新版的HIghcharts提供Java图片导出解决方案,你需要做的就是下个Maven,bulid一个war就Ok了.---addedy on 2012-11-15 多说一句 ...

  5. com.android.dex.DexException: Multiple dex files define Lcom/sina/sso/RemoteSSO;

    错误原因:ShareSDK的包里面也包含微博SDK的代码,两个Jar包含重复. 解决方法:用Winrar到ShareSDK的Jar里面把sso目录删掉,编译即可成功

  6. 食物链-HZUN寒假集训

    食物链 总时间限制: 1000ms 内存限制: 65536kB 描述 动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形.A吃B, B吃C,C吃A. 现有N个动物,以1-N编号.每个动 ...

  7. 完美解决IE渲染方式进入兼容模式问题

    <meta http-equiv="X-UA-Compatible" content="IE=9; IE=8; IE=7; IE=EDGE"> &l ...

  8. js 对XML文件的保存与读取

    一.保存(在前端用js创建xml格式) function exportToXml(jsonStr){ var obj = eval('('+ jsonStr + ')'); var xmlDoc = ...

  9. 总结Array类型中常用的方法

    Array类型应该是 ECMAScript 中最常用的类型之一了,并且它定义的数组与其他语言有着相当大的区别.数组是数据的有序集合,我们可以通过下标对指定位置的数据进行读 写:特别的是,在 ECMAS ...

  10. Python_从字符串中提取号码

    import re telNumber = '''Suppose my Phone No. is 0535-1234567,yours is 010-12345678,his is 025-87654 ...