更新项目配置,新增设备同步模块,优化WebSocket和Swagger配置,增强SCADA系统的免登录接口,支持数据字典项和登录日志的免登录查询与记录。调整Java编译设置,确保更好的开发体验。
This commit is contained in:
50
yy-admin-master/YY.Admin/Views/Control/MenuTreeView.xaml
Normal file
50
yy-admin-master/YY.Admin/Views/Control/MenuTreeView.xaml
Normal file
@@ -0,0 +1,50 @@
|
||||
<UserControl x:Class="YY.Admin.Views.Control.MenuTreeView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:YY.Admin.Views"
|
||||
xmlns:beh="clr-namespace:YY.Admin.Core.Behavior;assembly=YY.Admin.Core"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<TreeView
|
||||
x:Name="MenuTree"
|
||||
ItemsSource="{Binding MenuItems}"
|
||||
Background="Transparent"
|
||||
BorderThickness="0">
|
||||
<TreeView.ItemContainerStyle>
|
||||
<Style TargetType="TreeViewItem" BasedOn="{StaticResource CusTreeViewItemBaseStyle}">
|
||||
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
|
||||
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
|
||||
<Setter Property="FontSize" Value="{StaticResource FontSize}"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
|
||||
<!-- 禁止双击自动展开 -->
|
||||
<EventSetter Event="MouseDoubleClick" Handler="TreeViewItem_MouseDoubleClick"/>
|
||||
<!--<Setter Property="Margin" Value="0"/>-->
|
||||
<!-- 绑定命令到 TreeView 的 DataContext 下的 NavigateCommand -->
|
||||
<Setter
|
||||
Property="beh:TreeViewItemClickBehavior.Command"
|
||||
Value="{Binding DataContext.NavigateCommand,
|
||||
RelativeSource={RelativeSource AncestorType=TreeView}}"/>
|
||||
<!-- 把命令参数设为当前数据上下文(菜单项本身) -->
|
||||
<Setter Property="beh:TreeViewItemClickBehavior.CommandParameter" Value="{Binding}"/>
|
||||
</Style>
|
||||
</TreeView.ItemContainerStyle>
|
||||
<TreeView.ItemTemplate>
|
||||
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
|
||||
<StackPanel Orientation="Horizontal" Height="50">
|
||||
<!--菜单图标-->
|
||||
<TextBlock
|
||||
Text="{Binding Icon}"
|
||||
FontFamily="{StaticResource AntDesignIcon}"
|
||||
FontSize="16"
|
||||
VerticalAlignment="Center"
|
||||
Margin="20,0,12,0"/>
|
||||
<!--菜单名称-->
|
||||
<TextBlock Text="{Binding Name}" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</HierarchicalDataTemplate>
|
||||
</TreeView.ItemTemplate>
|
||||
</TreeView>
|
||||
</UserControl>
|
||||
22
yy-admin-master/YY.Admin/Views/Control/MenuTreeView.xaml.cs
Normal file
22
yy-admin-master/YY.Admin/Views/Control/MenuTreeView.xaml.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace YY.Admin.Views.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for MenuTreeView.xaml
|
||||
/// </summary>
|
||||
public partial class MenuTreeView : UserControl
|
||||
{
|
||||
public MenuTreeView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void TreeViewItem_MouseDoubleClick(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
// 阻止双击默认展开行为
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<UserControl x:Class="YY.Admin.Views.Control.PaginationDataGridControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<!-- 底部分页区 -->
|
||||
<DockPanel LastChildFill="False" Margin="0 10 0 0">
|
||||
|
||||
<!-- 分页条(靠右) -->
|
||||
<hc:Pagination
|
||||
MaxPageCount="{Binding MaxPageCount}"
|
||||
PageIndex="{Binding PageIndex, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
DataCountPerPage="{Binding DataCountPerPage}"
|
||||
IsJumpEnabled="True"
|
||||
DockPanel.Dock="Right"
|
||||
Margin="0,0,4,0">
|
||||
<hc:Interaction.Triggers>
|
||||
<hc:EventTrigger EventName="PageUpdated">
|
||||
<hc:EventToCommand Command="{Binding PageUpdatedCmd}" PassEventArgsToCommand="True" />
|
||||
</hc:EventTrigger>
|
||||
</hc:Interaction.Triggers>
|
||||
</hc:Pagination>
|
||||
|
||||
<hc:ComboBox
|
||||
Width="100"
|
||||
ItemsSource="{Binding PageSizes}"
|
||||
SelectedIndex="0"
|
||||
SelectedValue="{Binding DataCountPerPage, Mode=TwoWay}"
|
||||
DisplayMemberPath="Value"
|
||||
SelectedValuePath="Key"
|
||||
Margin="5,0 10 0"
|
||||
DockPanel.Dock="Right">
|
||||
<hc:Interaction.Triggers>
|
||||
<hc:EventTrigger EventName="SelectionChanged">
|
||||
<hc:EventToCommand Command="{Binding PageSizeUpdatedCmd}"
|
||||
PassEventArgsToCommand="True"/>
|
||||
</hc:EventTrigger>
|
||||
</hc:Interaction.Triggers>
|
||||
</hc:ComboBox>
|
||||
|
||||
<!-- 分页信息(靠左) -->
|
||||
<TextBlock
|
||||
Text="{Binding TotalCount, StringFormat='共 {0} 条'}"
|
||||
VerticalAlignment="Center"
|
||||
Margin="10,0 5 0"
|
||||
DockPanel.Dock="Right"/>
|
||||
</DockPanel>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,108 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using System.Windows.Threading;
|
||||
using YY.Admin.Core;
|
||||
using YY.Admin.Services.Service;
|
||||
|
||||
namespace YY.Admin.Views.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// PaginationDataGridControl.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class PaginationDataGridControl : UserControl
|
||||
{
|
||||
public PaginationDataGridControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
//this.Loaded += PaginationDataGridControl_Loaded;
|
||||
}
|
||||
|
||||
// 确保用户在控制加载时触发列生成
|
||||
//private void PaginationDataGridControl_Loaded(object sender, RoutedEventArgs e)
|
||||
//{
|
||||
// // 确保 DataGrid 已完全加载
|
||||
// Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() =>
|
||||
// {
|
||||
// // 确保 DataGrid 控件已初始化并且 ItemsSource 不为 null
|
||||
// var data = DataGrid.ItemsSource;
|
||||
// if (data == null)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// // 获取数据模型的类型
|
||||
// var type = data.GetType().GetGenericArguments().FirstOrDefault();
|
||||
// if (type == null) return;
|
||||
|
||||
// // 获取所有带有 BindDescriptionAttribute 的属性
|
||||
// var properties = type.GetProperties()
|
||||
// .Where(prop => Attribute.IsDefined(prop, typeof(BindDescriptionAttribute)))
|
||||
// .OrderBy(prop => ((BindDescriptionAttribute)prop.GetCustomAttribute(typeof(BindDescriptionAttribute))).DisplayIndex)
|
||||
// .ToList();
|
||||
|
||||
// foreach (var prop in properties)
|
||||
// {
|
||||
// var attribute = prop.GetCustomAttribute<BindDescriptionAttribute>();
|
||||
// if (attribute != null)
|
||||
// {
|
||||
// // 检查列是否已经存在
|
||||
// if (!ColumnExists(attribute.HeaderName))
|
||||
// {
|
||||
// var column = CreateDataGridColumn(attribute, prop);
|
||||
// if (column != null)
|
||||
// {
|
||||
// // 通过绑定添加列
|
||||
// DataGrid.Columns.Add(column);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }));
|
||||
//}
|
||||
|
||||
//// 检查列是否已经存在
|
||||
//private bool ColumnExists(string headerName)
|
||||
//{
|
||||
// return DataGrid.Columns.Any(c => c.Header.ToString() == headerName);
|
||||
//}
|
||||
|
||||
//private DataGridColumn CreateDataGridColumn(BindDescriptionAttribute attribute, PropertyInfo property)
|
||||
//{
|
||||
// DataGridColumn column = null;
|
||||
|
||||
// // 根据属性的显示方式来创建不同的列
|
||||
// switch (attribute.ShowAs)
|
||||
// {
|
||||
// case ShowScheme.普通文本:
|
||||
// column = new DataGridTextColumn
|
||||
// {
|
||||
// Header = attribute.HeaderName,
|
||||
// Binding = new Binding(property.Name),
|
||||
// Width = attribute.Width,
|
||||
// DisplayIndex = attribute.DisplayIndex
|
||||
// };
|
||||
// break;
|
||||
|
||||
// case ShowScheme.自定义:
|
||||
// // 如果需要自定义列,您可以在这里扩展并处理自定义类型的列
|
||||
// break;
|
||||
// }
|
||||
|
||||
// return column;
|
||||
//}
|
||||
}
|
||||
}
|
||||
193
yy-admin-master/YY.Admin/Views/Control/SidebarControl.xaml
Normal file
193
yy-admin-master/YY.Admin/Views/Control/SidebarControl.xaml
Normal file
@@ -0,0 +1,193 @@
|
||||
<UserControl x:Class="YY.Admin.Views.Control.SidebarControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
|
||||
xmlns:prism="http://prismlibrary.com/"
|
||||
xmlns:md="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||
xmlns:core="clr-namespace:YY.Admin.Core;assembly=YY.Admin.Core"
|
||||
xmlns:ctls="clr-namespace:YY.Admin.Core.Controls;assembly=YY.Admin.Core"
|
||||
xmlns:local="clr-namespace:YY.Admin.Views.Control">
|
||||
|
||||
<UserControl.Resources>
|
||||
|
||||
<!-- AntDesign 模板 -->
|
||||
<DataTemplate x:Key="AntDesignIconTemplate">
|
||||
<TextBlock
|
||||
FontFamily="{StaticResource AntDesignIcon}"
|
||||
FontSize="22"
|
||||
Text="{Binding Icon}"
|
||||
Margin="0,0,0,5"
|
||||
HorizontalAlignment="Center"/>
|
||||
</DataTemplate>
|
||||
|
||||
<!-- MaterialDesign 模板 -->
|
||||
<DataTemplate x:Key="MaterialDesignIconTemplate">
|
||||
<md:PackIcon
|
||||
Kind="{Binding Icon}"
|
||||
Width="22"
|
||||
Height="22"
|
||||
Margin="0,0,0,5"
|
||||
HorizontalAlignment="Center"/>
|
||||
</DataTemplate>
|
||||
|
||||
<!-- FontAwesome 模板 -->
|
||||
<DataTemplate x:Key="FontawesomeIconTemplate">
|
||||
<ctls:FontAwesomeIcon
|
||||
Icon="{Binding Icon}"
|
||||
FontSize="22"
|
||||
Margin="0,0,0,5"
|
||||
HorizontalAlignment="Center"/>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="NavItemTemplate">
|
||||
<StackPanel Orientation="Vertical" Margin="5" HorizontalAlignment="Center">
|
||||
<ContentControl>
|
||||
<ContentControl.Style>
|
||||
<Style TargetType="ContentControl">
|
||||
<!-- 默认模板(AntDesign) -->
|
||||
<Setter Property="ContentTemplate" Value="{StaticResource AntDesignIconTemplate}"/>
|
||||
<!-- 绑定数据上下文 -->
|
||||
<Setter Property="Content" Value="{Binding}"/>
|
||||
|
||||
<!-- 触发器根据 IconType 切换模板 -->
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IconType}" Value="{x:Static core:IconTypeEnum.MaterialDesign}">
|
||||
<Setter Property="ContentTemplate" Value="{StaticResource MaterialDesignIconTemplate}"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding IconType}" Value="{x:Static core:IconTypeEnum.FontAwesome}">
|
||||
<Setter Property="ContentTemplate" Value="{StaticResource FontawesomeIconTemplate}"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ContentControl.Style>
|
||||
</ContentControl>
|
||||
<TextBlock Text="{Binding Name}" FontSize="12" TextTrimming="CharacterEllipsis" TextWrapping="NoWrap" TextAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
|
||||
<Style x:Key="NavItemStyle" TargetType="ListBoxItem" BasedOn="{StaticResource ListBoxItemBaseStyle}">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ListBoxItem">
|
||||
<Grid x:Name="templateRoot" Margin="0,0,0,5">
|
||||
<!-- 左侧指示器 -->
|
||||
<Border
|
||||
x:Name="indicator"
|
||||
Width="4"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Margin="6,0,0,0"
|
||||
CornerRadius="2"
|
||||
Background="{DynamicResource PrimaryBrush}"
|
||||
Opacity="0"
|
||||
Panel.ZIndex="1"
|
||||
Height="{Binding ActualHeight, ElementName=templateRoot, Converter={StaticResource PercentageConverter}, ConverterParameter=0.4}">
|
||||
<Border.RenderTransform>
|
||||
<TranslateTransform Y="10"/>
|
||||
</Border.RenderTransform>
|
||||
</Border>
|
||||
|
||||
<!-- 内容边框 -->
|
||||
<Border x:Name="border" CornerRadius="6" Background="Transparent" Margin="5,0">
|
||||
<ContentPresenter x:Name="content" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
<ControlTemplate.Triggers>
|
||||
<!-- 选中动画 -->
|
||||
<Trigger Property="IsSelected" Value="True">
|
||||
<Setter TargetName="border" Property="Background" Value="{DynamicResource SecondaryRegionBrush}"/>
|
||||
<Setter TargetName="content" Property="TextBlock.Foreground" Value="{DynamicResource PrimaryBrush}"/>
|
||||
<Trigger.EnterActions>
|
||||
<BeginStoryboard>
|
||||
<Storyboard>
|
||||
<DoubleAnimation
|
||||
Storyboard.TargetName="indicator"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
To="0.8"
|
||||
Duration="0:0:0.25"/>
|
||||
<DoubleAnimation
|
||||
Storyboard.TargetName="indicator"
|
||||
Storyboard.TargetProperty="RenderTransform.(TranslateTransform.Y)"
|
||||
To="0"
|
||||
Duration="0:0:0.25"/>
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</Trigger.EnterActions>
|
||||
<Trigger.ExitActions>
|
||||
<BeginStoryboard>
|
||||
<Storyboard>
|
||||
<DoubleAnimation
|
||||
Storyboard.TargetName="indicator"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
To="0"
|
||||
Duration="0:0:0.25"/>
|
||||
<DoubleAnimation
|
||||
Storyboard.TargetName="indicator"
|
||||
Storyboard.TargetProperty="RenderTransform.(TranslateTransform.Y)"
|
||||
To="10"
|
||||
Duration="0:0:0.25"/>
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</Trigger.ExitActions>
|
||||
</Trigger>
|
||||
|
||||
<!-- 鼠标悬停 -->
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="border" Property="Background" Value="{DynamicResource SecondaryRegionBrush}"/>
|
||||
<Setter TargetName="content" Property="TextBlock.Foreground" Value="{DynamicResource PrimaryBrush}"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<!-- 鼠标点击触发命令 -->
|
||||
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="OnNavItemMouseDown"/>
|
||||
</Style>
|
||||
|
||||
</UserControl.Resources>
|
||||
|
||||
<Border BorderBrush="{DynamicResource BorderBrush}" BorderThickness="0,0,1,0">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<!-- 上部占满剩余空间 -->
|
||||
<RowDefinition Height="*"/>
|
||||
<!-- 底部自适应 -->
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 上半部分:主导航 -->
|
||||
<ListBox
|
||||
Grid.Row="0"
|
||||
Padding="0,5,0,0"
|
||||
BorderThickness="0"
|
||||
hc:BorderElement.CornerRadius="0"
|
||||
ItemsSource="{Binding TopNavItems}"
|
||||
SelectedItem="{Binding SelectedTopNavItem, Mode=TwoWay}"
|
||||
ItemTemplate="{StaticResource NavItemTemplate}"
|
||||
ItemContainerStyle="{StaticResource NavItemStyle}">
|
||||
<!--<i:Interaction.Triggers>
|
||||
<i:EventTrigger EventName="SelectionChanged">
|
||||
<prism:InvokeCommandAction
|
||||
Command="{Binding NavItemClickCommand}"
|
||||
CommandParameter="{Binding SelectedItem, RelativeSource={RelativeSource AncestorType=ListBox}}"/>
|
||||
</i:EventTrigger>
|
||||
</i:Interaction.Triggers>-->
|
||||
</ListBox>
|
||||
|
||||
<!-- 底部部分:固定操作 -->
|
||||
<ListBox
|
||||
Grid.Row="1"
|
||||
Padding="0"
|
||||
BorderThickness="0"
|
||||
hc:BorderElement.CornerRadius="0"
|
||||
ItemsSource="{Binding BottomNavItems}"
|
||||
SelectedItem="{Binding SelectedBottomNavItem, Mode=TwoWay}"
|
||||
ItemTemplate="{StaticResource NavItemTemplate}"
|
||||
ItemContainerStyle="{StaticResource NavItemStyle}"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,37 @@
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using YY.Admin.Module;
|
||||
|
||||
namespace YY.Admin.Views.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for Sidebar.xaml
|
||||
/// </summary>
|
||||
public partial class SidebarControl : UserControl
|
||||
{
|
||||
public SidebarControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
private void OnNavItemMouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (sender is ListBoxItem item && item.DataContext is NavItem navItem)
|
||||
{
|
||||
// 执行命令(如果有)
|
||||
if (navItem.Command?.CanExecute(navItem) == true)
|
||||
{
|
||||
navItem.Command.Execute(navItem);
|
||||
}
|
||||
|
||||
// 如果不可选,阻止选中
|
||||
if (!navItem.IsActive)
|
||||
{
|
||||
// 阻止 ListBox 自动选择该项
|
||||
e.Handled = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<UserControl x:Class="YY.Admin.Views.Control.TabContentView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:prism="http://prismlibrary.com/">
|
||||
<Grid>
|
||||
<!-- 这个 ContentControl 将被注册为 Prism 区域 -->
|
||||
<ContentControl x:Name="PART_ContentHost" />
|
||||
</Grid>
|
||||
</UserControl>
|
||||
371
yy-admin-master/YY.Admin/Views/Control/TabContentView.xaml.cs
Normal file
371
yy-admin-master/YY.Admin/Views/Control/TabContentView.xaml.cs
Normal file
@@ -0,0 +1,371 @@
|
||||
//using Prism.Ioc;
|
||||
//using System;
|
||||
//using System.Windows;
|
||||
//using System.Windows.Controls;
|
||||
//using System.Windows.Threading;
|
||||
|
||||
//namespace YY.Admin.Views.Control
|
||||
//{
|
||||
// /// <summary>
|
||||
// /// TabContentView.xaml 的交互逻辑
|
||||
// /// </summary>
|
||||
// public partial class TabContentView : UserControl
|
||||
// {
|
||||
// public TabContentView()
|
||||
// {
|
||||
// InitializeComponent();
|
||||
// this.Loaded += TabContentView_Loaded;
|
||||
// this.Unloaded += TabContentView_Unloaded;
|
||||
// }
|
||||
|
||||
// private bool _navigated = false;
|
||||
|
||||
// private void TabContentView_Loaded(object sender, RoutedEventArgs e)
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// if (string.IsNullOrWhiteSpace(RegionName))
|
||||
// return;
|
||||
|
||||
// var regionManager = ContainerLocator.Current.Resolve<IRegionManager>();
|
||||
|
||||
// // 把内部 ContentControl 注册为 region(基于 RegionName)
|
||||
// RegionManager.SetRegionName(PART_ContentHost, RegionName);
|
||||
// RegionManager.SetRegionManager(PART_ContentHost, regionManager);
|
||||
|
||||
// // 延迟导航,确保 region 真正注册到 RegionManager 并且模板生成为止
|
||||
// if (!_navigated && !string.IsNullOrEmpty(ViewName))
|
||||
// {
|
||||
// Dispatcher.BeginInvoke(new Action(() =>
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// // 再次检查 region 是否存在并执行导航
|
||||
// if (regionManager.Regions.ContainsRegionWithName(RegionName))
|
||||
// {
|
||||
// regionManager.RequestNavigate(RegionName, ViewName);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// // 如果 region 仍然不存在,尝试 RequestNavigate(Prism 通常会创建 region)
|
||||
// regionManager.RequestNavigate(RegionName, ViewName);
|
||||
// }
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// System.Diagnostics.Debug.WriteLine($"TabContentView 导航异常: {ex.Message}");
|
||||
// }
|
||||
// }), DispatcherPriority.Background);
|
||||
|
||||
// _navigated = true;
|
||||
// }
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// System.Diagnostics.Debug.WriteLine($"TabContentView_Loaded 出错: {ex.Message}");
|
||||
// }
|
||||
// }
|
||||
|
||||
// private void TabContentView_Unloaded(object sender, RoutedEventArgs e)
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// if (!string.IsNullOrWhiteSpace(RegionName))
|
||||
// {
|
||||
// var regionManager = ContainerLocator.Current.Resolve<IRegionManager>();
|
||||
// if (regionManager.Regions.ContainsRegionWithName(RegionName))
|
||||
// {
|
||||
// var region = regionManager.Regions[RegionName];
|
||||
// region.RemoveAll();
|
||||
// regionManager.Regions.Remove(RegionName);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// System.Diagnostics.Debug.WriteLine($"TabContentView_Unloaded 清理异常: {ex.Message}");
|
||||
// }
|
||||
// }
|
||||
|
||||
// #region RegionName DP
|
||||
// public static readonly DependencyProperty RegionNameProperty =
|
||||
// DependencyProperty.Register(nameof(RegionName), typeof(string), typeof(TabContentView), new PropertyMetadata(null));
|
||||
|
||||
// public string RegionName
|
||||
// {
|
||||
// get => (string)GetValue(RegionNameProperty);
|
||||
// set => SetValue(RegionNameProperty, value);
|
||||
// }
|
||||
// #endregion
|
||||
|
||||
// #region ViewName DP
|
||||
// public static readonly DependencyProperty ViewNameProperty =
|
||||
// DependencyProperty.Register(nameof(ViewName), typeof(string), typeof(TabContentView), new PropertyMetadata(null));
|
||||
|
||||
// public string ViewName
|
||||
// {
|
||||
// get => (string)GetValue(ViewNameProperty);
|
||||
// set => SetValue(ViewNameProperty, value);
|
||||
// }
|
||||
// #endregion
|
||||
// }
|
||||
//}
|
||||
using Prism.Ioc;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace YY.Admin.Views.Control
|
||||
{
|
||||
public partial class TabContentView : UserControl
|
||||
{
|
||||
public TabContentView()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
this.Loaded += TabContentView_Loaded;
|
||||
this.Unloaded += TabContentView_Unloaded;
|
||||
this.IsVisibleChanged += TabContentView_IsVisibleChanged;
|
||||
this.DataContextChanged += TabContentView_DataContextChanged;
|
||||
}
|
||||
|
||||
private bool _navigated = false;
|
||||
private IRegionManager _regionManager;
|
||||
private DispatcherOperation _pendingNavigationOp;
|
||||
|
||||
#region DPs
|
||||
public static readonly DependencyProperty RegionNameProperty =
|
||||
DependencyProperty.Register(nameof(RegionName), typeof(string), typeof(TabContentView), new PropertyMetadata(null, OnRegionOrViewNameChanged));
|
||||
|
||||
public string RegionName
|
||||
{
|
||||
get => (string)GetValue(RegionNameProperty);
|
||||
set => SetValue(RegionNameProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ViewNameProperty =
|
||||
DependencyProperty.Register(nameof(ViewName), typeof(string), typeof(TabContentView), new PropertyMetadata(null, OnRegionOrViewNameChanged));
|
||||
|
||||
public string ViewName
|
||||
{
|
||||
get => (string)GetValue(ViewNameProperty);
|
||||
set => SetValue(ViewNameProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
private void TabContentView_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(RegionName))
|
||||
return;
|
||||
|
||||
_regionManager = ContainerLocator.Current.Resolve<IRegionManager>();
|
||||
|
||||
// 确保 region/manager 设置在后续导航前准备好(但不要重复注册 region 这里)
|
||||
// We will call PrepareRegionForRegistration inside EnsureNavigateIfNeeded before actual registration.
|
||||
EnsureNavigateIfNeeded();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"TabContentView_Loaded 出错: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void TabContentView_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
|
||||
=> EnsureNavigateIfNeeded();
|
||||
|
||||
private void TabContentView_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
|
||||
=> EnsureNavigateIfNeeded();
|
||||
|
||||
private void EnsureNavigateIfNeeded()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_navigated)
|
||||
return;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(RegionName) || string.IsNullOrWhiteSpace(ViewName))
|
||||
return;
|
||||
|
||||
if (!IsLoaded || !IsVisible)
|
||||
return;
|
||||
|
||||
if (_regionManager == null)
|
||||
_regionManager = ContainerLocator.Current.Resolve<IRegionManager>();
|
||||
|
||||
// 在注册 region 之前准备 host(清理旧 region 与清空 Content),避免 Prism 抛 Content 非空异常
|
||||
PrepareRegionForRegistration();
|
||||
|
||||
// 取消之前挂起的导航(保证最后一次胜出)
|
||||
if (_pendingNavigationOp != null && _pendingNavigationOp.Status == DispatcherOperationStatus.Pending)
|
||||
{
|
||||
Debug.WriteLine($"取消挂起导航: region={RegionName}, view={ViewName}");
|
||||
_pendingNavigationOp.Abort();
|
||||
_pendingNavigationOp = null;
|
||||
}
|
||||
|
||||
var desiredView = ViewName;
|
||||
var desiredRegion = RegionName;
|
||||
|
||||
_pendingNavigationOp = Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_regionManager == null)
|
||||
_regionManager = ContainerLocator.Current.Resolve<IRegionManager>();
|
||||
|
||||
// 现在把 PART_ContentHost 注册为一个 region(设置 RegionName/RegionManager)
|
||||
// 注意:如果前面清理正确,这里不会再触发 ContentControl already has content 错误
|
||||
RegionManager.SetRegionName(PART_ContentHost, desiredRegion);
|
||||
RegionManager.SetRegionManager(PART_ContentHost, _regionManager);
|
||||
|
||||
Debug.WriteLine($"开始 RequestNavigate -> region={desiredRegion}, view={desiredView}");
|
||||
|
||||
_regionManager.RequestNavigate(desiredRegion, desiredView, result =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (result.Success == true)
|
||||
{
|
||||
_navigated = true;
|
||||
Debug.WriteLine($"导航成功: region={desiredRegion}, view={desiredView}");
|
||||
}
|
||||
else
|
||||
{
|
||||
_navigated = false;
|
||||
Debug.WriteLine($"导航失败: region={desiredRegion}, view={desiredView}, error={result.Exception?.Message}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_navigated = false;
|
||||
Debug.WriteLine($"导航回调异常: {ex.Message}");
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"调度导航异常: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_pendingNavigationOp = null;
|
||||
}
|
||||
}), DispatcherPriority.Background);
|
||||
|
||||
Debug.WriteLine($"调度导航: Region={RegionName}, View={ViewName}, Loaded={IsLoaded}, Visible={IsVisible}, navigated={_navigated}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"EnsureNavigateIfNeeded 异常: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在注册 region 之前清理宿主控件与 RegionManager 中可能残留的 region。
|
||||
/// 这一步是防止 Prism 在 adapt 时因为 ContentControl.Content 非空而抛异常。
|
||||
/// </summary>
|
||||
private void PrepareRegionForRegistration()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 如果 RegionManager 中已存在同名 region,则先移除它(并 RemoveAll 内容)
|
||||
if (_regionManager != null && _regionManager.Regions.ContainsRegionWithName(RegionName))
|
||||
{
|
||||
try
|
||||
{
|
||||
var existing = _regionManager.Regions[RegionName];
|
||||
existing.RemoveAll();
|
||||
_regionManager.Regions.Remove(RegionName);
|
||||
Debug.WriteLine($"已移除旧 region: {RegionName}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"移除旧 region 发生异常: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// 如果宿主 ContentControl 已经有内容,清空它(很关键)
|
||||
if (PART_ContentHost != null && PART_ContentHost.Content != null)
|
||||
{
|
||||
Debug.WriteLine($"清空 PART_ContentHost.Content (原内容类型: {PART_ContentHost.Content.GetType().Name})");
|
||||
PART_ContentHost.Content = null;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"PrepareRegionForRegistration 异常: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void TabContentView_Unloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 取消挂起导航
|
||||
if (_pendingNavigationOp != null && _pendingNavigationOp.Status == DispatcherOperationStatus.Pending)
|
||||
{
|
||||
_pendingNavigationOp.Abort();
|
||||
_pendingNavigationOp = null;
|
||||
}
|
||||
|
||||
// 清理 region 与宿主内容
|
||||
if (!string.IsNullOrWhiteSpace(RegionName))
|
||||
{
|
||||
var rm = _regionManager ?? ContainerLocator.Current.Resolve<IRegionManager>();
|
||||
try
|
||||
{
|
||||
if (rm != null && rm.Regions.ContainsRegionWithName(RegionName))
|
||||
{
|
||||
var region = rm.Regions[RegionName];
|
||||
region.RemoveAll();
|
||||
rm.Regions.Remove(RegionName);
|
||||
Debug.WriteLine($"卸载并移除 region: {RegionName}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"TabContentView_Unloaded 清理 region 异常: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
if (PART_ContentHost != null && PART_ContentHost.Content != null)
|
||||
{
|
||||
PART_ContentHost.Content = null;
|
||||
Debug.WriteLine($"Unloaded: 清空 PART_ContentHost.Content");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"TabContentView_Unloaded 清理异常: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_navigated = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnRegionOrViewNameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is TabContentView tc)
|
||||
{
|
||||
// 新参数到来,允许再次导航
|
||||
tc._navigated = false;
|
||||
|
||||
// 取消旧挂起导航
|
||||
if (tc._pendingNavigationOp != null && tc._pendingNavigationOp.Status == DispatcherOperationStatus.Pending)
|
||||
{
|
||||
tc._pendingNavigationOp.Abort();
|
||||
tc._pendingNavigationOp = null;
|
||||
}
|
||||
|
||||
// 尝试导航(这一调用会在 Loaded/可见时真正执行)
|
||||
tc.EnsureNavigateIfNeeded();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user