Browse Source

初始化

master
pry 1 year ago
parent
commit
76ea0622b1
100 changed files with 11691 additions and 0 deletions
  1. +17
    -0
      BPA.Model/BPA.Model.csproj
  2. +61
    -0
      BPA.Model/RawMaterInfo.cs
  3. +51
    -0
      BPA.Model/ViewItem.cs
  4. +37
    -0
      BPA.SingleDevice.sln
  5. +16
    -0
      BPA.SingleDevice/App.xaml
  6. +31
    -0
      BPA.SingleDevice/App.xaml.cs
  7. +10
    -0
      BPA.SingleDevice/AssemblyInfo.cs
  8. +14
    -0
      BPA.SingleDevice/BPA.SingleDevice.csproj
  9. +46
    -0
      BPA.SingleDevice/Controls/ControlDisplay.cs
  10. +29
    -0
      BPA.SingleDevice/Converter/GetIconConverter.cs
  11. +32
    -0
      BPA.SingleDevice/Converter/IsSelectedToIconConverter.cs
  12. +48
    -0
      BPA.SingleDevice/Themes/Generic.xaml
  13. +13
    -0
      BPA.SingleDevice/Themes/ThemesColor.xaml
  14. +20
    -0
      BPA.SingleDevice/View/DeviceManagementView.xaml
  15. +28
    -0
      BPA.SingleDevice/View/DeviceManagementView.xaml.cs
  16. +83
    -0
      BPA.SingleDevice/View/MainView.xaml
  17. +34
    -0
      BPA.SingleDevice/View/MainView.xaml.cs
  18. +118
    -0
      BPA.SingleDevice/View/RawMaterialManagementView.xaml
  19. +28
    -0
      BPA.SingleDevice/View/RawMaterialManagementView.xaml.cs
  20. +24
    -0
      BPA.SingleDevice/View/RecipeManagementView.xaml
  21. +28
    -0
      BPA.SingleDevice/View/RecipeManagementView.xaml.cs
  22. +12
    -0
      BPA.SingleDevice/ViewModel/DeviceManagementViewModel.cs
  23. +48
    -0
      BPA.SingleDevice/ViewModel/MainViewModel.cs
  24. +30
    -0
      BPA.SingleDevice/ViewModel/RawMaterialManagementViewModel.cs
  25. +12
    -0
      BPA.SingleDevice/ViewModel/RecipeManagementViewModel.cs
  26. +9
    -0
      BPA.UIControl/BPA.UIControl.csproj
  27. +67
    -0
      BPA.UIControl/BPACommand.cs
  28. +24
    -0
      BPA.UIControl/BPAVirtualizingStackPanel.cs
  29. +174
    -0
      BPA.UIControl/BPAWindow.cs
  30. +144
    -0
      BPA.UIControl/Badge.cs
  31. +63
    -0
      BPA.UIControl/Card.cs
  32. +248
    -0
      BPA.UIControl/Clock.cs
  33. +55
    -0
      BPA.UIControl/Commons/ColumnDefinitionCollectionTypeConverter.cs
  34. +22
    -0
      BPA.UIControl/Commons/ElementObservableCollection.cs
  35. +51
    -0
      BPA.UIControl/Commons/EnumDescriptionConverter.cs
  36. +255
    -0
      BPA.UIControl/Commons/FrameworkElementExtension.cs
  37. +25
    -0
      BPA.UIControl/Commons/KnownBoxes/BooleanBoxes.cs
  38. +58
    -0
      BPA.UIControl/Commons/ObjectExtension.cs
  39. +50
    -0
      BPA.UIControl/Commons/PageSizeCollectionConverter.cs
  40. +52
    -0
      BPA.UIControl/Commons/RowDefinitionCollectionTypeConverter.cs
  41. +106
    -0
      BPA.UIControl/Commons/ValidateArgument.cs
  42. +22
    -0
      BPA.UIControl/Commons/WindowHelper.cs
  43. +81
    -0
      BPA.UIControl/ControlMask.cs
  44. +29
    -0
      BPA.UIControl/Converters/BadgeOffsetConverter.cs
  45. +64
    -0
      BPA.UIControl/Converters/BooleanConverter.cs
  46. +18
    -0
      BPA.UIControl/Converters/BooleanToBrushConverter.cs
  47. +16
    -0
      BPA.UIControl/Converters/BooleanToInverseConverter.cs
  48. +16
    -0
      BPA.UIControl/Converters/BooleanToStringConverter.cs
  49. +17
    -0
      BPA.UIControl/Converters/BooleanToVisibilityConverter.cs
  50. +88
    -0
      BPA.UIControl/Converters/CarouselScaleConvereter.cs
  51. +39
    -0
      BPA.UIControl/Converters/CloneConverter.cs
  52. +26
    -0
      BPA.UIControl/Converters/ComboBoxPopupWidthConverter.cs
  53. +30
    -0
      BPA.UIControl/Converters/DataGridRowIndexConverter.cs
  54. +28
    -0
      BPA.UIControl/Converters/DoubleToGridLengthConverter.cs
  55. +76
    -0
      BPA.UIControl/Converters/DoubleToThicknessConverter.cs
  56. +32
    -0
      BPA.UIControl/Converters/DoublesToPointConverter.cs
  57. +33
    -0
      BPA.UIControl/Converters/DoublesToSizeConverter.cs
  58. +30
    -0
      BPA.UIControl/Converters/EnumGetDescriptionConverter.cs
  59. +49
    -0
      BPA.UIControl/Converters/EnumToBooleanConverter.cs
  60. +24
    -0
      BPA.UIControl/Converters/EnumToBooleanInverseConverter.cs
  61. +62
    -0
      BPA.UIControl/Converters/EnumToVisibilityConverter.cs
  62. +41
    -0
      BPA.UIControl/Converters/GetArcPointConverter.cs
  63. +28
    -0
      BPA.UIControl/Converters/GetCanvasCentreConverter.cs
  64. +46
    -0
      BPA.UIControl/Converters/GetDataGridColumnCheckedContentConverter.cs
  65. +34
    -0
      BPA.UIControl/Converters/GetPercentConverter.cs
  66. +24
    -0
      BPA.UIControl/Converters/GetWidthConverter.cs
  67. +25
    -0
      BPA.UIControl/Converters/HalfOfDoubleConverter.cs
  68. +83
    -0
      BPA.UIControl/Converters/InsideCornerRadiusConverter.cs
  69. +24
    -0
      BPA.UIControl/Converters/IsEqualConverter.cs
  70. +36
    -0
      BPA.UIControl/Converters/IsLargeArcConverter.cs
  71. +25
    -0
      BPA.UIControl/Converters/IsNullOrEmptyConverter.cs
  72. +41
    -0
      BPA.UIControl/Converters/ListViewGridViewConverter.cs
  73. +51
    -0
      BPA.UIControl/Converters/MathConverter.cs
  74. +78
    -0
      BPA.UIControl/Converters/MathMultipleConverter.cs
  75. +25
    -0
      BPA.UIControl/Converters/NotNullConverter.cs
  76. +25
    -0
      BPA.UIControl/Converters/NotNullOrEmptyConverter.cs
  77. +41
    -0
      BPA.UIControl/Converters/NullToVisibilityConverter.cs
  78. +37
    -0
      BPA.UIControl/Converters/VisibilityToBooleanConverter.cs
  79. +27
    -0
      BPA.UIControl/DataAnnotations/ColumnWidthAttribute.cs
  80. +138
    -0
      BPA.UIControl/DataGridDetailToggleButtonColumn.cs
  81. +188
    -0
      BPA.UIControl/DataGridSelectCheckBoxColumn.cs
  82. +359
    -0
      BPA.UIControl/DateTimePicker.cs
  83. BIN
     
  84. BIN
     
  85. BIN
     
  86. BIN
     
  87. BIN
     
  88. BIN
     
  89. BIN
     
  90. +7230
    -0
      BPA.UIControl/Demo/Rubyer.xml
  91. BIN
     
  92. +32
    -0
      BPA.UIControl/Demo/RubyerDemo.exe.config
  93. BIN
     
  94. BIN
     
  95. BIN
     
  96. BIN
     
  97. BIN
     
  98. BIN
     
  99. BIN
     
  100. BIN
     

+ 17
- 0
BPA.Model/BPA.Model.csproj View File

@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BPA.Communication" Version="1.0.142" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\BPA.UIControl\BPA.UIControl.csproj" />
</ItemGroup>

</Project>

+ 61
- 0
BPA.Model/RawMaterInfo.cs View File

@@ -0,0 +1,61 @@
using BPA.Helper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BPA.Model
{
public class RawMaterInfo : NotifyBase
{
/// <summary>
/// 物料信息
/// </summary>
/// <param name="id">原料ID</param>
/// <param name="name">原料名称</param>
/// <param name="dNum">原料对应设备编号</param>
/// <param name="wNum">原料对应设备的料仓编号</param>
/// <param name="lm">最后修改时间</param>
public RawMaterInfo(string id, string name, string dNum, string wNum, string lm)
{
Id = id;
Name = name;
DeviceNum = dNum;
WarehouseNum = wNum;
LastModified = lm;
}

/// <summary>
/// 物料ID
/// </summary>
public string Id { get { return _mId; } set { _mId = value; OnPropertyChanged(); } }
private string _mId;

/// <summary>
/// 原料名称
/// </summary>
public string Name { get { return _mName; } set { _mName = value; OnPropertyChanged(); } }
private string _mName;

/// <summary>
/// 设备编号
/// </summary>
public string DeviceNum { get { return _mDeviceNum; } set { _mDeviceNum = value; OnPropertyChanged(); } }
private string _mDeviceNum;

/// <summary>
/// 设备料仓编号
/// </summary>
public string WarehouseNum { get { return _mWarehouseNum; } set { _mWarehouseNum = value; OnPropertyChanged(); } }
private string _mWarehouseNum;

/// <summary>
/// 最后修改时间
/// </summary>
public string LastModified { get { return _mLastModified; } set { _mLastModified = value; OnPropertyChanged(); } }
private string _mLastModified;


}
}

+ 51
- 0
BPA.Model/ViewItem.cs View File

@@ -0,0 +1,51 @@
using BPA.Helper;
using BPA.UIControl;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BPA.Model
{
public class ViewItem : NotifyBase
{
public ViewItem(string name, string description, object content, IconType? iconType = null)
{
Name = name;
Description = description;
Content = content;
Icon = iconType;
}
public ViewItem()
{

}

/// <summary>
/// 名称
/// </summary>
public string Name { get { return _mName; } set { _mName = value; OnPropertyChanged(); } }
private string _mName;

/// <summary>
/// 描述
/// </summary>
public string Description { get { return _mDescription; } set { _mDescription = value; OnPropertyChanged(); } }
private string _mDescription;

/// <summary>
/// 内容
/// </summary>
public object Content { get { return _mContent; } set { _mContent = value; OnPropertyChanged(); } }
private object _mContent;

/// <summary>
/// 图标类型
/// </summary>
public IconType? Icon { get { return _mIcon; } set { _mIcon = value; OnPropertyChanged(); } }
private IconType? _mIcon;


}
}

+ 37
- 0
BPA.SingleDevice.sln View File

@@ -0,0 +1,37 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.33414.496
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BPA.SingleDevice", "BPA.SingleDevice\BPA.SingleDevice.csproj", "{DA921F9F-BE4E-4553-9453-BC8B35DE0C50}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BPA.Model", "BPA.Model\BPA.Model.csproj", "{CC3E5D6F-AE46-4116-8326-EFF82685EF29}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BPA.UIControl", "BPA.UIControl\BPA.UIControl.csproj", "{19BBBC01-3914-43EA-8A71-0A5DA9D8AAE1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{DA921F9F-BE4E-4553-9453-BC8B35DE0C50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DA921F9F-BE4E-4553-9453-BC8B35DE0C50}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DA921F9F-BE4E-4553-9453-BC8B35DE0C50}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DA921F9F-BE4E-4553-9453-BC8B35DE0C50}.Release|Any CPU.Build.0 = Release|Any CPU
{CC3E5D6F-AE46-4116-8326-EFF82685EF29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CC3E5D6F-AE46-4116-8326-EFF82685EF29}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CC3E5D6F-AE46-4116-8326-EFF82685EF29}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CC3E5D6F-AE46-4116-8326-EFF82685EF29}.Release|Any CPU.Build.0 = Release|Any CPU
{19BBBC01-3914-43EA-8A71-0A5DA9D8AAE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{19BBBC01-3914-43EA-8A71-0A5DA9D8AAE1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{19BBBC01-3914-43EA-8A71-0A5DA9D8AAE1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{19BBBC01-3914-43EA-8A71-0A5DA9D8AAE1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B4869FEA-BC50-4047-AF2E-70215CD6714A}
EndGlobalSection
EndGlobal

+ 16
- 0
BPA.SingleDevice/App.xaml View File

@@ -0,0 +1,16 @@
<Application
x:Class="BPA.SingleDevice.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:BPA.SingleDevice">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/BPA.UIControl;component/Themes/Resources/I18N/zh-CN.xaml" />
<ResourceDictionary Source="/BPA.UIControl;component/Themes/Generic.xaml" />
<ResourceDictionary Source="/BPA.SingleDevice;component/Themes/ThemesColor.xaml" />
<ResourceDictionary Source="/BPA.SingleDevice;component/Themes/Generic.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>

+ 31
- 0
BPA.SingleDevice/App.xaml.cs View File

@@ -0,0 +1,31 @@
using BPA.SingleDevice.View;
using BPA.UIControl;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;

namespace BPA.SingleDevice
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);

MainView mv = new MainView();
mv.Show();
}

protected override void OnExit(ExitEventArgs e)
{
base.OnExit(e);
}
}
}

+ 10
- 0
BPA.SingleDevice/AssemblyInfo.cs View File

@@ -0,0 +1,10 @@
using System.Windows;

[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]

+ 14
- 0
BPA.SingleDevice/BPA.SingleDevice.csproj View File

@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\BPA.Model\BPA.Model.csproj" />
</ItemGroup>

</Project>

+ 46
- 0
BPA.SingleDevice/Controls/ControlDisplay.cs View File

@@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows;

namespace BPA.SingleDevice.Controls
{
/// <summary>
/// 展示控件
/// </summary>
public class ControlDisplay : ContentControl
{
static ControlDisplay()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ControlDisplay), new FrameworkPropertyMetadata(typeof(ControlDisplay)));
}

public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(ControlDisplay), new PropertyMetadata(null));

/// <summary>
/// 标题
/// </summary>
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}


public static readonly DependencyProperty DescriptionProperty =
DependencyProperty.Register("Description", typeof(string), typeof(ControlDisplay), new PropertyMetadata(null));

