WPF 实现 3D 导航
-
框架支持
.NET4 至 .NET8; -
Visual Studio 2022; -
AnimationNavigationBar3D带有3D效果的导航栏控件的样式。包含多个AnimationNavigationBar3DItem,通过 UniformGrid 进行排列,超出显示区域的项可以滚动查看。 -
AnimationNavigationBar3DItem继承ListBboxItem是3D导航栏中的每个项。使用Viewport3D来创建一个具有3D效果的容器,当鼠标移入或移出时,旋转动画来改变项的外观。它包含一个正面Background和一个背面ContentBack,可以显示不同的内容。使用可以根据需要自定义内容,如果在每个项中只设置了正面内容而没有设置背面内容,那么背面会自动克隆GetXmlReader并显示与正面相同的内容。 -
GetXmlReader用于通过将UIElement对象转换为XML字符串,再将其转换回UIElement对象,实现对UIElement对象的克隆。
1)新增 AnimationNavigationBar3D.cs 代码如下:
public classAnimationNavigationBar3D : ListBox
{
static AnimationNavigationBar3D()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(AnimationNavigationBar3D),
new FrameworkPropertyMetadata(typeof(AnimationNavigationBar3D)));
}
protected override bool IsItemItsOwnContainerOverride(object item)
{
return item is AnimationNavigationBar3DItem;
}
protected override DependencyObject GetContainerForItemOverride()
{
returnnew AnimationNavigationBar3DItem();
}
}
2)新增 AnimationNavigationBar3DItem.cs 代码如下:
public classAnimationNavigationBar3DItem : ListBoxItem
{
publicstaticreadonly DependencyProperty FillProperty =
DependencyProperty.Register("Fill", typeof(Brush), typeof(AnimationNavigationBar3DItem),
new PropertyMetadata(null));
publicstaticreadonly DependencyProperty ContentBackProperty =
DependencyProperty.Register("ContentBack", typeof(object), typeof(AnimationNavigationBar3DItem),
new PropertyMetadata(null));
static AnimationNavigationBar3DItem()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(AnimationNavigationBar3DItem),
new FrameworkPropertyMetadata(typeof(AnimationNavigationBar3DItem)));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
if (ContentBack == null)
ContentBack = ControlsHelper.GetXmlReader(Content);
}
/// <summary>
/// Color fore
/// </summary>
public Brush Fill
{
get => (Brush)GetValue(FillProperty);
set => SetValue(FillProperty, value);
}
/// <summary>
/// The content after the mouse is moved in
/// </summary>
publicobject ContentBack
{
get => (object)GetValue(ContentBackProperty);
set => SetValue(ContentBackProperty, value);
}
}
3)新增 AnimationNavigationBar3D.xaml 代码如下:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:WPFDevelopers.Controls">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Basic/ControlBasic.xaml" />
<ResourceDictionary Source="Basic/Animations.xaml" />
</ResourceDictionary.MergedDictionaries>
<Style
x:Key="WD.AnimationNavigationBar3DItem"
BasedOn="{StaticResource WD.ControlBasicStyle}"
TargetType="{x:Type controls:AnimationNavigationBar3DItem}">
<Setter Property="Width" Value="80" />
<Setter Property="Height" Value="80" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Foreground" Value="{DynamicResource WD.WindowForegroundColorBrush}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:AnimationNavigationBar3DItem}">
<Viewport3D Width="{TemplateBinding Height}" Height="{TemplateBinding Width}">
<Viewport3D.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard Storyboard.TargetName="axis3d" Storyboard.TargetProperty="Angle">
<DoubleAnimation
EasingFunction="{StaticResource WD.CubicEaseInOut}"
To="90"
Duration="00:00:1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard Storyboard.TargetName="axis3d" Storyboard.TargetProperty="Angle">
<DoubleAnimation
EasingFunction="{StaticResource WD.CubicEaseInOut}"
To="0"
Duration="00:00:1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Viewport3D.Triggers>
<Viewport3D.Camera>
<OrthographicCamera
LookDirection="0,0,-100"
Position="0,0,100"
UpDirection="0,1,0" />
</Viewport3D.Camera>
<Viewport3D.Children>
<ModelVisual3D>
<ModelVisual3D.Content>
<AmbientLight Color="{DynamicResource WD.BackgroundColor}" />
</ModelVisual3D.Content>
</ModelVisual3D>
<ContainerUIElement3D>
<ContainerUIElement3D.Transform>
<RotateTransform3D>
<RotateTransform3D.Rotation>
<AxisAngleRotation3D
x:Name="axis3d"
Angle="0"
Axis="1 0 0" />
</RotateTransform3D.Rotation>
</RotateTransform3D>
</ContainerUIElement3D.Transform>
<Viewport2DVisual3D>
<Viewport2DVisual3D.Material>
<DiffuseMaterial Viewport2DVisual3D.IsVisualHostMaterial="True" />
</Viewport2DVisual3D.Material>
<Viewport2DVisual3D.Geometry>
<MeshGeometry3D
Positions="-1,1,1 -1,-1,1 1,-1,1 1,1,1"
TextureCoordinates="0,0 0,1 1,1 1,0"
TriangleIndices="0 1 2 0 2 3" />
</Viewport2DVisual3D.Geometry>
<Border
Width="110"
Height="110"
Background="{TemplateBinding Background}"
CornerRadius="0,0,0,0">
<ContentPresenter
x:Name="PART_ContentPresenter"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
TextElement.Foreground="{TemplateBinding Foreground}" />
</Border>
</Viewport2DVisual3D>
<Viewport2DVisual3D>
<Viewport2DVisual3D.Material>
<DiffuseMaterial Viewport2DVisual3D.IsVisualHostMaterial="True" />
</Viewport2DVisual3D.Material>
<Viewport2DVisual3D.Geometry>
<MeshGeometry3D
Positions="-1,1,1 1,1,1 1,1,-1 -1,1,-1"
TextureCoordinates="0,0 0,1 1,1 1,0"
TriangleIndices="0 1 2 0 2 3" />
</Viewport2DVisual3D.Geometry>
<Border
Width="110"
Height="110"
Background="{TemplateBinding Fill}"
CornerRadius="0,0,0,0"
RenderTransformOrigin="0.5,0.5">
<Border.RenderTransform>
<TransformGroup>
<RotateTransform Angle="90" />
</TransformGroup>
</Border.RenderTransform>
<ContentPresenter
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{Binding ContentBack, RelativeSource={RelativeSource AncestorType={x:Type controls:AnimationNavigationBar3DItem}}}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
TextElement.Foreground="{TemplateBinding Foreground}" />
</Border>
</Viewport2DVisual3D>
</ContainerUIElement3D>
</Viewport3D.Children>
</Viewport3D>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style
x:Key="WD.AnimationNavigationBar3D"
BasedOn="{StaticResource WD.ControlBasicStyle}"
TargetType="{x:Type controls:AnimationNavigationBar3D}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:AnimationNavigationBar3D}">
<Border
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
UseLayoutRounding="{TemplateBinding UseLayoutRounding}">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsPresenter />
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<UniformGrid Columns="{Binding Items.Count, RelativeSource={RelativeSource AncestorType={x:Type controls:AnimationNavigationBar3D}}}" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
<Style BasedOn="{StaticResource WD.AnimationNavigationBar3DItem}" TargetType="{x:Type controls:AnimationNavigationBar3DItem}" />
<Style BasedOn="{StaticResource WD.AnimationNavigationBar3D}" TargetType="{x:Type controls:AnimationNavigationBar3D}" />
</ResourceDictionary>
4)新增 GetXmlReader.cs 代码如下:
public static object GetXmlReader(object Content)
{
var originalContent = Content as UIElement;
string contentXaml = XamlWriter.Save(originalContent);
using (StringReader stringReader = new StringReader(contentXaml))
{
using (XmlReader xmlReader = XmlReader.Create(stringReader))
{
object clonedContent = XamlReader.Load(xmlReader);
if (clonedContent is UIElement clonedElement)
{
return clonedElement;
}
}
}
returnnull;
}
5)新增 AnimationNavigationBar3D.xaml 示例代码如下:
<wd:AnimationNavigationBar3D VerticalAlignment="Bottom">
<wd:AnimationNavigationBar3DItem Background="#E21854" Fill="#FD3574">
<StackPanel VerticalAlignment="Center">
<Path
Width="40"
Height="40"
Data="{StaticResource WD.SmileyOutlineGeometry}"
Fill="{DynamicResource WD.WindowForegroundColorBrush}"
Stretch="Uniform" />
<TextBlock HorizontalAlignment="Center" Text="Emoji" />
</StackPanel>
</wd:AnimationNavigationBar3DItem>
<wd:AnimationNavigationBar3DItem Background="#41A545" Fill="#5EECA6">
<StackPanel VerticalAlignment="Center">
<Path
Width="40"
Height="40"
Data="{StaticResource WD.BusGeometry}"
Fill="{DynamicResource WD.WindowForegroundColorBrush}"
Stretch="Uniform" />
<TextBlock HorizontalAlignment="Center" Text="Bus" />
</StackPanel>
</wd:AnimationNavigationBar3DItem>
<wd:AnimationNavigationBar3DItem Background="#0A58F0" Fill="#3A7DFE">
<StackPanel VerticalAlignment="Center">
<Path
Width="40"
Height="40"
Data="{StaticResource WD.FriendGeometry}"
Fill="{DynamicResource WD.WindowForegroundColorBrush}"
Stretch="Uniform" />
<TextBlock HorizontalAlignment="Center" Text="Friend" />
</StackPanel>
</wd:AnimationNavigationBar3DItem>
<wd:AnimationNavigationBar3DItem Background="#5F0574" Fill="#8E1FA4">
<StackPanel VerticalAlignment="Center">
<Path
Width="40"
Height="40"
Data="{StaticResource WD.AlarmClockGeometry}"
Fill="{DynamicResource WD.WindowForegroundColorBrush}"
Stretch="Uniform" />
<TextBlock HorizontalAlignment="Center" Text="Clock" />
</StackPanel>
</wd:AnimationNavigationBar3DItem>
<wd:AnimationNavigationBar3DItem Background="#1F0355" Fill="#5B31AD">
<wd:AnimationNavigationBar3DItem.Content>
<StackPanel VerticalAlignment="Center">
<Path
Width="40"
Height="40"
Data="{StaticResource WD.BuildingRegularGeometry}"
Fill="{DynamicResource WD.WindowForegroundColorBrush}"
Stretch="Uniform" />
<TextBlock HorizontalAlignment="Center" Text="Regular" />
</StackPanel>
</wd:AnimationNavigationBar3DItem.Content>
<wd:AnimationNavigationBar3DItem.ContentBack>
<StackPanel VerticalAlignment="Center">
<Path
Width="40"
Height="40"
Data="{StaticResource WD.BuildingRegularGeometry}"
Fill="{DynamicResource WD.WindowForegroundColorBrush}"
Stretch="Uniform" />
<TextBlock HorizontalAlignment="Center" Text="建筑" />
</StackPanel>
</wd:AnimationNavigationBar3DItem.ContentBack>
</wd:AnimationNavigationBar3DItem>
</wd:AnimationNavigationBar3D>
作者:小码编匠

