上一回实现了一个宽度不均匀的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. JSPWiki安装配置及FCKEditor的集成

    版本:JSPWiki-2.8.2 FCKeditor_2.6.3     安装方法我参照:http://doc.jspwiki.org/2.4/wiki/InstallingJSPWiki FCKEd ...

  2. Django之代码风格

    1 代码风格 稍微关注一下下面这些代码标准风格指导规则将会对你大有益处,我们高度建议你通读词章,即便你此时可能正想跳过它. 1.1 让你的代码保持可读性的重要性 代码在读方面的重要性胜过写.一个代码块 ...

  3. Yii2基本概念之——生命周期(LifeCycle)

    人有生老病死,一年有春夏秋冬四季演替,封建王朝有兴盛.停滞.衰亡的周期律--"其兴也勃焉,其亡也忽焉".换句话说,人,季节,王朝等等这些世间万物都有自己的生命周期.同样地,在软件行 ...

  4. Postgresql中临时表(temporary table)的特性和用法

    熟悉Oracle的人,相比对临时表(temporary table)并不陌生,很多场景对解决问题起到不错的作用,开源库Postgresql中,也有临时表的概念,虽然和Oracle中临时表名字相同,使用 ...

  5. springmvc 请求经过controller后静态资源无法访问的问题

    经过RequestMapping(“xx”)后 转发请求时会在url里面附带地址, 导致访问静态资源文件失败, 解决办法是在 spring-mvc.xml文件中加上 <mvc:default-s ...

  6. 最近面了不少java开发,据此来说下我的感受:哪怕事先只准备1小时,成功概率也能大大提升

    本人最近几年一直在做java后端方面的技术面试官,而在最近两周,又密集了面试了一些java初级和高级开发的候选人,在面试过程中,我自认为比较慎重,遇到问题回答不好的候选人,我总会再三从不同方面提问,只 ...

  7. 对C#热更新方案ILRuntime的探究

    转载请标明出处:http://www.cnblogs.com/zblade/ 对于游戏中的热更,目前主流的解决方案,分为Lua(ulua/slua/xlua/tolua)系和ILRuntime代表的c ...

  8. [CVPR2017] Visual Translation Embedding Network for Visual Relation Detection 论文笔记

    http://www.ee.columbia.edu/ln/dvmm/publications/17/zhang2017visual.pdf Visual Translation Embedding ...

  9. BigDecimal.setScale 处理java小数点

    BigDecimal.setScale()方法用于格式化小数点setScale(1)表示保留一位小数,默认用四舍五入方式 setScale(1,BigDecimal.ROUND_DOWN)直接删除多余 ...

  10. golang项目中使用条件编译

    golang项目中使用条件编译 C语言中的条件编译 golang中没有类似C语言中条件编译的写法,比如在C代码中可以使用如下语法做一些条件编译,结合宏定义来使用可以实现诸如按需编译release和de ...