/// <summary>
/// 描述
/// </summary>
public string Description
{
get { return (string)GetValue(DescriptionProperty); }
set { SetValue(DescriptionProperty, value); }
}
}
}

+ 29
- 0
BPA.SingleDevice/Converter/GetIconConverter.cs View File

@@ -0,0 +1,29 @@
using BPA.UIControl.Commons;
using BPA.UIControl;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;

namespace BPA.SingleDevice.Converter
{
/// <summary>
/// 根据 IconType 获取 Icon
/// </summary>
public class GetIconConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var iconType = ValidateArgument.NotNullOrEmptyCast<IconType>(value, nameof(value));
return new Icon { Type = iconType };
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

+ 32
- 0
BPA.SingleDevice/Converter/IsSelectedToIconConverter.cs View File

@@ -0,0 +1,32 @@
using BPA.UIControl;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
using System.Windows;

namespace BPA.SingleDevice.Converter
{
public class IsSelectedToIconConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((bool)value)
{
return new Icon { Type = IconType.CheckboxFill };
}
else
{
return new Icon { Type = IconType.CheckboxBlankFill };
}
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return DependencyProperty.UnsetValue;
}
}
}

+ 48
- 0
BPA.SingleDevice/Themes/Generic.xaml View File

@@ -0,0 +1,48 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:BPA.SingleDevice.Controls"
xmlns:ui="clr-namespace:BPA.UIControl;assembly=BPA.UIControl">
<Style TargetType="{x:Type controls:ControlDisplay}">
<Setter Property="Foreground" Value="{DynamicResource DefaultForeground}" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="Focusable" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:ControlDisplay}">
<Grid
Margin="0,5,0,0"
ui:GridHelper.RowDefinitions="Auto, Auto, *"
ui:PanelHelper.Spacing="10">
<TextBlock Style="{StaticResource Head3TextBlock}" Text="{TemplateBinding Title}" />
<TextBox
x:Name="descriptionText"
Grid.Row="1"
Height="Auto"
Margin="3"
BorderThickness="0"
IsReadOnly="True"
Text="{TemplateBinding Description}"
TextWrapping="Wrap" />
<ui:Card
Grid.Row="2"
Margin="3,5"
Padding="10">
<ContentPresenter
x:Name="contentPresenter"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Focusable="False"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</ui:Card>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="Description" Value="{x:Null}">
<Setter TargetName="descriptionText" Property="Visibility" Value="Collapsed" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

+ 13
- 0
BPA.SingleDevice/Themes/ThemesColor.xaml View File

@@ -0,0 +1,13 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Color x:Key="LightPrimaryColor">#2d2d2d</Color>
<Color x:Key="DarkPrimaryColor">#2196F3</Color>

<Color x:Key="LightLightColor">#6EC6FF</Color>
<Color x:Key="DarkLightColor">#6EC6FF</Color>

<Color x:Key="LightDarkColor">#0069C0</Color>
<Color x:Key="DarkDarkColor">#0069C0</Color>

<Color x:Key="LightAccentColor">#F50057</Color>
<Color x:Key="DarkAccentColor">#F50057</Color>
</ResourceDictionary>

+ 20
- 0
BPA.SingleDevice/View/DeviceManagementView.xaml View File

@@ -0,0 +1,20 @@
<UserControl
x:Class="BPA.SingleDevice.View.DeviceManagementView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:BPA.SingleDevice.View"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<Grid>
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
Background="Transparent"
FontSize="50"
Foreground="White"
Text="设备管理" />
</Grid>
</UserControl>

+ 28
- 0
BPA.SingleDevice/View/DeviceManagementView.xaml.cs View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
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;

namespace BPA.SingleDevice.View
{
/// <summary>
/// DeviceManagementView.xaml 的交互逻辑
/// </summary>
public partial class DeviceManagementView : UserControl
{
public DeviceManagementView()
{
InitializeComponent();
}
}
}

+ 83
- 0
BPA.SingleDevice/View/MainView.xaml View File

@@ -0,0 +1,83 @@
<Window
x:Class="BPA.SingleDevice.View.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converter="clr-namespace:BPA.SingleDevice.Converter"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:BPA.SingleDevice.View"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="clr-namespace:BPA.UIControl;assembly=BPA.UIControl"
xmlns:vm="clr-namespace:BPA.SingleDevice.ViewModel"
Title="味魔方自动配料系统"
Width="1200"
Topmost="True"
Height="800"
Background="Transparent"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">

<Window.DataContext>
<vm:MainViewModel />
</Window.DataContext>


<Grid>

<!--#region 菜单栏设置-->
<ui:HamburgerMenu
ui:ControlHelper.CornerRadius="0"
ui:ControlHelper.FocusedBrush="{DynamicResource Dark}"
ui:ControlHelper.FocusedForegroundBrush="{StaticResource WhiteForeground}"
ui:ControlHelper.MaskOpacity="1"
ui:ControlHelper.SelectedBrush="{DynamicResource Dark}"
ui:HeaderHelper.FontSize="16"
ui:HeaderHelper.Foreground="{StaticResource WhiteForeground}"
ui:HeaderHelper.Padding="0 13"
ui:ItemsControlHelper.ItemMargin="5 2"
ui:ItemsControlHelper.ItemPadding="10 13"
Background="#2d2d2d"
BorderThickness="0"
CollapsedWidth="56"
CornerRadius="0"
ExpandedWidth="200"
FontSize="16"
Foreground="{StaticResource WhiteForeground}"
Header="主页"
IsShowLittleBar="False"
PaneBackground="#3F000000"
PaneBorderCornerRadius="0"
SelectedIndex="0"
TransitionDuration="0:0:0.5"
TransitionType="FadeLeft">
<ui:HamburgerMenuItem
Command="{Binding SelecteCommand}"
CommandParameter="RawMaterialManagementView"
Content="{Binding MainContent}"
Header="原料管理"
IconType="ReservedFill" />
<ui:HamburgerMenuItem
Command="{Binding SelecteCommand}"
CommandParameter="DeviceManagementView"
Content="{Binding MainContent}"
Header="设备管理"
IconType="ServerFill" />
<ui:HamburgerMenuItem
Command="{Binding SelecteCommand}"
CommandParameter="RecipeManagementView"
Content="{Binding MainContent}"
Header="配方管理"
IconType="NewspaperFill" />
<!--<ui:HamburgerMenuItem
Command="{Binding SelecteCommand}"
CommandParameter="{Binding Content, RelativeSource={RelativeSource Self}}"
Content="自定义"
Header="自定义">
<ui:HamburgerMenuItem.Icon>
<Image Source="..\rubyer.ico" />
</ui:HamburgerMenuItem.Icon>
</ui:HamburgerMenuItem>-->
</ui:HamburgerMenu>
<!--#endregion-->
</Grid>

</Window>

+ 34
- 0
BPA.SingleDevice/View/MainView.xaml.cs View File

@@ -0,0 +1,34 @@
using BPA.UIControl;
using System;
using System.Collections.Generic;
using System.Linq;
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.Shapes;

namespace BPA.SingleDevice.View
{
/// <summary>
/// MainView.xaml 的交互逻辑
/// </summary>
public partial class MainView : Window
{
public MainView()
{
InitializeComponent();
this.Loaded += MainView_Loaded;
}

private void MainView_Loaded(object sender, RoutedEventArgs e)
{
ThemeManager.SwitchThemeMode(UIControl.Enums.ThemeMode.Dark);
}
}
}

+ 118
- 0
BPA.SingleDevice/View/RawMaterialManagementView.xaml View File

@@ -0,0 +1,118 @@
<UserControl
x:Class="BPA.SingleDevice.View.RawMaterialManagementView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:BPA.SingleDevice.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:BPA.SingleDevice.View"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="clr-namespace:BPA.UIControl;assembly=BPA.UIControl"
xmlns:vm="clr-namespace:BPA.SingleDevice.ViewModel"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">

<UserControl.DataContext>
<vm:RawMaterialManagementViewModel />
</UserControl.DataContext>

<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition />
</Grid.RowDefinitions>

<!--#region 功能按钮-->
<StackPanel
Grid.Row="0"
Margin="10"
HorizontalAlignment="Right"
ui:PanelHelper.Spacing="15"
Orientation="Horizontal">
<Button
HorizontalAlignment="Right"
Command="{Binding AddCommand}"
Content="添加原料"
Style="{DynamicResource DarkButton}" />

<Button
HorizontalAlignment="Right"
Command="{Binding SaveCommand}"
Content="保存修改"
Style="{DynamicResource SuccessButton}" />
</StackPanel>

<!--#endregion-->

<!--#region 列表-->
<controls:ControlDisplay Grid.Row="1">
<DataGrid
Grid.Row="1"
AutoGenerateColumns="False"
BorderBrush="Gray"
BorderThickness="1"
CanUserAddRows="False"
GridLinesVisibility="All"
IsReadOnly="True"
ItemsSource="{Binding RawMaterInfos}"
RowHeight="35"
SelectionMode="Single">
<!--<DataGrid.RowHeaderTemplate>
<DataTemplate>
<Border>
<TextBlock HorizontalAlignment="Center" Text="{Binding RelativeSource={RelativeSource AncestorType=DataGridRow}, Converter={StaticResource DataGridRowIndexConverter}}" />
</Border>
</DataTemplate>
</DataGrid.RowHeaderTemplate>-->
<DataGrid.Columns>
<DataGridTextColumn
Width="*"
Binding="{Binding Name}"
Header="原料名称" />
<DataGridTextColumn
Width="100"
Binding="{Binding DeviceNum}"
Header="设备编号" />
<DataGridTextColumn
Width="120"
Binding="{Binding WarehouseNum}"
Header="设备料仓编号" />
<DataGridTextColumn
Width="180"
Binding="{Binding LastModified}"
Header="最后修改时间" />
<DataGridTemplateColumn Width="150" Header="操作">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button
Command="{Binding DataContext.EditCommand, RelativeSource={RelativeSource AncestorType=local:RawMaterialManagementView}}"
CommandParameter="{Binding Id}"
Foreground="#2196F3"
Style="{StaticResource TextButton}">
<ui:Icon Type="Edit2Fill" />
</Button>
<Button
Command="{Binding DataContext.CopyCommand, RelativeSource={RelativeSource AncestorType=local:RawMaterialManagementView}}"
CommandParameter="{Binding Id}"
Foreground="#2196F3"
Style="{StaticResource TextButton}">
<ui:Icon Type="CopyleftFill" />
</Button>
<Button
Command="{Binding DataContext.RemoveCommand, RelativeSource={RelativeSource AncestorType=local:RawMaterialManagementView}}"
CommandParameter="{Binding Id}"
Style="{StaticResource TextErrorButton}">
<ui:Icon Type="DeleteBinFill" />
</Button>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</controls:ControlDisplay>
<!--#endregion-->

</Grid>
</UserControl>

+ 28
- 0
BPA.SingleDevice/View/RawMaterialManagementView.xaml.cs View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
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;

namespace BPA.SingleDevice.View
{
/// <summary>
/// RawMaterialManagementView.xaml 的交互逻辑
/// </summary>
public partial class RawMaterialManagementView : UserControl
{
public RawMaterialManagementView()
{
InitializeComponent();
}
}
}

+ 24
- 0
BPA.SingleDevice/View/RecipeManagementView.xaml View File

@@ -0,0 +1,24 @@
<UserControl x:Class="BPA.SingleDevice.View.RecipeManagementView"
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:controls="clr-namespace:BPA.SingleDevice.Controls"
xmlns:local="clr-namespace:BPA.SingleDevice.View"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="50"
Background="Transparent"
Foreground="White"
Text="配方管理" />

<controls:ControlDisplay Width="500" Height="100" Grid.Row="1" >
<!--<DataGrid x:Name="DataGrid2" Height="180" AutoGenerateColumns="True" CanUserAddRows="False" ItemsSource="{Binding Weathers}" />-->
</controls:ControlDisplay>

</Grid>
</UserControl>

+ 28
- 0
BPA.SingleDevice/View/RecipeManagementView.xaml.cs View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
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;

namespace BPA.SingleDevice.View
{
/// <summary>
/// RecipeManagementView.xaml 的交互逻辑
/// </summary>
public partial class RecipeManagementView : UserControl
{
public RecipeManagementView()
{
InitializeComponent();
}
}
}

+ 12
- 0
BPA.SingleDevice/ViewModel/DeviceManagementViewModel.cs View File

@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BPA.SingleDevice.ViewModel
{
internal class DeviceManagementViewModel
{
}
}

+ 48
- 0
BPA.SingleDevice/ViewModel/MainViewModel.cs View File

@@ -0,0 +1,48 @@
using BPA.Helper;
using BPA.Model;
using BPA.SingleDevice.View;
using BPA.UIControl;
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;

namespace BPA.SingleDevice.ViewModel
{
public class MainViewModel : NotifyBase
{
public MainViewModel()
{
//ViewItems = new ObservableCollection<ViewItem>
//{
// new ViewItem("原料管理", "总览", new RawMaterialManagementView(), IconType.Home2Line),
// new ViewItem("设备管理", "按钮", new DeviceManagementView(), IconType.CheckboxBlankFill),
// new ViewItem("配方管理", "文本框", new RecipeManagementView(), IconType.TBoxLine),
//};

SelecteCommand = new BPARelayCommand<object>(DoNavChanged);
}

public ObservableCollection<ViewItem> ViewItems { get; set; } = new ObservableCollection<ViewItem>();

public BPARelayCommand<object> SelecteCommand { get; set; }

private void DoNavChanged(object obj)
{
if (obj != null)
{
Type type = Type.GetType($"BPA.SingleDevice.View.{obj.ToString()}");
ConstructorInfo cti = type?.GetConstructor(Type.EmptyTypes);
MainContent = (FrameworkElement)cti?.Invoke(null);
}
}

public FrameworkElement MainContent { get { return _mMainContent; } set { _mMainContent = value; OnPropertyChanged(); } }
private FrameworkElement _mMainContent;

}
}

+ 30
- 0
BPA.SingleDevice/ViewModel/RawMaterialManagementViewModel.cs View File

@@ -0,0 +1,30 @@
using BPA.Helper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.ObjectModel;
using BPA.Model;