基于 .NET + Vue 3 的线路图绘制系统实战(含源码)
WPF 版简易 SIP 服务器:高效向 GB28181 摄像头发送直播请求
WinForm 下基于策略与工厂模式的 PLC 数据采集与监控系统
.NET 8 + Avalonia 跨平台简易校园信息管理系统的开发实战
C# + WPF + SuperSocket 开发面向工业自动化的 MES 系统
告别服务宕机,C# 看门狗守护你的 WinForm 与 Windows 服务
.NET 一款高效跨平台的自动更新工具(差异更新+热修复+自动升级)
WinForm 工业流量计串口调试助手:支持Modbus双协议的智能调试工具
面向工厂自动化的智能语音播报方案(基于.NET Windows服务)
工业自动化UI太难做?WPF 这套工业级控件方案真香(附源码)
工业自动化 WPF + Halcon 的模块化机器视觉解决方案
开源福利!八款 WPF + HandyControl 工业管理系统源码全公开
WinForm + Win32 API 自定义无边框窗口实战(工业软件必备)
基于 HslCommunication 的多端同步PLC远程监控系统
WinForm 数据采集实战:从串口通信到MES对接的轻量化解决方案
一个拒绝过度设计的 .NET 快速开发框架:开箱即用,专注"干活"
WinForm + SunnyUI 与 MQTTnet 实现智能可视化的火警联动大屏系统
工业自动化实战:基于 .NET + ModBus RTU协议的称重机开发
WinForm 基于 SunnyUI+ PCLSharp 的机器视觉焊接系统
.NET 9 + WPF + Halcon 构建工业视觉流程框架:从架构设计到落地实践
WinForm 高分屏适配难题?一款强大的控件自适应缩放工具
.NET 桌面应用 (WPF/WinForm) 高效自动更新解决方案
一行代码快速开发 AntdUI 风格的 WinForm 通用后台框架
WinForm + SQL Server + Modbus 实现仓库温控上位机系统开发
WinForm 开发的多功能工具:串口通信、加密解密、图像转换等功能
觉得有收获?不妨分享让更多人受益
关注「DotNet技术匠」,共同提升技术实力