namespace BPA.SingleDevice.ViewModel
{
public class RawMaterialManagementViewModel : NotifyBase
{
public RawMaterialManagementViewModel()
{
for (int i = 0; i < 10; i++)
{
RawMaterInfos.Add(new RawMaterInfo(i.ToString(), i.ToString(), i.ToString(), i.ToString(), DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")));
}
}

public ObservableCollection<RawMaterInfo> RawMaterInfos { get; set; } = new ObservableCollection<RawMaterInfo>();

public BPARelayCommand AddCommand { get; set; }
public BPARelayCommand SaveCommand { get; set; }
public BPARelayCommand<object> CopyCommand { get; set; }
public BPARelayCommand<object> EditCommand { get; set; }
public BPARelayCommand<object> RemoveCommand { get; set; }
}
}

+ 12
- 0
BPA.SingleDevice/ViewModel/RecipeManagementViewModel.cs View File

@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BPA.SingleDevice.ViewModel
{
internal class RecipeManagementViewModel
{
}
}

+ 9
- 0
BPA.UIControl/BPA.UIControl.csproj View File

@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
</PropertyGroup>

</Project>

+ 67
- 0
BPA.UIControl/BPACommand.cs View File

@@ -0,0 +1,67 @@
using System;
using System.Windows.Input;

namespace BPA.UIControl
{
/// <summary>
/// Command
/// </summary>
public class BPACommand : ICommand
{
private readonly Action<object> _execute;
private readonly Func<object, bool> _canExecute;

/// <summary>
/// Initializes a new instance of the <see cref="BPACommand"/> class.
/// </summary>
/// <param name="execute">The execute.</param>
public BPACommand(Action<object> execute)
: this(execute, null)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="BPACommand"/> class.
/// </summary>
/// <param name="execute">The execute.</param>
/// <param name="canExecute">The can execute.</param>
public BPACommand(Action<object> execute, Func<object, bool> canExecute)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute ?? (x => true);
}

/// <inheritdoc/>
public bool CanExecute(object parameter)
{
return _canExecute(parameter);
}

/// <inheritdoc/>
public void Execute(object parameter)
{
_execute(parameter);
}

/// <inheritdoc/>
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}

/// <summary>
/// Refreshes the.
/// </summary>
public void Refresh()
{
CommandManager.InvalidateRequerySuggested();
}
}
}

+ 24
- 0
BPA.UIControl/BPAVirtualizingStackPanel.cs View File

@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;

namespace BPA.UIControl
{
/// <summary>
/// 公开 BringIntoView() 的 VirtualizingStackPanel
/// 当使用 TreeViewHelper.RightClickToSelected 需要使用该虚拟化 StackPanel 作为 ItemsPanel
/// </summary>
public class BPAVirtualizingStackPanel : VirtualizingStackPanel
{
/// <summary>
/// Publically expose BringIndexIntoView.
/// </summary>
public void BringIntoView(int index)
{
this.BringIndexIntoView(index);
}
}
}

+ 174
- 0
BPA.UIControl/BPAWindow.cs View File

@@ -0,0 +1,174 @@
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;

namespace BPA.UIControl
{
/// <summary>
/// rubyer 窗体
/// </summary>
public class BPAWindow : Window
{
/// <summary>
/// Initializes a new instance of the <see cref="BPAWindow"/> class.
/// </summary>
public BPAWindow()
{
DefaultStyleKey = typeof(BPAWindow);
CommandBindings.Add(new CommandBinding(SystemCommands.CloseWindowCommand, CloseWindow));
CommandBindings.Add(new CommandBinding(SystemCommands.MaximizeWindowCommand, MaximizeWindow, CanResizeWindow));
CommandBindings.Add(new CommandBinding(SystemCommands.MinimizeWindowCommand, MinimizeWindow, CanMinimizeWindow));
CommandBindings.Add(new CommandBinding(SystemCommands.RestoreWindowCommand, RestoreWindow, CanResizeWindow));
CommandBindings.Add(new CommandBinding(SystemCommands.ShowSystemMenuCommand, ShowSystemMenu));
}

/// <inheritdoc/>
protected override void OnContentRendered(EventArgs e)
{
base.OnContentRendered(e);
if (SizeToContent == SizeToContent.WidthAndHeight)
InvalidateMeasure();
}

#region Window Commands

private void CanResizeWindow(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = ResizeMode == ResizeMode.CanResize || ResizeMode == ResizeMode.CanResizeWithGrip;
}

private void CanMinimizeWindow(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = ResizeMode != ResizeMode.NoResize;
}

private void CloseWindow(object sender, ExecutedRoutedEventArgs e)
{
(sender as Window).Close();
}

private void MaximizeWindow(object sender, ExecutedRoutedEventArgs e)
{
SystemCommands.MaximizeWindow(this);
}

private void MinimizeWindow(object sender, ExecutedRoutedEventArgs e)
{
SystemCommands.MinimizeWindow(this);
}

private void RestoreWindow(object sender, ExecutedRoutedEventArgs e)
{
SystemCommands.RestoreWindow(this);
}

private void ShowSystemMenu(object sender, ExecutedRoutedEventArgs e)
{
var element = e.OriginalSource as FrameworkElement;
if (element == null)
return;

var point = WindowState == WindowState.Maximized ? new Point(0, element.ActualHeight)
: new Point(Left + BorderThickness.Left, element.ActualHeight + Top + BorderThickness.Top);
point = element.TransformToAncestor(this).Transform(point);
SystemCommands.ShowSystemMenu(this, point);
}

#endregion Window Commands

#region 属性

/// <summary>
/// 标题栏内容
/// </summary>
public static readonly DependencyProperty TitleBarContentProperty =
DependencyProperty.Register("TitleBarContent", typeof(object), typeof(BPAWindow), new PropertyMetadata(default(object)));

/// <summary>
/// 标题栏内容
/// </summary>
public object TitleBarContent
{
get { return (object)GetValue(TitleBarContentProperty); }
set { SetValue(TitleBarContentProperty, value); }
}

/// <summary>
/// 是否显示标题栏阴影
/// </summary>
public static readonly DependencyProperty TitleShadowProperty =
DependencyProperty.Register("TitleShadow", typeof(bool), typeof(BPAWindow), new PropertyMetadata(default(bool)));

/// <summary>
/// 是否显示标题栏阴影
/// </summary>
public bool TitleShadow
{
get { return (bool)GetValue(TitleShadowProperty); }
set { SetValue(TitleShadowProperty, value); }
}

/// <summary>
/// 标题栏高度
/// </summary>
public static readonly DependencyProperty TitleHeightProperty =
DependencyProperty.Register("TitleHeight", typeof(double), typeof(BPAWindow), new PropertyMetadata(default(double)));

/// <summary>
/// 标题栏高度
/// </summary>
public double TitleHeight
{
get { return (double)GetValue(TitleHeightProperty); }
set { SetValue(TitleHeightProperty, value); }
}

/// <summary>
/// 标题背景色
/// </summary>
public static readonly DependencyProperty TitleBackgroundProperty =
DependencyProperty.Register("TitleBackground", typeof(Brush), typeof(BPAWindow), new PropertyMetadata(default(Brush)));

/// <summary>
/// 标题背景色
/// </summary>
public Brush TitleBackground
{
get { return (Brush)GetValue(TitleBackgroundProperty); }
set { SetValue(TitleBackgroundProperty, value); }
}

/// <summary>
/// 标题前景色
/// </summary>
public static readonly DependencyProperty TitleForegroundProperty =
DependencyProperty.Register("TitleForeground", typeof(Brush), typeof(BPAWindow), new PropertyMetadata(default(Brush)));

/// <summary>
/// 标题前景色
/// </summary>
public Brush TitleForeground
{
get { return (Brush)GetValue(TitleForegroundProperty); }
set { SetValue(TitleForegroundProperty, value); }
}

/// <summary>
/// Window 非活动边框颜色
/// </summary>
public static readonly DependencyProperty InactiveBorderBrushProperty =
DependencyProperty.Register("InactiveBorderBrush", typeof(Brush), typeof(BPAWindow), new PropertyMetadata(default(Brush)));

/// <summary>
/// Window 非活动边框颜色
/// </summary>
public Brush InactiveBorderBrush
{
get { return (Brush)GetValue(InactiveBorderBrushProperty); }
set { SetValue(InactiveBorderBrushProperty, value); }
}
#endregion 属性
}
}

+ 144
- 0
BPA.UIControl/Badge.cs View File

@@ -0,0 +1,144 @@
using BPA.UIControl.Commons.KnownBoxes;
using System;
using System.Collections.Generic;
using System.Linq;
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;

namespace BPA.UIControl
{
/// <summary>
/// 标记
/// </summary>
public class Badge : ContentControl
{
static Badge()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Badge), new FrameworkPropertyMetadata(typeof(Badge)));
}

#region 属性

/// <summary>
/// 标记颜色
/// </summary>
public static readonly DependencyProperty BadgeBrushProperty =
DependencyProperty.Register("BadgeBrush", typeof(Brush), typeof(Badge), new PropertyMetadata(default(Brush)));

/// <summary>
/// 标记颜色
/// </summary>
public Brush BadgeBrush
{
get { return (Brush)GetValue(BadgeBrushProperty); }
set { SetValue(BadgeBrushProperty, value); }
}

/// <summary>
/// 显示文本
/// </summary>
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(Badge), new PropertyMetadata(default(string), OnTextChanged));

/// <summary>
/// 显示文本
/// </summary>
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}

/// <summary>
/// 标记尺寸
/// </summary>
public static readonly DependencyProperty BadgeSizeProperty =
DependencyProperty.Register("BadgeSize", typeof(double), typeof(Badge), new PropertyMetadata(default(double)));

/// <summary>
/// 标记尺寸
/// </summary>
public double BadgeSize
{
get { return (double)GetValue(BadgeSizeProperty); }
set { SetValue(BadgeSizeProperty, value); }
}

/// <summary>
/// 标记字体尺寸
/// </summary>
public static readonly DependencyProperty BadgeFontSizeProperty =
DependencyProperty.Register("BadgeFontSize", typeof(double), typeof(Badge), new PropertyMetadata(default(double)));

/// <summary>
/// 标记字体尺寸
/// </summary>
public double BadgeFontSize
{
get { return (double)GetValue(BadgeFontSizeProperty); }
set { SetValue(BadgeFontSizeProperty, value); }
}

/// <summary>
/// 是否隐藏
/// </summary>
public static readonly DependencyProperty IsHiddenProperty =
DependencyProperty.Register("IsHidden", typeof(bool), typeof(Badge), new PropertyMetadata(BooleanBoxes.FalseBox));

/// <summary>
/// 是否隐藏
/// </summary>
public bool IsHidden
{
get { return (bool)GetValue(IsHiddenProperty); }
set { SetValue(IsHiddenProperty, BooleanBoxes.Box(value)); }
}

#endregion 属性

#region 事件

/// <summary>
/// 文本改变事件
/// </summary>
public static readonly RoutedEvent TextChangedEvent =
EventManager.RegisterRoutedEvent("TextChanged", RoutingStrategy.Direct, typeof(RoutedEventArgs), typeof(Badge));

/// <summary>
/// 文本改变事件
/// </summary>处理
public event RoutedEventHandler TextChanged
{
add { AddHandler(TextChangedEvent, value); }
remove { RemoveHandler(TextChangedEvent, value); }
}

#endregion 事件

#region 方法

private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.OldValue == null)
{
return;
}

Badge badge = d as Badge;
RoutedEventArgs args = new RoutedEventArgs();
args.RoutedEvent = TextChangedEvent;
badge.RaiseEvent(args);
}

#endregion 方法
}
}

+ 63
- 0
BPA.UIControl/Card.cs View File

@@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.Linq;
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.Effects;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace BPA.UIControl
{
/// <summary>
/// 卡片
/// </summary>
public class Card : ContentControl
{
/// <summary>
/// Initializes a new instance of the <see cref="Card"/> class.
/// </summary>
static Card()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Card), new FrameworkPropertyMetadata(typeof(Card)));
}

/// <summary>
/// 圆角半径
/// </summary>
public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register(
"CornerRadius", typeof(CornerRadius), typeof(Card), new PropertyMetadata(default(CornerRadius)));

/// <summary>
/// 圆角半径
/// </summary>
public CornerRadius CornerRadius
{
get { return (CornerRadius)GetValue(CornerRadiusProperty); }
set { SetValue(CornerRadiusProperty, value); }
}

/// <summary>
/// 边框阴影
/// </summary>
public static readonly DependencyProperty BorderEffectProperty = DependencyProperty.Register(
"BorderEffect", typeof(Effect), typeof(Card), new PropertyMetadata(default(Effect)));

/// <summary>
/// 边框阴影
/// </summary>
public Effect BorderEffect
{
get { return (Effect)GetValue(BorderEffectProperty); }
set { SetValue(BorderEffectProperty, value); }
}

}
}

+ 248
- 0
BPA.UIControl/Clock.cs View File

@@ -0,0 +1,248 @@
using BPA.UIControl.Commons.KnownBoxes;
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;

namespace BPA.UIControl
{
/// <summary>
/// 时钟
/// </summary>
[TemplatePart(Name = HourListPartName, Type = typeof(Selector))]
[TemplatePart(Name = MinuteListPartName, Type = typeof(Selector))]
[TemplatePart(Name = ConfirmPartName, Type = typeof(Button))]
public class Clock : Control
{
/// <summary>
/// 小时列表名称
/// </summary>
public const string HourListPartName = "PART_HourList";

/// <summary>
/// 分钟列表名称
/// </summary>
public const string MinuteListPartName = "PART_MinuteList";

/// <summary>
/// 确认按钮名称
/// </summary>
public const string ConfirmPartName = "PART_ConfirmButton";

/// <summary>
/// Initializes a new instance of the <see cref="Clock"/> class.
/// </summary>
static Clock()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Clock), new FrameworkPropertyMetadata(typeof(Clock)));
}

/// <inheritdoc/>
public override void OnApplyTemplate()
{
base.OnApplyTemplate();

if (GetTemplateChild(ConfirmPartName) is Button confirmButton)
{
confirmButton.Click += ConfirmButton_Click;
}

Hours = new ObservableCollection<int>(Enumerable.Range(0, 24));
Minutes = new ObservableCollection<int>(Enumerable.Range(0, 60));

CurrentTime = new DateTime(DateTime.Now.TimeOfDay.Ticks);
Hour = CurrentTime.Value.Hour;
Minute = CurrentTime.Value.Minute;
}

#region 路由事件

/// <summary>
/// 选择时间改变事件
/// </summary>
public static readonly RoutedEvent SelectedTimeChangedEvent = EventManager.RegisterRoutedEvent(
"SelectedTimeChanged", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler<DateTime?>), typeof(Clock));

/// <summary>
/// 选择时间改变事件处理
/// </summary>
public event RoutedPropertyChangedEventHandler<DateTime?> SelectedTimeChanged
{
add { AddHandler(SelectedTimeChangedEvent, value); }
remove { RemoveHandler(SelectedTimeChangedEvent, value); }
}

/// <summary>
/// 当前事件改变事件
/// </summary>
public static readonly RoutedEvent CurrentTimeChangedEvent = EventManager.RegisterRoutedEvent(
"CurrentTimeChanged", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler<DateTime?>), typeof(Clock));

/// <summary>
/// 当前事件改变事件处理
/// </summary>
public event RoutedPropertyChangedEventHandler<DateTime?> CurrentTimeChanged
{
add { AddHandler(CurrentTimeChangedEvent, value); }
remove { RemoveHandler(CurrentTimeChangedEvent, value); }
}
#endregion

#region 依赖属性

/// <summary>
/// 时集合
/// </summary>
internal static readonly DependencyProperty HoursProperty = DependencyProperty.Register(
"Hours", typeof(ObservableCollection<int>), typeof(Clock), new PropertyMetadata(null));

/// <summary>
/// 时集合
/// </summary>
internal ObservableCollection<int> Hours
{
get { return (ObservableCollection<int>)GetValue(HoursProperty); }
set { SetValue(HoursProperty, value); }
}

/// <summary>
/// 时
/// </summary>
public static readonly DependencyProperty HourProperty = DependencyProperty.Register(
"Hour", typeof(int), typeof(Clock), new PropertyMetadata(0, OnItemSeletedChanged));

/// <summary>
/// 时
/// </summary>
public int Hour
{
get { return (int)GetValue(HourProperty); }
set { SetValue(HourProperty, value); }
}

/// <summary>
/// 分集合
/// </summary>
internal static readonly DependencyProperty MinutesProperty = DependencyProperty.Register(
"Minutes", typeof(ObservableCollection<int>), typeof(Clock), new PropertyMetadata(null));

/// <summary>
/// 分集合
/// </summary>
internal ObservableCollection<int> Minutes
{
get { return (ObservableCollection<int>)GetValue(MinutesProperty); }
set { SetValue(MinutesProperty, value); }
}

/// <summary>
/// 分
/// </summary>
public static readonly DependencyProperty MinuteProperty = DependencyProperty.Register(
"Minute", typeof(int), typeof(Clock), new PropertyMetadata(0, OnItemSeletedChanged));

/// <summary>
/// 分
/// </summary>
public int Minute
{
get { return (int)GetValue(MinuteProperty); }
set { SetValue(MinuteProperty, value); }
}

/// <summary>
/// 选中时间
/// </summary>
public static readonly DependencyProperty SelectedTimeProperty = DependencyProperty.Register(
"SelectedTime", typeof(DateTime?), typeof(Clock), new PropertyMetadata(default(DateTime), OnSelectTimeChanged));

private static void OnSelectTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var clock = (Clock)d;
if (clock.SelectedTime != null)
{
clock.Hour = clock.SelectedTime.Value.Hour;
clock.Minute = clock.SelectedTime.Value.Minute;
}
}

/// <summary>
/// 选中时间
/// </summary>
public DateTime? SelectedTime
{
get { return (DateTime?)GetValue(SelectedTimeProperty); }
set { SetValue(SelectedTimeProperty, value); }
}

/// <summary>
/// 当前时间
/// </summary>
public static readonly DependencyProperty CurrentTimeProperty =
DependencyProperty.Register("CurrentTime", typeof(DateTime?), typeof(Clock), new PropertyMetadata(default(DateTime?)));

/// <summary>
/// 当前时间
/// </summary>
public DateTime? CurrentTime
{
get { return (DateTime?)GetValue(CurrentTimeProperty); }
set { SetValue(CurrentTimeProperty, value); }
}

/// <summary>
/// 是否显示确认按钮
/// </summary>
public static readonly DependencyProperty IsShowConfirmButtonProperty =
DependencyProperty.Register("IsShowConfirmButton", typeof(bool), typeof(Clock), new PropertyMetadata(BooleanBoxes.TrueBox));

/// <summary>
/// 是否显示确认按钮
/// </summary>
public bool IsShowConfirmButton
{
get { return (bool)GetValue(IsShowConfirmButtonProperty); }
set { SetValue(IsShowConfirmButtonProperty, BooleanBoxes.Box(value)); }
}

#endregion

/// <summary>
/// 时间选择改变
/// </summary>
private static void OnItemSeletedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var clock = d as Clock;

if (!clock.IsLoaded)
{
return;
}

clock.CurrentTime = Convert.ToDateTime($"{clock.Hour}:{clock.Minute}");

if (e.NewValue != null)
{
var args = new RoutedPropertyChangedEventArgs<DateTime?>(DateTime.Now, (DateTime)clock.CurrentTime);
args.RoutedEvent = CurrentTimeChangedEvent;
clock.RaiseEvent(args);
}
}

/// <summary>
/// 确认时间
/// </summary>
private void ConfirmButton_Click(object sender, RoutedEventArgs e)
{
var oldTime = SelectedTime;
var newTime = CurrentTime;
SelectedTime = CurrentTime;

var args = new RoutedPropertyChangedEventArgs<DateTime?>(oldTime, newTime);
args.RoutedEvent = SelectedTimeChangedEvent;
RaiseEvent(args);
}
}
}

+ 55
- 0
BPA.UIControl/Commons/ColumnDefinitionCollectionTypeConverter.cs View File

@@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows;

namespace BPA.UIControl.Commons
{
/// <summary>
/// ColumnDefinitionCollection 类型转换
/// from https://github.com/dotnet/maui
/// Microsoft.Maui.Controls.ColumnDefinitionCollectionTypeConverter.cs
/// </summary>
public class ColumnDefinitionCollectionTypeConverter : TypeConverter
{
/// <inheritdoc/>
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
=> sourceType == typeof(string);

/// <inheritdoc/>
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
=> destinationType == typeof(string);

/// <inheritdoc/>
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
var strValue = value?.ToString();

if (strValue != null)
{
var lengths = strValue.Split(',');
var converter = new GridLengthConverter();
var definitions = new ColumnDefinition[lengths.Length];
for (var i = 0; i < lengths.Length; i++)
definitions[i] = new ColumnDefinition { Width = (GridLength)converter.ConvertFromInvariantString(lengths[i]) };
return definitions;
}

throw new InvalidOperationException(string.Format("Cannot convert \"{0}\" into {1}", strValue, typeof(ColumnDefinitionCollection)));
}

/// <inheritdoc/>
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (!(value is ColumnDefinitionCollection cdc))
throw new NotSupportedException();
var converter = new GridLengthConverter();
return string.Join(", ", cdc.Select(cd => converter.ConvertToInvariantString(cd.Width)));
}
}
}

+ 22
- 0
BPA.UIControl/Commons/ElementObservableCollection.cs View File

@@ -0,0 +1,22 @@
using System.Collections.ObjectModel;
using System.Windows;

namespace BPA.UIControl.Commons
{
/// <summary>
/// 归属元素的可通知集合
/// </summary>
/// <typeparam name="T"></typeparam>
public class ElementObservableCollection<T> : ObservableCollection<T>
{
/// <summary>
/// 所有者
/// </summary>
public UIElement Owner { get; }

public ElementObservableCollection(UIElement owner)
{
Owner = owner;
}
}
}

+ 51
- 0
BPA.UIControl/Commons/EnumDescriptionConverter.cs View File

@@ -0,0 +1,51 @@
using System;
using System.ComponentModel;
using System.Globalization;

namespace BPA.UIControl.Commons
{
/// <summary>
/// Enum -> Description
/// </summary>
public class EnumDescriptionConverter : EnumConverter
{
/// <summary>
/// Initializes a new instance of the <see cref="EnumDescriptionConverter"/> class.
/// </summary>
/// <param name="type">The type.</param>
public EnumDescriptionConverter(Type type)
: base(type)
{
}

/// <inheritdoc/>
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string))
{
return value?.GetDescription();
}

return string.Empty;
}

/// <inheritdoc/>
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string)
{
var enumValues = EnumType.GetEnumValues();
for (int i = 0; i < enumValues.Length; i++)
{
var enumValue = enumValues.GetValue(i);
if (value.Equals(enumValue.GetDescription()))
{
return enumValue;
}
}
}

return base.ConvertFrom(context, culture, value);
}
}
}

+ 255
- 0
BPA.UIControl/Commons/FrameworkElementExtension.cs View File

@@ -0,0 +1,255 @@
using BPA.UIControl.Commons;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Automation;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;

namespace BPA.UIControl
{
/// <summary>
/// FrameworkElement 扩展
/// </summary>
public static class FrameworkElementExtension
{
/// <summary>
/// 获取 root 元素
/// </summary>
/// <param name="dependencyObject">依赖对象</param>
/// <returns>root 元素</returns>
public static DependencyObject GetRoot(this DependencyObject dependencyObject)
{
DependencyObject result = dependencyObject;
while ((dependencyObject = VisualTreeHelper.GetParent(dependencyObject)) != null)
{
result = dependencyObject;
}
return result;
}

/// <summary>
/// 查找父元素
/// </summary>
/// <typeparam name="T">类型</typeparam>
/// <param name="element">元素</param>
/// <returns>父元素</returns>
public static T FindParent<T>(this FrameworkElement element) where T : FrameworkElement
{
T t = default;
while (element != null)
{
t = element as T;
if (t != null)
{
break;
}
element = element.Parent as FrameworkElement;
}
return t;
}

/// <summary>
/// 获取最顶层父元素
/// </summary>
/// <typeparam name="T">类型</typeparam>
/// <param name="dependencyObject">依赖对象</param>
/// <returns>父元素</returns>
public static T GetOldestParent<T>(this DependencyObject dependencyObject) where T : DependencyObject
{
T t;
while ((t = VisualTreeHelper.GetParent(dependencyObject) as T) != null)
{
dependencyObject = t;
}
return dependencyObject.AssertCast<T>();
}

/// <summary>
/// 尝试查找资源
/// </summary>
/// <typeparam name="T">类型</typeparam>
/// <param name="root">元素</param>
/// <param name="resourceKey">资源 key</param>
/// <returns>资源</returns>
internal static T TryFindResourceUp<T>(this FrameworkElement root, object resourceKey)
{
if (root != null && root.Resources.Contains(resourceKey))
{
object obj = root.Resources[resourceKey];
if (!(obj is T))
{
return default;
}
return (T)obj;
}
else
{
if (root.Parent is FrameworkElement)
{
return ((FrameworkElement)root.Parent).TryFindResourceUp<T>(resourceKey);
}
return default;
}
}

/// <summary>
/// 获取子元素
/// </summary>
/// <typeparam name="T">类型</typeparam>
/// <param name="parent">父元素</param>
/// <returns>子元素</returns>
public static T TryGetChildPartFromVisualTree<T>(this FrameworkElement parent) where T : FrameworkElement
{
return parent.TryGetChildFromVisualTree<T>(null);
}

/// <summary>
/// 根据 Name 获取子元素
/// </summary>
/// <typeparam name="T">类型</typeparam>
/// <param name="parent">父元素</param>
/// <param name="partName">名称</param>
/// <returns>子元素</returns>
public static T TryGetChildPartFromVisualTree<T>(this FrameworkElement parent, string partName) where T : FrameworkElement
{
ValidateArgument.NotNullOrEmpty(partName, "partName");
return parent.TryGetChildFromVisualTree<T>((FrameworkElement childControl) => childControl.Name == partName);
}

/// <summary>
/// 根据 AutomationId 获取子元素
/// </summary>
/// <typeparam name="T">类型</typeparam>
/// <param name="parent">父元素</param>
/// <param name="automationId">自动 ID</param>
/// <returns>子元素</returns>
internal static T TryGetChildWithAutomationIdFromVisualTree<T>(this FrameworkElement parent, string automationId) where T : FrameworkElement
{
ValidateArgument.NotNullOrEmpty(automationId, "automationId");
return parent.TryGetChildFromVisualTree<T>((FrameworkElement childControl) => AutomationProperties.GetAutomationId(childControl) == automationId);
}

/// <summary>
/// VisualTree 获取子元素
/// </summary>
/// <typeparam name="T">类型</typeparam>
/// <param name="parent">父元素</param>
/// <param name="evaluateChild">子元素表达式</param>
/// <returns>子元素</returns>
internal static T TryGetChildFromVisualTree<T>(this FrameworkElement parent, Func<FrameworkElement, bool> evaluateChild) where T : FrameworkElement
{
ValidateArgument.NotNull(parent, "parent");
FrameworkElement frameworkElement = null;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
frameworkElement = VisualTreeHelper.GetChild(parent, i) as FrameworkElement;
if (frameworkElement != null && (frameworkElement is T && (evaluateChild == null || evaluateChild.Invoke(frameworkElement)) || (frameworkElement = frameworkElement.TryGetChildFromVisualTree<T>(evaluateChild)) != null))
{
break;
}
}

return frameworkElement as T;
}

/// <summary>
/// 获取父元素
/// </summary>
/// <typeparam name="T">类型</typeparam>
/// <param name="child">子元素</param>
/// <returns>父元素</returns>
public static T TryGetParentFromVisualTree<T>(this FrameworkElement child) where T : FrameworkElement
{
if (child != null && !(child is T))
{
return (VisualTreeHelper.GetParent(child) as FrameworkElement).TryGetParentFromVisualTree<T>();
}

return (T)(object)child;
}

/// <summary>
/// 获取父元素
/// </summary>
/// <typeparam name="T">类型</typeparam>
/// <param name="child">子元素</param>
/// <returns>父元素</returns>
public static T TryGetParentFromVisualTree<T>(this DependencyObject child) where T : DependencyObject
{
if (child != null && !(child is T))
{
return VisualTreeHelper.GetParent(child).TryGetParentFromVisualTree<T>();
}

return (T)(object)child;
}

/// <summary>
/// 遍历子元素
/// </summary>
/// <param name="dependencyObj">依赖对象</param>
/// <param name="childAction">变量方法</param>
internal static void ForEachVisualChild(this DependencyObject dependencyObj, Action<DependencyObject> childAction)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(dependencyObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(dependencyObj, i);
child.ForEachVisualChild(childAction);
childAction.Invoke(child);
}
}

/// <summary>
/// 是否包含子对象
/// </summary>
/// <param name="parent">父元素</param>
/// <param name="dependencyObj">依赖对象</param>
/// <returns>结果</returns>
public static bool ContainsChild(this DependencyObject parent, DependencyObject dependencyObj)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(parent, i);
if (child == dependencyObj || child.ContainsChild(dependencyObj))
{
return true;
}
}

return false;
}

/// <summary>
/// 获取依赖属性的值
/// </summary>
/// <typeparam name="T">类型</typeparam>
/// <param name="reference">对象</param>
/// <param name="property">属性</param>
/// <returns>属性的值</returns>
public static T GetAncestorValue<T>(this DependencyObject reference, DependencyProperty property)
{
T result = default;
while (reference != null && (result = ObjectExtension.AssertCast<T>(reference.GetValue(property))) == null)
{
DependencyObject parent = VisualTreeHelper.GetParent(reference);
if (parent == null)
{
FrameworkElement frameworkElement = reference as FrameworkElement;
if (frameworkElement != null)
{
parent = frameworkElement.Parent;
}
}

reference = parent;
}

return result;
}
}
}

+ 25
- 0
BPA.UIControl/Commons/KnownBoxes/BooleanBoxes.cs View File

@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BPA.UIControl.Commons.KnownBoxes
{
internal static class BooleanBoxes
{
internal static object TrueBox = true;

internal static object FalseBox = false;

internal static object Box(bool value)
{
if (value)
{
return TrueBox;
}

return FalseBox;
}
}
}

+ 58
- 0
BPA.UIControl/Commons/ObjectExtension.cs View File

@@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace BPA.UIControl.Commons
{
/// <summary>
/// Object 扩展
/// </summary>
public static class ObjectExtension
{
/// <summary>
/// 强转为某个类型
/// </summary>
/// <param name="value">The value.</param>
/// <returns>A T.</returns>
public static T AssertCast<T>(this object value)
{
return (T)((object)value);
}

/// <summary>
/// 强转为某个类型, 不为空
/// </summary>
/// <param name="value">The value.</param>
/// <returns>A T.</returns>
public static T AssertCastNotNull<T>(this object value)
{
return value.AssertCast<T>();
}

/// <summary>
/// 获取描述
/// </summary>
/// <param name="value">对象</param>
/// <returns>描述</returns>
public static string GetDescription(this object value)
{
if (value != null)
{
FieldInfo fieldInfo = value.GetType().GetField(value.ToString());
if (fieldInfo != null)
{
var attributes = fieldInfo.GetCustomAttributes<DescriptionAttribute>(inherit: false);
return ((attributes.Count() > 0) && (!string.IsNullOrEmpty(attributes.First().Description)))
? attributes.First().Description
: value.ToString();
}
}

return string.Empty;
}
}
}

+ 50
- 0
BPA.UIControl/Commons/PageSizeCollectionConverter.cs View File

@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows;

namespace BPA.UIControl.Commons
{
/// <summary>
/// 每页大小集合转换器
/// </summary>
public class PageSizeCollectionConverter : TypeConverter
{
/// <inheritdoc/>
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
=> sourceType == typeof(string);

/// <inheritdoc/>
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
=> true;

/// <inheritdoc/>
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
var strValue = value?.ToString();

if (strValue != null)
{
var sizes = strValue.Split(',');

return sizes.Select(x => int.Parse(x));
}

throw new InvalidOperationException(string.Format("Cannot convert \"{0}\" into {1}", strValue, typeof(IEnumerable<int>)));
}

/// <inheritdoc/>
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (!(value is IEnumerable<int> sizes))
throw new NotSupportedException();

return string.Join(", ", sizes);
}
}
}

+ 52
- 0
BPA.UIControl/Commons/RowDefinitionCollectionTypeConverter.cs View File

@@ -0,0 +1,52 @@
using System;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Windows.Controls;
using System.Windows;

namespace BPA.UIControl.Commons
{
/// <summary>
/// RowDefinitionCollection 类型转换
/// from https://github.com/dotnet/maui
/// Microsoft.Maui.Controls.RowDefinitionCollectionTypeConverter.cs
/// </summary>
public class RowDefinitionCollectionTypeConverter : TypeConverter
{
/// <inheritdoc/>
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
=> sourceType == typeof(string);

/// <inheritdoc/>
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
=> true;

/// <inheritdoc/>
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
var strValue = value?.ToString();

if (strValue != null)
{
var lengths = strValue.Split(',');
var converter = new GridLengthConverter();
var definitions = new RowDefinition[lengths.Length];
for (var i = 0; i < lengths.Length; i++)
definitions[i] = new RowDefinition { Height = (GridLength)converter.ConvertFromInvariantString(lengths[i]) };
return definitions;
}

throw new InvalidOperationException(string.Format("Cannot convert \"{0}\" into {1}", strValue, typeof(RowDefinitionCollection)));
}

/// <inheritdoc/>
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (!(value is RowDefinitionCollection rdc))
throw new NotSupportedException();
var converter = new GridLengthConverter();
return string.Join(", ", rdc.Select(rd => converter.ConvertToInvariantString(rd.Height)));
}
}
}

+ 106
- 0
BPA.UIControl/Commons/ValidateArgument.cs View File

@@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BPA.UIControl.Commons
{
/// <summary>
/// 参数验证
/// </summary>
public sealed class ValidateArgument
{
/// <summary>
/// 非 null
/// </summary>
/// <param name="obj">对象</param>
/// <param name="parameterName">参数名称</param>
/// <exception cref="ArgumentNullException"></exception>
public static void NotNull(object obj, string parameterName)
{
if (obj == null)
{
throw new ArgumentNullException(parameterName);
}
}

/// <summary>
/// 非 null 或空
/// </summary>
/// <param name="parameterValue">字符串值</param>
/// <param name="parameterName">参数名称</param>
/// <exception cref="ArgumentException"></exception>
public static void NotNullOrEmpty(string parameterValue, string parameterName)
{
if (string.IsNullOrEmpty(parameterValue))
{
throw new ArgumentException(parameterName);
}
}

/// <summary>
/// 非 Null 或空类型转换
/// </summary>
/// <typeparam name="T">类型</typeparam>
/// <param name="parameterValue">参数值</param>
/// <param name="parameterName">参数名称</param>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
public static T NotNullOrEmptyCast<T>(object parameterValue, string parameterName)
{
ValidateArgument.NotNull(parameterValue, parameterName);
if (parameterValue is T)
{
return (T)((object)parameterValue);
}
throw new ArgumentException(parameterName);
}

/// <summary>
/// 类型转换
/// </summary>
/// <typeparam name="T">类型</typeparam>
/// <param name="parameterValue">参数值</param>
/// <param name="parameterName">参数名称</param>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
public static T Cast<T>(object parameterValue, string parameterName)
{
if (parameterValue == null || parameterValue is T)
{
return (T)((object)parameterValue);
}
throw new ArgumentException(parameterName);
}

/// <summary>
/// 是否在范围内
/// </summary>
/// <param name="checkedExpression">检查表达式</param>
/// <param name="parameterName">参数名称</param>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static void Range(bool checkedExpression, string parameterName)
{
if (!checkedExpression)
{
throw new ArgumentOutOfRangeException(parameterName);
}
}

/// <summary>
/// 是否在范围内
/// </summary>
/// <param name="checkedExpression">检查表达式</param>
/// <param name="parameterName">参数名称</param>
/// <param name="message">错误信息</param>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static void Range(bool checkedExpression, string parameterName, string message)
{
if (!checkedExpression)
{
throw new ArgumentOutOfRangeException(parameterName, message);
}
}
}
}

+ 22
- 0
BPA.UIControl/Commons/WindowHelper.cs View File

@@ -0,0 +1,22 @@
using System.Linq;
using System.Windows;

namespace BPA.UIControl.Commons
{
/// <summary>
/// Window 帮助类
/// </summary>
public class WindowHelper
{
/// <summary>
/// 获取当前 Window, 找不到 Active Window 时返回 MainWindow
/// </summary>
/// <returns>当前 Window</returns>
public static Window GetCurrentWindow()
{
var window = Application.Current.Windows.OfType<Window>().FirstOrDefault(x => x.IsActive) ??
Application.Current.MainWindow;
return window;
}
}
}

+ 81
- 0
BPA.UIControl/ControlMask.cs View File

@@ -0,0 +1,81 @@
using BPA.UIControl.Commons.KnownBoxes;
using System.Windows;
using System.Windows.Controls;

namespace BPA.UIControl
{
/// <summary>
/// 控件遮罩
/// 用于提供 MouseOver 和 Pressed 等效果
/// </summary>
public class ControlMask : Control
{
/// <summary>
/// Initializes a new instance of the <see cref="ControlMask"/> class.
/// </summary>
static ControlMask()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ControlMask), new FrameworkPropertyMetadata(typeof(ControlMask)));
}

/// <summary>
/// 是否激活
/// </summary>
public static readonly DependencyProperty IsActiveProperty = DependencyProperty.Register(
"IsActive", typeof(bool), typeof(ControlMask), new PropertyMetadata(BooleanBoxes.FalseBox));

/// <summary>
/// 是否激活
/// </summary>
public bool IsActive
{
get { return (bool)GetValue(IsActiveProperty); }
set { SetValue(IsActiveProperty, BooleanBoxes.Box(value)); }
}

/// <summary>
/// 圆角半径
/// </summary>
public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register(
"CornerRadius", typeof(CornerRadius), typeof(ControlMask), new PropertyMetadata(default(CornerRadius)));

/// <summary>
/// 圆角半径
/// </summary>
public CornerRadius CornerRadius
{
get { return (CornerRadius)GetValue(CornerRadiusProperty); }
set { SetValue(CornerRadiusProperty, value); }
}

/// <summary>
/// 遮罩透明度
/// </summary>
public static readonly DependencyProperty MaskOpacityProperty = DependencyProperty.Register(
"MaskOpacity", typeof(double), typeof(ControlMask), new PropertyMetadata(default(double)));

/// <summary>
/// 遮罩透明度
/// </summary>
public double MaskOpacity
{
get { return (double)GetValue(MaskOpacityProperty); }
set { SetValue(MaskOpacityProperty, value); }
}

/// <summary>
/// 父元素 (用于出发 IsMouseOver)
/// </summary>
public static readonly DependencyProperty ParentElementProperty = DependencyProperty.Register(
"ParentElement", typeof(UIElement), typeof(ControlMask), new PropertyMetadata(default(UIElement)));

/// <summary>
/// 父元素 (用于出发 IsMouseOver)
/// </summary>
public UIElement ParentElement
{
get { return (UIElement)GetValue(ParentElementProperty); }
set { SetValue(ParentElementProperty, value); }
}
}
}

+ 29
- 0
BPA.UIControl/Converters/BadgeOffsetConverter.cs View File

@@ -0,0 +1,29 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

namespace BPA.UIControl.Converters
{
/// <summary>
/// Badge 偏移计算转换器
/// </summary>
public class BadgeOffsetConverter : IMultiValueConverter
{
/// <inheritdoc/>
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var height = (double)values[0];
var width = (double)values[1];
double verOffset = (height / 2) * -1;
double horOffset = (width - (height / 2)) * -1;
return new Thickness(0, verOffset, horOffset, 0);
}

/// <inheritdoc/>
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
}

+ 64
- 0
BPA.UIControl/Converters/BooleanConverter.cs View File

@@ -0,0 +1,64 @@
using BPA.UIControl.Commons;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;

namespace BPA.UIControl.Converters
{
/// <summary>
/// bool 转换器
/// </summary>
/// <typeparam name="T"></typeparam>
public class BooleanConverter<T> : IValueConverter
{
/// <summary>
/// bool 转换器
/// </summary>
/// <param name="trueValue">true 对应值</param>
/// <param name="falseValue">false 对应值</param>
protected BooleanConverter(T trueValue, T falseValue)
{
TrueValue = trueValue;
FalseValue = falseValue;
}

/// <summary>
/// true 对应值
/// </summary>
public T TrueValue { get; set; }

/// <summary>
/// false 对应值
/// </summary>
public T FalseValue { get; set; }

/// <inheritdoc/>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return BooleanConverter<T>.ConvertBooleanToType(value, TrueValue, FalseValue);
}

/// <summary>
/// 转换 bool 为类型
/// </summary>
/// <param name="value">bool 值</param>
/// <param name="trueValue">true 时的值</param>
/// <param name="falseValue">false 时的值</param>
/// <returns>类型的值</returns>
internal static T ConvertBooleanToType(object value, T trueValue, T falseValue)
{
bool flag = ValidateArgument.NotNullOrEmptyCast<bool>(value, "value");
return flag ? trueValue : falseValue;
}

/// <inheritdoc/>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value is T flag && EqualityComparer<T>.Default.Equals(flag, TrueValue);
}
}
}

+ 18
- 0
BPA.UIControl/Converters/BooleanToBrushConverter.cs View File

@@ -0,0 +1,18 @@
using System.Windows.Media;

namespace BPA.UIControl.Converters
{
/// <summary>
/// bool to brush
/// </summary>
public class BooleanToBrushConverter : BooleanConverter<Brush>
{
/// <summary>
/// Initializes a new instance of the <see cref="BooleanToBrushConverter"/> class.
/// </summary>
public BooleanToBrushConverter()
: base(Brushes.Green, Brushes.Red)
{
}
}
}

+ 16
- 0
BPA.UIControl/Converters/BooleanToInverseConverter.cs View File

@@ -0,0 +1,16 @@
namespace BPA.UIControl.Converters
{
/// <summary>
/// bool - 取反
/// </summary>
public class BooleanToInverseConverter : BooleanConverter<bool>
{
/// <summary>
/// Initializes a new instance of the <see cref="BooleanToInverseConverter"/> class.
/// </summary>
public BooleanToInverseConverter()
: base(false, true)
{
}
}
}

+ 16
- 0
BPA.UIControl/Converters/BooleanToStringConverter.cs View File

@@ -0,0 +1,16 @@
namespace BPA.UIControl.Converters
{
/// <summary>
/// bool to string
/// </summary>
public class BooleanToStringConverter : BooleanConverter<string>
{
/// <summary>
/// Initializes a new instance of the <see cref="BooleanToStringConverter"/> class.
/// </summary>
public BooleanToStringConverter()
: base("true", "false")
{
}
}
}

+ 17
- 0
BPA.UIControl/Converters/BooleanToVisibilityConverter.cs View File

@@ -0,0 +1,17 @@
using System.Windows;

namespace BPA.UIControl.Converters
{
/// <summary>
/// bool - 显示状态转换器
/// </summary>
public class BooleanToVisibilityConverter : BooleanConverter<Visibility>
{
/// <summary>
/// Initializes a new instance of the <see cref="BooleanToVisibilityConverter"/> class.
/// </summary>
public BooleanToVisibilityConverter() : base(Visibility.Visible, Visibility.Collapsed)
{
}
}
}

+ 88
- 0
BPA.UIControl/Converters/CarouselScaleConvereter.cs View File

@@ -0,0 +1,88 @@
using System;
using System.Diagnostics;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;

namespace BPA.UIControl.Converters
{
/// <summary>
/// 轮播图水平尺度转换
/// </summary>
public class CarouselScaleConvereter : IMultiValueConverter
{
private static double HorizontalScale(object[] values, ScaleTransform scaleTransform)
{
if (!(values[0] is FlipViewItem item) || !(values[3] is double))
{
return scaleTransform.ScaleX;
}

var scrollViewer = item.TryGetParentFromVisualTree<ScrollViewer>();
if (scrollViewer == null || scrollViewer.Name != "PART_ContentScrollViewer")
{
return scaleTransform.ScaleX;
}

var point = new Point(-(scrollViewer.ViewportWidth - item.ActualWidth) / 2, 0);
var targetPosition = item.TransformToVisual(scrollViewer).Transform(point);
var different = Math.Abs(targetPosition.X);
if (different > scrollViewer.ViewportWidth)
{
return 1D;
}

var percent = 1 - different / scrollViewer.ViewportWidth * 2;
return 1 + percent;
}

private static double VerticalScale(object[] values, ScaleTransform scaleTransform)
{
if (!(values[0] is FlipViewItem item) || !(values[4] is double))
{
return scaleTransform.ScaleX;
}

var scrollViewer = item.TryGetParentFromVisualTree<ScrollViewer>();
if (scrollViewer == null || scrollViewer.Name != "PART_ContentScrollViewer")
{
return scaleTransform.ScaleX;
}

var point = new Point(0, -(scrollViewer.ViewportHeight - item.ActualHeight) / 2);
var targetPosition = item.TransformToVisual(scrollViewer).Transform(point);
var different = Math.Abs(targetPosition.Y);
if (different > scrollViewer.ViewportHeight)
{
return 1D;
}

var percent = 1 - different / scrollViewer.ViewportHeight * 2;
return 1 + percent;
}

/// <inheritdoc/>
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var scaleTransform = values[1] as ScaleTransform;
var orientation = (Orientation)values[2];

if (orientation == Orientation.Horizontal)
{
return HorizontalScale(values, scaleTransform);
}
else
{
return VerticalScale(values, scaleTransform);
}
}

/// <inheritdoc/>
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

+ 39
- 0
BPA.UIControl/Converters/CloneConverter.cs View File

@@ -0,0 +1,39 @@
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Markup;

namespace BPA.UIControl.Converters
{
/// <summary>
/// 克隆内容
/// </summary>
public class CloneConverter : IValueConverter
{
/// <inheritdoc/>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
{
return value;
}

try
{
string xaml = XamlWriter.Save(value);
var cloneValue = XamlReader.Parse(xaml);
return cloneValue;
}
catch (Exception)
{
return value;
}
}

/// <inheritdoc/>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

+ 26
- 0
BPA.UIControl/Converters/ComboBoxPopupWidthConverter.cs View File

@@ -0,0 +1,26 @@
using System;
using System.Globalization;
using System.Windows.Data;

namespace BPA.UIControl.Converters
{
/// <summary>
/// ComboBox 弹窗宽度转换
/// </summary>
public class ComboBoxPopupWidthConverter : IMultiValueConverter
{
/// <inheritdoc/>
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var width = (double)values[0];
var marginLeft = (double)values[1];
return width + (marginLeft * 2);
}

/// <inheritdoc/>
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
}

+ 30
- 0
BPA.UIControl/Converters/DataGridRowIndexConverter.cs View File

@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace BPA.UIControl.Converters
{
/// <summary>
/// to data grid row index
/// </summary>
public class DataGridRowIndexConverter : IValueConverter
{
/// <inheritdoc/>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value is DataGridRow row ? row.GetIndex() + 1 : 0;
}

/// <inheritdoc/>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return DependencyProperty.UnsetValue;
}
}
}

+ 28
- 0
BPA.UIControl/Converters/DoubleToGridLengthConverter.cs View File

@@ -0,0 +1,28 @@
using BPA.UIControl.Commons;
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

namespace BPA.UIControl.Converters
{
/// <summary>
/// double -> GridLength
/// </summary>
public class DoubleToGridLengthConverter : IValueConverter
{
/// <inheritdoc/>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var doubleValue = ValidateArgument.NotNullOrEmptyCast<double>(value, "value");
return new GridLength(doubleValue);
}

/// <inheritdoc/>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var gridLength = ValidateArgument.NotNullOrEmptyCast<GridLength>(value, "value");
return gridLength.Value;
}
}
}

+ 76
- 0
BPA.UIControl/Converters/DoubleToThicknessConverter.cs View File

@@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;

namespace BPA.UIControl.Converters
{
/// <summary>
/// double => Thickness
/// </summary>
public class DoubleToThicknessConverter : IValueConverter
{
/// <summary>
/// Thickness 模式
/// </summary>
public ThicknessMode Mode { get; set; }

/// <inheritdoc/>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is double thickness)
{
switch (Mode)
{
case ThicknessMode.All:
default:
return new Thickness(thickness);

case ThicknessMode.LeftAndRight:
return new Thickness(thickness, 0, thickness, 0);

case ThicknessMode.Left:
return new Thickness(thickness, 0, 0, 0);

case ThicknessMode.Right:
return new Thickness(0, 0, thickness, 0);

case ThicknessMode.TopAndBottom:
return new Thickness(0, thickness, 0, thickness);

case ThicknessMode.Top:
return new Thickness(0, thickness, 0, 0);

case ThicknessMode.Bottom:
return new Thickness(0, 0, 0, thickness);
}
}

return new Thickness(0);
}

/// <inheritdoc/>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

/// <summary>
/// Thickness 模式
/// </summary>
public enum ThicknessMode
{
All = 0,
LeftAndRight,
Left,
Right,
TopAndBottom,
Top,
Bottom,
}
}

+ 32
- 0
BPA.UIControl/Converters/DoublesToPointConverter.cs View File

@@ -0,0 +1,32 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

namespace BPA.UIControl.Converters
{
/// <summary>
/// 两个数 -> 点
/// </summary>
public class DoublesToPointConverter : IMultiValueConverter
{
/// <inheritdoc/>
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length < 2)
{
throw new ArgumentException("参数少于 2 个");
}

double x = System.Convert.ToDouble(values[0]);
double y = System.Convert.ToDouble(values[1]);
return new Point(x, y);
}

/// <inheritdoc/>
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
}

+ 33
- 0
BPA.UIControl/Converters/DoublesToSizeConverter.cs View File

@@ -0,0 +1,33 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

namespace BPA.UIControl.Converters
{
/// <summary>
/// 2 double -> Size
/// </summary>
public class DoublesToSizeConverter : IMultiValueConverter
{
/// <inheritdoc/>
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length < 2)
{
throw new ArgumentException("参数少于 2 个");
}

double width = System.Convert.ToDouble(values[0]);
double height = System.Convert.ToDouble(values[1]);

return new Size(width, height);
}

/// <inheritdoc/>
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
}

+ 30
- 0
BPA.UIControl/Converters/EnumGetDescriptionConverter.cs View File

@@ -0,0 +1,30 @@
using BPA.UIControl.Commons;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;

namespace BPA.UIControl.Converters
{
/// <summary>
/// 枚举获取说明
/// </summary>
public class EnumGetDescriptionConverter : IValueConverter
{
/// <inheritdoc/>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var enumValue = ValidateArgument.NotNullOrEmptyCast<Enum>(value, "value");
return enumValue.GetDescription();
}

/// <inheritdoc/>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
}

+ 49
- 0
BPA.UIControl/Converters/EnumToBooleanConverter.cs View File

@@ -0,0 +1,49 @@
using BPA.UIControl.Commons;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;

namespace BPA.UIControl.Converters
{
/// <summary>
/// enum -> bool
/// </summary>
public class EnumToBooleanConverter : IValueConverter
{
/// <inheritdoc/>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return EnumToBooleanConverter.ConvertEnumToBool(value, parameter);
}

/// <summary>
/// Converts the enum to bool.
/// </summary>
/// <param name="value">The value.</param>
/// <param name="parameter">The parameter.</param>
/// <returns>A bool.</returns>
internal static bool ConvertEnumToBool(object value, object parameter)
{
string text = ValidateArgument.NotNullOrEmptyCast<string>(parameter, "parameter");
ValidateArgument.NotNull(value, "value");
return text.Equals(value.ToString());
}

/// <inheritdoc/>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
bool flag = ValidateArgument.NotNullOrEmptyCast<bool>(value, "value");
string text = ValidateArgument.NotNullOrEmptyCast<string>(parameter, "paramater");
if (flag)
{
return Enum.Parse(targetType, text, false);
}

return null;
}
}
}

+ 24
- 0
BPA.UIControl/Converters/EnumToBooleanInverseConverter.cs View File

@@ -0,0 +1,24 @@
using System;
using System.Globalization;
using System.Windows.Data;

namespace BPA.UIControl.Converters
{
/// <summary>
/// enum -> inverse bool
/// </summary>
public class EnumToBooleanInverseConverter : IValueConverter
{
/// <inheritdoc/>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return !EnumToBooleanConverter.ConvertEnumToBool(value, parameter);
}

/// <inheritdoc/>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

+ 62
- 0
BPA.UIControl/Converters/EnumToVisibilityConverter.cs View File

@@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;

namespace BPA.UIControl.Converters
{
/// <summary>
/// enum => Visiblity
/// </summary>
public class EnumToVisibilityConverter : IValueConverter
{
/// <summary>
/// true 对应 Visibility
/// </summary>
public Visibility TrueVisibility { get; set; }

/// <summary>
/// false 对应 Visibility
/// </summary>
public Visibility FalseVisibility { get; set; }

/// <summary>
/// Initializes a new instance of the <see cref="EnumToVisibilityConverter"/> class.
/// </summary>
public EnumToVisibilityConverter()
{
TrueVisibility = Visibility.Visible;
FalseVisibility = Visibility.Collapsed;
}

/// <inheritdoc/>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return EnumToVisibilityConverter.ConvertEnumToVisibility(value, parameter, TrueVisibility, FalseVisibility);
}

/// <summary>
/// Converts the enum to visibility.
/// </summary>
/// <param name="value">The value.</param>
/// <param name="parameter">The parameter.</param>
/// <param name="trueVisibility">The true visibility.</param>
/// <param name="falseVisibility">The false visibility.</param>
/// <returns>An object.</returns>
public static object ConvertEnumToVisibility(object value, object parameter, Visibility trueVisibility, Visibility falseVisibility)
{
bool flag = EnumToBooleanConverter.ConvertEnumToBool(value, parameter);
return BooleanConverter<Visibility>.ConvertBooleanToType(flag, trueVisibility, falseVisibility);
}

/// <inheritdoc/>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

+ 41
- 0
BPA.UIControl/Converters/GetArcPointConverter.cs View File

@@ -0,0 +1,41 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

namespace BPA.UIControl.Converters
{
/// <summary>
/// 4 个 double 计算 Arc 点
/// </summary>
public class GetArcPointConverter : IMultiValueConverter
{
/// <inheritdoc/>
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length < 4)
{
throw new ArgumentException("参数少于 4 个");
}

double value = System.Convert.ToDouble(values[0]);
double min = System.Convert.ToDouble(values[1]);
double max = System.Convert.ToDouble(values[2]);
double radius = System.Convert.ToDouble(values[3]) / 2;

double percent = (value - min) / (max - min) >= 1 ? 0.9999 : value / (max - min);
double angle = percent * 360;

double pointX = Math.Sin(angle * Math.PI / 180) * radius + radius;
double pointY = (Math.Cos(angle * Math.PI / 180) * radius - radius) * -1;

return new Point(pointX, pointY);
}

/// <inheritdoc/>
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
}

+ 28
- 0
BPA.UIControl/Converters/GetCanvasCentreConverter.cs View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;

namespace BPA.UIControl.Converters
{
/// <summary>
/// 获取 Canvas 中心点
/// </summary>
public class GetCanvasCentreConverter : IMultiValueConverter
{
/// <inheritdoc/>
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return ((double)values[0] - (double)values[1]) / 2;
}

/// <inheritdoc/>
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
}

+ 46
- 0
BPA.UIControl/Converters/GetDataGridColumnCheckedContentConverter.cs View File

@@ -0,0 +1,46 @@
using BPA.UIControl.Commons;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;

namespace BPA.UIControl.Converters
{
/// <summary>
/// 获取 DataGridColumn 的 CheckedContent 属性
/// </summary>
public class GetDataGridColumnCheckedContentConverter : IMultiValueConverter
{
/// <inheritdoc/>
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length < 2)
{
throw new ArgumentException("the values length can't be less the 2.");
}

var dataGrid = ValidateArgument.NotNullOrEmptyCast<DataGrid>(values[0], "values[0]");
var columnHeader = ValidateArgument.NotNullOrEmptyCast<DataGridColumnHeader>(values[1], "values[1]");
foreach (var column in dataGrid.Columns)
{
if (columnHeader.Column == column)
{
return ToggleButtonHelper.GetCheckedContent(column) ?? column.Header;
}
}

return null;
}

/// <inheritdoc/>
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
}

+ 34
- 0
BPA.UIControl/Converters/GetPercentConverter.cs View File

@@ -0,0 +1,34 @@
using System;
using System.Globalization;
using System.Windows.Data;

namespace BPA.UIControl.Converters
{
/// <summary>
/// 获取百分比
/// </summary>
public class GetPercentConverter : IMultiValueConverter
{
/// <inheritdoc/>
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length < 3)
{
throw new ArgumentException("参数少于 3 个");
}

double value = System.Convert.ToDouble(values[0]);
double min = System.Convert.ToDouble(values[1]);
double max = System.Convert.ToDouble(values[2]);
double percent = (value - min) / (max - min);

return $"{System.Convert.ToInt32(percent * 100)}%";
}

/// <inheritdoc/>
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
}

+ 24
- 0
BPA.UIControl/Converters/GetWidthConverter.cs View File

@@ -0,0 +1,24 @@
using System;
using System.Globalization;
using System.Windows.Data;

namespace BPA.UIControl.Converters
{
/// <summary>
/// The get width converter.
/// </summary>
public class GetWidthConverter : IMultiValueConverter
{
/// <inheritdoc/>
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return (double)values[0] - (double)values[1] * 2;
}

/// <inheritdoc/>
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
}

+ 25
- 0
BPA.UIControl/Converters/HalfOfDoubleConverter.cs View File

@@ -0,0 +1,25 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

namespace BPA.UIControl.Converters
{
/// <summary>
/// double 值一半转换器
/// </summary>
public class HalfOfDoubleConverter : IValueConverter
{
/// <inheritdoc/>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value is double d ? d / 2 : DependencyProperty.UnsetValue;
}

/// <inheritdoc/>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return DependencyProperty.UnsetValue;
}
}
}

+ 83
- 0
BPA.UIControl/Converters/InsideCornerRadiusConverter.cs View File

@@ -0,0 +1,83 @@
using BPA.UIControl.Commons;
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

namespace BPA.UIControl.Converters
{
/// <summary>
/// 内圆角半径转换
/// </summary>
public class InsideCornerRadiusConverter : IMultiValueConverter
{
/// <summary>
/// 内圆角半径类型
/// </summary>
public enum InsideCornerRadiusType
{
/// <summary>
/// 全部方向
/// </summary>
All = 0,

/// <summary>
/// 上方向
/// </summary>
Top,

/// <summary>
/// 下方向
/// </summary>
Bottom,

/// <summary>
/// 左方向
/// </summary>
Left,

/// <summary>
/// 右方向
/// </summary>
Right,
}

/// <summary>
/// 内圆角半径类型
/// </summary>
public InsideCornerRadiusType Type { get; set; }

/// <inheritdoc/>
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length < 2)
{
throw new ArgumentException("values length not less then 2.");
}

var thickness = ValidateArgument.NotNullOrEmptyCast<Thickness>(values[0], "values[0]");
var cornerRadius = ValidateArgument.NotNullOrEmptyCast<CornerRadius>(values[1], "values[1]");
var insideCornerRadius = cornerRadius.TopLeft - (thickness.Left / 2);
if (insideCornerRadius < 0)
{
insideCornerRadius = 0;
}

return Type switch
{
InsideCornerRadiusType.All => new CornerRadius(insideCornerRadius),
InsideCornerRadiusType.Top => new CornerRadius(insideCornerRadius, insideCornerRadius, 0, 0),
InsideCornerRadiusType.Bottom => new CornerRadius(0, 0, insideCornerRadius, insideCornerRadius),
InsideCornerRadiusType.Left => new CornerRadius(insideCornerRadius, 0, insideCornerRadius, 0),
InsideCornerRadiusType.Right => new CornerRadius(0, insideCornerRadius, 0, insideCornerRadius),
_ => new CornerRadius(insideCornerRadius),
};
}

/// <inheritdoc/>
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

+ 24
- 0
BPA.UIControl/Converters/IsEqualConverter.cs View File

@@ -0,0 +1,24 @@
using System;
using System.Globalization;
using System.Windows.Data;

namespace BPA.UIControl.Converters
{
/// <summary>
/// 是否相等
/// </summary>
public class IsEqualConverter : IMultiValueConverter
{
/// <inheritdoc/>
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values[0].Equals(values[1]);
}

/// <inheritdoc/>
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
}

+ 36
- 0
BPA.UIControl/Converters/IsLargeArcConverter.cs View File

@@ -0,0 +1,36 @@
using System;
using System.Globalization;
using System.Windows.Data;

namespace BPA.UIControl.Converters
{
/// <summary>
/// 是否为大弧形
/// </summary>
public class IsLargeArcConverter : IMultiValueConverter
{
/// <inheritdoc/>
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length < 3)
{
throw new ArgumentException("参数少于 3 个");
}

double value = System.Convert.ToDouble(values[0]);
double min = System.Convert.ToDouble(values[1]);
double max = System.Convert.ToDouble(values[2]);

double percent = value / (max - min) > 1 ? 1 : value / (max - min);
double angle = percent * 360;

return angle > 180;
}

/// <inheritdoc/>
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
}

+ 25
- 0
BPA.UIControl/Converters/IsNullOrEmptyConverter.cs View File

@@ -0,0 +1,25 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

namespace BPA.UIControl.Converters
{
/// <summary>
/// 是否为 null 或空字符串
/// </summary>
public class IsNullOrEmptyConverter : IValueConverter
{
/// <inheritdoc/>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value == null || string.IsNullOrEmpty(value.ToString());
}

/// <inheritdoc/>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return DependencyProperty.UnsetValue;
}
}
}

+ 41
- 0
BPA.UIControl/Converters/ListViewGridViewConverter.cs View File

@@ -0,0 +1,41 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace BPA.UIControl.Converters
{
/// <summary>
/// ListView 的 GridView Style 选择转换器
/// </summary>
public class ListViewGridViewConverter : IValueConverter
{
/// <summary>
/// 默认 ListView 样式
/// </summary>
public Style DefaultStyle { get; set; }

/// <summary>
/// GridView 样式
/// </summary>
public Style ViewStyle { get; set; }

/// <inheritdoc/>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is ListView listView)
{
return listView.View != null ? ViewStyle : DefaultStyle;
}

return value is ViewBase ? ViewStyle : DefaultStyle;
}

/// <inheritdoc/>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
}

+ 51
- 0
BPA.UIControl/Converters/MathConverter.cs View File

@@ -0,0 +1,51 @@
using BPA.UIControl.Commons;
using System;
using System.Globalization;
using System.Windows.Data;
using static BPA.UIControl.Converters.MathMultipleConverter;

namespace BPA.UIControl.Converters
{
/// <summary>
/// 数学计算转换器
/// </summary>
public class MathConverter : IValueConverter
{
/// <summary>
/// 计算操作类型
/// </summary>
public MathOperation Operation { get; set; }

/// <inheritdoc/>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
ValidateArgument.NotNull(value, nameof(value));
ValidateArgument.NotNull(parameter, nameof(parameter));

if (!double.TryParse(value.ToString(), out double num1) || !double.TryParse(parameter.ToString(), out double num2))
return 0;

switch (Operation)
{
case MathOperation.Add:
default:
return num1 + num2;

case MathOperation.Divide:
return num1 / num2;

case MathOperation.Multiply:
return num1 * num2;

case MathOperation.Subtract:
return num1 - num2;
}
}

/// <inheritdoc/>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

+ 78
- 0
BPA.UIControl/Converters/MathMultipleConverter.cs View File

@@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;

namespace BPA.UIControl.Converters
{
/// <summary>
/// 数学计算多转换器
/// </summary>
public class MathMultipleConverter : IMultiValueConverter
{
/// <summary>
/// 计算操作类型
/// </summary>
public enum MathOperation
{
/// <summary>
/// 加
/// </summary>
Add,

/// <summary>
/// 减
/// </summary>
Subtract,

/// <summary>
/// 乘
/// </summary>
Multiply,

/// <summary>
/// 除
/// </summary>
Divide
}

/// <summary>
/// 计算操作类型
/// </summary>
public MathOperation Operation { get; set; }

/// <inheritdoc/>
public object Convert(object[] value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null || value.Length < 2 || value[0] == null || value[1] == null) return Binding.DoNothing;

if (!double.TryParse(value[0].ToString(), out double value1) || !double.TryParse(value[1].ToString(), out double value2))
return 0;

switch (Operation)
{
case MathOperation.Add:
default:
return value1 + value2;

case MathOperation.Divide:
return value1 / value2;

case MathOperation.Multiply:
return value1 * value2;

case MathOperation.Subtract:
return value1 - value2;
}
}

/// <inheritdoc/>
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

+ 25
- 0
BPA.UIControl/Converters/NotNullConverter.cs View File

@@ -0,0 +1,25 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

namespace BPA.UIControl.Converters
{
/// <summary>
/// 是否不为 null 转换器
/// </summary>
public class NotNullConverter : IValueConverter
{
/// <inheritdoc/>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value != null;
}

/// <inheritdoc/>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return DependencyProperty.UnsetValue;
}
}
}

+ 25
- 0
BPA.UIControl/Converters/NotNullOrEmptyConverter.cs View File

@@ -0,0 +1,25 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

namespace BPA.UIControl.Converters
{
/// <summary>
/// string 不为 null 或者 empty
/// </summary>
public class NotNullOrEmptyConverter : IValueConverter
{
/// <inheritdoc/>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value != null && !string.IsNullOrEmpty(value.ToString());
}

/// <inheritdoc/>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return DependencyProperty.UnsetValue;
}
}
}

+ 41
- 0
BPA.UIControl/Converters/NullToVisibilityConverter.cs View File

@@ -0,0 +1,41 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

namespace BPA.UIControl.Converters
{
/// <summary>
/// null -> Visiblity
/// </summary>
public class NullToVisibilityConverter : IValueConverter
{
/// <summary>
/// null 对应 Visibility
/// </summary>
public Visibility NullVisibility { get; set; }

/// <summary>
/// 非 null 对应 Visibility
/// </summary>
public Visibility NotNullVisibility { get; set; }

public NullToVisibilityConverter()
{
NullVisibility = Visibility.Collapsed;
NotNullVisibility = Visibility.Visible;
}

/// <inheritdoc/>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value is null ? NullVisibility : NotNullVisibility;
}

/// <inheritdoc/>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

+ 37
- 0
BPA.UIControl/Converters/VisibilityToBooleanConverter.cs View File

@@ -0,0 +1,37 @@
using BPA.UIControl.Commons;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;

namespace BPA.UIControl.Converters
{
/// <summary>
/// Visibility -> bool
/// </summary>
public class VisibilityToBooleanConverter : IValueConverter
{
/// <inheritdoc/>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ConvertVisibilityToBool(value);
}

internal static bool ConvertVisibilityToBool(object value)
{
Visibility visibility = ValidateArgument.NotNullOrEmptyCast<Visibility>(value, "value");
return visibility == Visibility.Visible;
}

/// <inheritdoc/>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
bool flag = ValidateArgument.NotNullOrEmptyCast<bool>(value, "value");
return flag ? Visibility.Visible : Visibility.Collapsed;
}
}
}

+ 27
- 0
BPA.UIControl/DataAnnotations/ColumnWidthAttribute.cs View File

@@ -0,0 +1,27 @@
using System;
using System.Windows.Controls;

namespace BPA.UIControl.DataAnnotations
{
/// <summary>
/// DataGrid 列宽
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class ColumnWidthAttribute : Attribute
{
/// <summary>
/// 列宽
/// </summary>
public DataGridLength Width { get; }

/// <summary>
/// DataGrid 列宽
/// </summary>
/// <param name="width">列宽</param>
public ColumnWidthAttribute(string width)
{
var converter = new DataGridLengthConverter();
Width = (DataGridLength)converter.ConvertFromString(width);
}
}
}

+ 138
- 0
BPA.UIControl/DataGridDetailToggleButtonColumn.cs View File

@@ -0,0 +1,138 @@
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;

namespace BPA.UIControl
{
/// <summary>
/// DataGrid 详细内容展开列
/// </summary>
public class DataGridDetailToggleButtonColumn : DataGridBoundColumn
{
/// <summary>
/// Initializes a new instance of the <see cref="DataGridDetailToggleButtonColumn"/> class.
/// </summary>
static DataGridDetailToggleButtonColumn()
{
var elementStyle = (Style)Application.Current.FindResource("RubyerDataGridDetailToggleButtonColumn");
DataGridBoundColumn.ElementStyleProperty.OverrideMetadata(typeof(DataGridDetailToggleButtonColumn), new FrameworkPropertyMetadata(elementStyle));
DataGridBoundColumn.EditingElementStyleProperty.OverrideMetadata(typeof(DataGridDetailToggleButtonColumn), new FrameworkPropertyMetadata(elementStyle));
}

/// <inheritdoc/>
protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
{
return GenerateToggleButton(isEditing: false, cell);
}

/// <inheritdoc/>
protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
{
return GenerateToggleButton(isEditing: true, cell);
}

/// <summary>
/// Applies the binding.
/// </summary>
/// <param name="target">The target.</param>
/// <param name="property">The property.</param>
internal void ApplyBinding(DependencyObject target, DependencyProperty property)
{
BindingBase binding = Binding;
if (binding != null)
{
BindingOperations.SetBinding(target, property, binding);
}
else
{
BindingOperations.ClearBinding(target, property);
}
}

/// <summary>
/// Applies the style.
/// </summary>
/// <param name="isEditing">If true, is editing.</param>
/// <param name="defaultToElementStyle">If true, default to element style.</param>
/// <param name="element">The element.</param>
internal void ApplyStyle(bool isEditing, bool defaultToElementStyle, FrameworkElement element)
{
Style style = PickStyle(isEditing, defaultToElementStyle);
if (style != null)
{
element.Style = style;
}
}

/// <summary>
/// Picks the style.
/// </summary>
/// <param name="isEditing">If true, is editing.</param>
/// <param name="defaultToElementStyle">If true, default to element style.</param>
/// <returns>A Style.</returns>
private Style PickStyle(bool isEditing, bool defaultToElementStyle)
{
Style style = (isEditing ? EditingElementStyle : ElementStyle);
if (isEditing && defaultToElementStyle && style == null)
{
style = ElementStyle;
}

return style;
}

/// <summary>
/// Generates the toggle button.
/// </summary>
/// <param name="isEditing">If true, is editing.</param>
/// <param name="cell">The cell.</param>
/// <returns>A ToggleButton.</returns>
private ToggleButton GenerateToggleButton(bool isEditing, DataGridCell cell)
{
ToggleButton toggleButton = (cell != null) ? (cell.Content as ToggleButton) : null;
toggleButton ??= new ToggleButton();
toggleButton.IsThreeState = false;
ApplyStyle(isEditing, defaultToElementStyle: true, toggleButton);
ApplyBinding(toggleButton, ToggleButton.IsCheckedProperty);
return toggleButton;
}

/// <inheritdoc/>
protected override object PrepareCellForEdit(FrameworkElement editingElement, RoutedEventArgs editingEventArgs)
{
if (editingElement is ToggleButton toggleButton)
{
toggleButton.Focus();
bool? isChecked = toggleButton.IsChecked;
if (IsMouseLeftButtonDown(editingEventArgs))
{
toggleButton.IsChecked = !isChecked.GetValueOrDefault();
var dataGridCell = editingElement.GetVisualAncestry().OfType<DataGridCell>().FirstOrDefault();
dataGridCell.IsEditing = false;
}

return isChecked;
}

return false;
}

/// <summary>
/// Are the mouse left button down.
/// </summary>
/// <param name="e">The e.</param>
/// <returns>A bool.</returns>
private static bool IsMouseLeftButtonDown(RoutedEventArgs e)
{
if (e is MouseButtonEventArgs mouseButtonEventArgs && mouseButtonEventArgs.ChangedButton == MouseButton.Left)
{
return mouseButtonEventArgs.ButtonState == MouseButtonState.Pressed;
}

return false;
}
}
}

+ 188
- 0
BPA.UIControl/DataGridSelectCheckBoxColumn.cs View File

@@ -0,0 +1,188 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Media;

namespace BPA.UIControl
{
/// <summary>
/// DataGrid 选择列
/// </summary>
public class DataGridSelectCheckBoxColumn : DataGridCheckBoxColumn
{
/// <summary>
/// Initializes a new instance of the <see cref="DataGridSelectCheckBoxColumn"/> class.
/// </summary>
static DataGridSelectCheckBoxColumn()
{
}

/// <summary>
/// Initializes a new instance of the <see cref="DataGridSelectCheckBoxColumn"/> class.
/// </summary>
public DataGridSelectCheckBoxColumn()
{
var headStyle = (Style)Application.Current.FindResource("DataGridSelectCheckBoxColumnHeader");
HeaderStyle = headStyle;
CanUserSort = false;
}

/// <inheritdoc/>
protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
{
return GenerateCheckBox(false, cell);
}

/// <inheritdoc/>
protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
{
return GenerateCheckBox(true, cell);
}

private CheckBox GenerateCheckBox(bool isEditing, DataGridCell cell)
{
CheckBox checkBox = (cell != null) ? (cell.Content as CheckBox) : null;
if (checkBox == null)
{
checkBox = new CheckBox();

if (!isEditing)
{
checkBox.Loaded += CheckBox_Checked;
checkBox.Checked += CheckBox_Checked;
checkBox.Unchecked += CheckBox_Checked;
}
}

checkBox.IsThreeState = IsThreeState;
ApplyStyle(isEditing, defaultToElementStyle: true, checkBox);
ApplyBinding(checkBox, ToggleButton.IsCheckedProperty);
return checkBox;
}

private void CheckBox_Checked(object sender, RoutedEventArgs e)
{
if (sender is CheckBox checkBox)
{
var dataGrid = checkBox.TryGetParentFromVisualTree<DataGrid>();
var columnHeader = GetHeader(this, dataGrid);
var headerCheckBox = columnHeader.TryGetChildFromVisualTree<CheckBox>(x => x is CheckBox);
if (headerCheckBox is null)
{
return;
}

var allValues = new List<bool?>();
foreach (var item in dataGrid.Items)
{
var row = dataGrid.ItemContainerGenerator.ContainerFromItem(item) as DataGridRow;
var cellsPresenter = row.TryGetChildFromVisualTree<DataGridCellsPresenter>(x => x is DataGridCellsPresenter);
if (cellsPresenter is null)
{
continue;
}

var cell = cellsPresenter.ItemContainerGenerator.ContainerFromIndex(DisplayIndex) as DataGridCell;
var currentCheckBox = cell.TryGetChildFromVisualTree<CheckBox>(x => x is CheckBox);
allValues.Add(currentCheckBox.IsChecked);
}

if (allValues.All(x => x == true))
{
headerCheckBox.IsChecked = true;
}
else if (allValues.All(x => x == false))
{
headerCheckBox.IsChecked = false;
}
else
{
headerCheckBox.IsChecked = null;
}
}
}

/// <inheritdoc/>
protected override bool CommitCellEdit(FrameworkElement editingElement)
{
return base.CommitCellEdit(editingElement);
}

private DataGridColumnHeader GetHeader(DataGridColumn column, DependencyObject reference)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(reference); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(reference, i);
if ((child is DataGridColumnHeader colHeader) && (colHeader.Column == column))
{
return colHeader;
}

colHeader = GetHeader(column, child);
if (colHeader != null)
{
return colHeader;
}
}

return null;
}

/// <summary>
/// Applies the binding.
/// </summary>
/// <param name="target">The target.</param>
/// <param name="property">The property.</param>
internal void ApplyBinding(DependencyObject target, DependencyProperty property)
{
BindingBase binding = Binding;
if (binding != null)
{
BindingOperations.SetBinding(target, property, binding);
}
else
{
BindingOperations.ClearBinding(target, property);
}
}

/// <summary>
/// Applies the style.
/// </summary>
/// <param name="isEditing">If true, is editing.</param>
/// <param name="defaultToElementStyle">If true, default to element style.</param>
/// <param name="element">The element.</param>
internal void ApplyStyle(bool isEditing, bool defaultToElementStyle, FrameworkElement element)
{
Style style = PickStyle(isEditing, defaultToElementStyle);
if (style != null)
{
element.Style = style;
}
}

/// <summary>
/// Picks the style.
/// </summary>
/// <param name="isEditing">If true, is editing.</param>
/// <param name="defaultToElementStyle">If true, default to element style.</param>
/// <returns>A Style.</returns>
private Style PickStyle(bool isEditing, bool defaultToElementStyle)
{
Style style = (isEditing ? EditingElementStyle : ElementStyle);
if (isEditing && defaultToElementStyle && style == null)
{
style = ElementStyle;
}

return style;
}
}
}

+ 359
- 0
BPA.UIControl/DateTimePicker.cs View File

@@ -0,0 +1,359 @@
using BPA.UIControl.Commons.KnownBoxes;
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Threading;

namespace BPA.UIControl
{
/// <summary>
/// 日期时间选择器
/// </summary>
[TemplatePart(Name = TextBoxPartName, Type = typeof(TextBox))]
[TemplatePart(Name = CurrentTextBoxPartName, Type = typeof(TextBox))]
[TemplatePart(Name = PopupPartName, Type = typeof(Popup))]
[TemplatePart(Name = ButtonPartName, Type = typeof(Button))]
[TemplatePart(Name = ClockPartName, Type = typeof(Clock))]
[TemplatePart(Name = CalendarPartName, Type = typeof(Calendar))]
[TemplatePart(Name = ConfirmButtonPartName, Type = typeof(Button))]
public class DateTimePicker : Control
{
const string TextBoxPartName = "PART_TextBox";
const string PopupPartName = "PART_Popup";
const string ButtonPartName = "PART_Button";
const string ClockPartName = "PART_Clock";
const string CalendarPartName = "PART_Calendar";
const string ConfirmButtonPartName = "PART_ConfirmButton";
const string CurrentTextBoxPartName = "PART_CurrentTextBox";

private TextBox _textBox;
private Popup _popup;
private Clock _clock;
private Calendar _calendar;
private Button _confirmButton;

private bool isInternalSetting = true;
private DateTime currentDateTime;

/// <summary>
/// Initializes a new instance of the <see cref="DateTimePicker"/> class.
/// </summary>
static DateTimePicker()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(DateTimePicker), new FrameworkPropertyMetadata(typeof(DateTimePicker)));
}

/// <inheritdoc/>
public override void OnApplyTemplate()
{
base.OnApplyTemplate();

Clock clock = GetTemplateChild(ClockPartName) as Clock;
clock.CurrentTimeChanged += Clock_SelectedTimeChanged;
this._clock = clock;

Calendar calendar = GetTemplateChild(CalendarPartName) as Calendar;
calendar.SelectedDatesChanged += Calendar_SelectedDatesChanged;
this._calendar = calendar;

TextBox textBox = GetTemplateChild(TextBoxPartName) as TextBox;
Binding binding1 = new Binding("Text");
binding1.Source = this;
binding1.Mode = BindingMode.TwoWay;
textBox.SetBinding(TextBox.TextProperty, binding1);
_textBox = textBox;

Popup popup = GetTemplateChild(PopupPartName) as Popup;
popup.Closed += Popup_Closed;
popup.Opened += Popup_Opened;
if (IsDropDownOpen)
{
_popup.IsOpen = true;
}
_popup = popup;

Button button = GetTemplateChild(ButtonPartName) as Button;
button.Click += Button_Click;

Button confirmButton = GetTemplateChild(ConfirmButtonPartName) as Button;
confirmButton.Click += ConfirmButton_Click;
_confirmButton = confirmButton;

currentDateTime = SelectedDateTime == null ? DateTime.Now : SelectedDateTime.Value;
}

#region 路由事件

/// <summary>
/// 选择时间改变事件
/// </summary>
public static readonly RoutedEvent SelectedTimeChangedEvent =
EventManager.RegisterRoutedEvent("SelectedTimeChanged", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler<DateTime?>), typeof(DateTimePicker));

/// <summary>
/// 选择时间改变事件处理
/// </summary>
public event RoutedPropertyChangedEventHandler<DateTime?> SelectedTimeChanged
{
add { AddHandler(SelectedTimeChangedEvent, value); }
remove { RemoveHandler(SelectedTimeChangedEvent, value); }
}

#endregion 路由事件

#region 依赖属性

/// <summary>
/// 选择的日期时间
/// </summary>
public static readonly DependencyProperty SelectedDateTimeProperty = DependencyProperty.Register(
"SelectedDateTime", typeof(DateTime?), typeof(DateTimePicker), new FrameworkPropertyMetadata(default(DateTime?), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedTimeChanged));

/// <summary>
/// 选择的日期时间
/// </summary>
public DateTime? SelectedDateTime
{
get { return (DateTime?)GetValue(SelectedDateTimeProperty); }
set { SetValue(SelectedDateTimeProperty, value); }
}

/// <summary>
/// 是否下拉打开
/// </summary>
public static readonly DependencyProperty IsDropDownOpenProperty = DependencyProperty.Register(
"IsDropDownOpen", typeof(bool), typeof(DateTimePicker), new PropertyMetadata(BooleanBoxes.FalseBox, OnIsDropDownOpenChanged));

/// <summary>
/// 是否下拉打开
/// </summary>
public bool IsDropDownOpen
{
get { return (bool)GetValue(IsDropDownOpenProperty); }
set { SetValue(IsDropDownOpenProperty, BooleanBoxes.Box(value)); }
}

/// <summary>
/// Ons the is drop down open changed.
/// </summary>
/// <param name="d">The d.</param>
/// <param name="e">The e.</param>
private static void OnIsDropDownOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DateTimePicker tp = d as DateTimePicker;
bool newValue = (bool)e.NewValue;
if (tp._popup != null && tp._popup.IsOpen != newValue)
{
tp._popup.IsOpen = newValue;
if (newValue)
{
tp.Dispatcher.BeginInvoke(DispatcherPriority.Input, (Action)delegate ()
{
tp._clock.Focus();
});
}
}
}

/// <summary>
/// 显示文本
/// </summary>
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text", typeof(string), typeof(DateTimePicker), new PropertyMetadata(null, OnTextChanged));

/// <summary>
/// 显示文本
/// </summary>
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}

/// <summary>
/// 时间文本改变
/// </summary>
private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DateTimePicker dateTimePicker = (DateTimePicker)d;

var oldDateTime = dateTimePicker.SelectedDateTime?.ToString(dateTimePicker.SelectedDateTimeFormat);
if (oldDateTime != dateTimePicker.Text)
{
if (DateTime.TryParse(dateTimePicker.Text, out DateTime result))
{
dateTimePicker.SelectedDateTime = result;
dateTimePicker.currentDateTime = result;
}
else if (string.IsNullOrEmpty(dateTimePicker.Text))
{
dateTimePicker.SelectedDateTime = null;
}
else
{
dateTimePicker.Text = oldDateTime;
}
}
}

/// <summary>
/// 选择时间格式化
/// </summary>
public static readonly DependencyProperty SelectedDateTimeFormatProperty = DependencyProperty.Register(
"SelectedDateTimeFormat", typeof(string), typeof(DateTimePicker), new PropertyMetadata(default(string), OnSelectedDateTimeFormatChanged));

private static void OnSelectedDateTimeFormatChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is DateTimePicker picker)
{
picker.Text = picker.SelectedDateTime?.ToString(picker.SelectedDateTimeFormat);
}
}

/// <summary>
/// 选择时间格式化
/// </summary>
public string SelectedDateTimeFormat
{
get { return (string)GetValue(SelectedDateTimeFormatProperty); }
set { SetValue(SelectedDateTimeFormatProperty, value); }
}

#endregion 依赖属性

#region 方法

/// <summary>
/// 文本鼠标点下
/// </summary>
private void TextBox_MouseDown(object sender, MouseButtonEventArgs e)
{
TextBox textBox = sender as TextBox;
textBox.Focus();
}

/// <summary>
/// 选择时间改变
/// </summary>
private static void OnSelectedTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is DateTimePicker dateTimePicker)
{
dateTimePicker.Text = dateTimePicker.SelectedDateTime?.ToString(dateTimePicker.SelectedDateTimeFormat);

var args = new RoutedPropertyChangedEventArgs<DateTime?>((DateTime?)e.OldValue, (DateTime?)e.NewValue);
args.RoutedEvent = DateTimePicker.SelectedTimeChangedEvent;
dateTimePicker.RaiseEvent(args);

dateTimePicker.currentDateTime = dateTimePicker.SelectedDateTime == null ? DateTime.Now : dateTimePicker.SelectedDateTime.Value;

dateTimePicker._textBox?.Focus();
dateTimePicker._textBox?.SelectAll();
}
}

/// <summary>
/// 时钟的时间改变
/// </summary>
private void Clock_SelectedTimeChanged(object sender, RoutedPropertyChangedEventArgs<DateTime?> e)
{
if (isInternalSetting)
{
return;
}

if (SelectedDateTime != null)
{
DateTime dateTime = (DateTime)SelectedDateTime;
DateTime newDate = (DateTime)e.NewValue;
currentDateTime = new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, newDate.Hour, newDate.Minute, newDate.Second);
}
else
{
currentDateTime = e.NewValue.Value;
}

}

/// <summary>
/// 日期改变
/// </summary>
private void Calendar_SelectedDatesChanged(object sender, SelectionChangedEventArgs e)
{
if (isInternalSetting)
{
return;
}

if (SelectedDateTime != null)
{
DateTime dateTime = (DateTime)SelectedDateTime;
DateTime newTime = (DateTime)_calendar.SelectedDate;
currentDateTime = new DateTime(newTime.Year, newTime.Month, newTime.Day, dateTime.Hour, dateTime.Minute, dateTime.Second);
}
else
{
var time = _clock.CurrentTime == null ? TimeSpan.Zero : _clock.CurrentTime.Value.TimeOfDay;
currentDateTime = (DateTime)_calendar.SelectedDate + time;
}

Mouse.Capture(null);
}

/// <summary>
/// 点击时间按钮
/// </summary>
private void Button_Click(object sender, RoutedEventArgs e)
{
Text = _textBox.Text;
IsDropDownOpen = !IsDropDownOpen;
}

/// <summary>
/// popup 打开后
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Popup_Opened(object sender, EventArgs e)
{
isInternalSetting = true;
_calendar.SelectedDate = currentDateTime.Date;
_calendar.DisplayDate = currentDateTime.Date;
_clock.SelectedTime = currentDateTime;
isInternalSetting = false;
}

/// <summary>
/// popup 关闭
/// </summary>
private void Popup_Closed(object sender, EventArgs e)
{
if (_confirmButton.IsKeyboardFocusWithin)
{
_textBox.Focus();
}
else
{
IsDropDownOpen = false;
}
}

/// <summary>
/// 确定
/// </summary>
private void ConfirmButton_Click(object sender, RoutedEventArgs e)
{
SelectedDateTime = currentDateTime;
IsDropDownOpen = false;

var args = new RoutedPropertyChangedEventArgs<DateTime?>(SelectedDateTime, SelectedDateTime);
args.RoutedEvent = DateTimePicker.SelectedTimeChangedEvent;
RaiseEvent(args);
}

#endregion 方法
}
}

BIN
View File


BIN
View File


BIN
View File


BIN
View File


BIN
View File


BIN
View File


BIN
View File


+ 7230
- 0
BPA.UIControl/Demo/Rubyer.xml
File diff suppressed because it is too large
View File


BIN
View File


+ 32
- 0
BPA.UIControl/Demo/RubyerDemo.exe.config View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.Bcl.AsyncInterfaces" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.ComponentModel.Annotations" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.1.0" newVersion="4.2.1.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

BIN
View File


BIN
View File


BIN
View File


BIN
View File


BIN
View File


BIN
View File


BIN
View File


BIN
View File


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save