Merge pull request #32 from xincheng213618/main

Update net3.5->net4.8  && Add NET8.0 WPFdemo
This commit is contained in:
DebugST 2025-05-15 14:05:49 +08:00 committed by GitHub
commit a296731d3e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 2331 additions and 48 deletions

368
.gitignore vendored
View File

@ -1,4 +1,364 @@
.DS_Store ## Ignore Visual Studio temporary files, build results, and
/Bin ## files generated by popular Visual Studio add-ons.
bin/ ##
obj/ ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Oo]ut/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
.idea/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
@ -10,8 +10,9 @@
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ST.Library.UI</RootNamespace> <RootNamespace>ST.Library.UI</RootNamespace>
<AssemblyName>ST.Library.UI</AssemblyName> <AssemblyName>ST.Library.UI</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
@ -22,6 +23,7 @@
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
@ -32,6 +34,7 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Release\ST.Library.UI.XML</DocumentationFile> <DocumentationFile>bin\Release\ST.Library.UI.XML</DocumentationFile>
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <Reference Include="System" />

View File

@ -1,29 +1,28 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// <auto-generated> // <auto-generated>
// This code was generated by a tool. // 此代码由工具生成。
// Runtime Version:4.0.30319.42000 // 运行时版本:4.0.30319.42000
// //
// Changes to this file may cause incorrect behavior and will be lost if // 对此文件的更改可能会导致不正确的行为,并且如果
// the code is regenerated. // 重新生成代码,这些更改将会丢失。
// </auto-generated> // </auto-generated>
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
namespace WinNodeEditorDemo.Properties namespace WinNodeEditorDemo.Properties {
{ using System;
/// <summary> /// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc. /// 一个强类型的资源类,用于查找本地化的字符串等。
/// </summary> /// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder // 此类是由 StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio. // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
// To add or remove a member, edit your .ResX file then rerun ResGen // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
// with the /str option, or rebuild your VS project. // (以 /str 作为命令选项),或重新生成 VS 项目。
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources internal class Resources {
{
private static global::System.Resources.ResourceManager resourceMan; private static global::System.Resources.ResourceManager resourceMan;
@ -34,12 +33,12 @@ namespace WinNodeEditorDemo.Properties
} }
/// <summary> /// <summary>
/// Returns the cached ResourceManager instance used by this class. /// 返回此类使用的缓存的 ResourceManager 实例。
/// </summary> /// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager { internal static global::System.Resources.ResourceManager ResourceManager {
get { get {
if ((resourceMan == null)) { if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WinNodeEditorDemo.Properties.Resources", typeof(Resources).Assembly); global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WinNodeEditorDemo.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp; resourceMan = temp;
} }
@ -48,8 +47,8 @@ namespace WinNodeEditorDemo.Properties
} }
/// <summary> /// <summary>
/// Overrides the current thread's CurrentUICulture property for all /// 重写当前线程的 CurrentUICulture 属性,对
/// resource lookups using this strongly typed resource class. /// 使用此强类型资源类的所有资源查找执行重写。
/// </summary> /// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture { internal static global::System.Globalization.CultureInfo Culture {

View File

@ -1,21 +1,19 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// <auto-generated> // <auto-generated>
// This code was generated by a tool. // 此代码由工具生成。
// Runtime Version:4.0.30319.42000 // 运行时版本:4.0.30319.42000
// //
// Changes to this file may cause incorrect behavior and will be lost if // 对此文件的更改可能会导致不正确的行为,并且如果
// the code is regenerated. // 重新生成代码,这些更改将会丢失。
// </auto-generated> // </auto-generated>
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
namespace WinNodeEditorDemo.Properties namespace WinNodeEditorDemo.Properties {
{
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.14.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
{
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform> <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
@ -10,8 +10,9 @@
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>WinNodeEditorDemo</RootNamespace> <RootNamespace>WinNodeEditorDemo</RootNamespace>
<AssemblyName>WinNodeEditorDemo</AssemblyName> <AssemblyName>WinNodeEditorDemo</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
@ -22,6 +23,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants> <DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<PlatformTarget>x86</PlatformTarget> <PlatformTarget>x86</PlatformTarget>
@ -31,6 +33,7 @@
<DefineConstants>TRACE</DefineConstants> <DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <Reference Include="System" />
@ -82,7 +85,9 @@
<Compile Include="Properties\Resources.Designer.cs"> <Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen> <AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon> <DependentUpon>Resources.resx</DependentUpon>
<DesignTime>True</DesignTime>
</Compile> </Compile>
<None Include="app.config" />
<None Include="Properties\Settings.settings"> <None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator> <Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput> <LastGenOutput>Settings.Designer.cs</LastGenOutput>

View File

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/></startup></configuration>

View File

@ -1,10 +1,14 @@
 
Microsoft Visual Studio Solution File, Format Version 11.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2010 # Visual Studio Version 17
VisualStudioVersion = 17.14.35906.104
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ST.Library.UI", "ST.Library.UI\ST.Library.UI.csproj", "{EFFCC270-4999-4077-A543-56CCCCE92147}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ST.Library.UI", "ST.Library.UI\ST.Library.UI.csproj", "{EFFCC270-4999-4077-A543-56CCCCE92147}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinNodeEditorDemo", "WinNodeEditorDemo\WinNodeEditorDemo.csproj", "{4E1829B5-2160-48F5-ABD6-11914A6A25DD}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinNodeEditorDemo", "WinNodeEditorDemo\WinNodeEditorDemo.csproj", "{4E1829B5-2160-48F5-ABD6-11914A6A25DD}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfNodeEdittorDemo", "WpfNodeEdittorDemo\WpfNodeEdittorDemo.csproj", "{B355CF99-7494-472A-8E0E-1543236FFC88}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -35,8 +39,23 @@ Global
{4E1829B5-2160-48F5-ABD6-11914A6A25DD}.Release|Mixed Platforms.Build.0 = Release|x86 {4E1829B5-2160-48F5-ABD6-11914A6A25DD}.Release|Mixed Platforms.Build.0 = Release|x86
{4E1829B5-2160-48F5-ABD6-11914A6A25DD}.Release|x86.ActiveCfg = Release|x86 {4E1829B5-2160-48F5-ABD6-11914A6A25DD}.Release|x86.ActiveCfg = Release|x86
{4E1829B5-2160-48F5-ABD6-11914A6A25DD}.Release|x86.Build.0 = Release|x86 {4E1829B5-2160-48F5-ABD6-11914A6A25DD}.Release|x86.Build.0 = Release|x86
{B355CF99-7494-472A-8E0E-1543236FFC88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B355CF99-7494-472A-8E0E-1543236FFC88}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B355CF99-7494-472A-8E0E-1543236FFC88}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{B355CF99-7494-472A-8E0E-1543236FFC88}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{B355CF99-7494-472A-8E0E-1543236FFC88}.Debug|x86.ActiveCfg = Debug|Any CPU
{B355CF99-7494-472A-8E0E-1543236FFC88}.Debug|x86.Build.0 = Debug|Any CPU
{B355CF99-7494-472A-8E0E-1543236FFC88}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B355CF99-7494-472A-8E0E-1543236FFC88}.Release|Any CPU.Build.0 = Release|Any CPU
{B355CF99-7494-472A-8E0E-1543236FFC88}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{B355CF99-7494-472A-8E0E-1543236FFC88}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{B355CF99-7494-472A-8E0E-1543236FFC88}.Release|x86.ActiveCfg = Release|Any CPU
{B355CF99-7494-472A-8E0E-1543236FFC88}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {CE29E4E7-E2FB-4387-9743-04332BC01645}
EndGlobalSection
EndGlobal EndGlobal

View File

@ -0,0 +1,9 @@
<Application x:Class="WpfNodeEdittorDemo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfNodeEdittorDemo"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>

View File

@ -0,0 +1,14 @@
using System.Configuration;
using System.Data;
using System.Windows;
namespace WpfNodeEdittorDemo
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}
}

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)
)]

View File

@ -0,0 +1,108 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ST.Library.UI.NodeEditor;
using System.Drawing;
using System.Windows.Forms;
namespace WinNodeEditorDemo
{
[STNode("/", "Crystal_lz", "2212233137@qq.com", "www.st233.com", "关于此节点的描述信息\r\n此类为\r\nSTNodeAttribute\r\nSTNodePropertyAttribute\r\n效果演示类")]
public class AttrTestNode : STNode
{
//因为属性编辑器默认并不支持Color类型数据 所以这里重写一个描述器并指定
[STNodeProperty("颜色", "颜色信息", DescriptorType = typeof(DescriptorForColor))]
public Color Color { get; set; }
[STNodeProperty("整型数组", "整型数组测试")]
public int[] IntArr { get; set; }
[STNodeProperty("布尔", "布尔类型测试")]
public bool Bool { get; set; }
[STNodeProperty("字符串", "字符串类型测试")]
public string String { get; set; }
[STNodeProperty("整型", "整型测试")]
public int Int { get; set; }
[STNodeProperty("浮点数", "浮点数类型测试")]
public float Float { get; set; }
[STNodeProperty("枚举值", "枚举类型测试 -> FormBorderStyle")]
public FormBorderStyle STYLE { get; set; }
public AttrTestNode() {
this.String = "string";
IntArr = new int[] { 10, 20 };
base.InputOptions.Add("string", typeof(string), false);
base.OutputOptions.Add("string", typeof(string), false);
this.Title = "AttrTestNode";
this.TitleColor = Color.FromArgb(200, Color.Goldenrod);
}
/// <summary>
/// 此方法为魔术方法(Magic function)
/// 若存在 static void ShowHelpInfo(string) 且此类被STNodeAttribute标记
/// 则此方法将作为属性编辑器上 查看帮助 功能
/// </summary>
/// <param name="strFileName">此类所在的模块所在的文件路径</param>
public static void ShowHelpInfo(string strFileName) {
MessageBox.Show("this is -> ShowHelpInfo(string);\r\n" + strFileName);
}
protected override void OnOwnerChanged() {
base.OnOwnerChanged();
if (this.Owner == null) return;
this.Owner.SetTypeColor(typeof(string), Color.Goldenrod);
}
}
/// <summary>
/// 因为属性编辑器默认并不支持Color类型数据 所以这里重写一个描述器
/// </summary>
public class DescriptorForColor : STNodePropertyDescriptor
{
private Rectangle m_rect;//此区域用作 属性窗口上绘制颜色预览
//当此属性在属性窗口中被确定位置时候发生
protected override void OnSetItemLocation() {
base.OnSetItemLocation();
Rectangle rect = base.RectangleR;
m_rect = new Rectangle(rect.Right - 25, rect.Top + 5, 19, 12);
}
//将属性值转换为字符串 属性窗口值绘制时将采用此字符串
protected override string GetStringFromValue() {
Color clr = (Color)this.GetValue(null);
return clr.A + "," + clr.R + "," + clr.G + "," + clr.B;
}
//将属性窗口中输入的字符串转化为Color属性 当属性窗口中用户确认输入时调用
protected override object GetValueFromString(string strText) {
string[] strClr = strText.Split(',');
return Color.FromArgb(
int.Parse(strClr[0]), //A
int.Parse(strClr[1]), //R
int.Parse(strClr[2]), //G
int.Parse(strClr[3])); //B
}
//绘制属性窗口值区域时候调用
protected override void OnDrawValueRectangle(DrawingTools dt) {
base.OnDrawValueRectangle(dt);//先采用默认的绘制 并再绘制颜色预览
dt.SolidBrush.Color = (Color)this.GetValue(null);
dt.Graphics.FillRectangle(dt.SolidBrush, m_rect);//填充颜色
dt.Graphics.DrawRectangle(Pens.Black, m_rect); //绘制边框
}
protected override void OnMouseClick(MouseEventArgs e) {
//如果用户点击在 颜色预览区域 则弹出系统颜色对话框
if (m_rect.Contains(e.Location)) {
ColorDialog cd = new ColorDialog();
if (cd.ShowDialog() != DialogResult.OK) return;
this.SetValue(cd.Color, null);
this.Invalidate();
return;
}
//否则其他区域将采用默认处理方式 弹出字符串输入框
base.OnMouseClick(e);
}
}
}

View File

@ -0,0 +1,130 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ST.Library.UI.NodeEditor;
using System.Drawing;
namespace WinNodeEditorDemo.Blender
{
/// <summary>
/// 此类仅仅是演示 并不包含颜色混合功能
/// </summary>
[STNode("/Blender/", "Crystal_lz", "2212233137@qq.com", "st233.com", "this is blender mixrgb node")]
public class BlenderMixColorNode : STNode
{
private ColorMixType _MixType;
[STNodeProperty("MixType","This is MixType")]
public ColorMixType MixType {
get { return _MixType; }
set {
_MixType = value;
m_ctrl_select.Enum = value; //当属性被赋值后 对应控件状态值也应当被修改
}
}
private bool _Clamp;
[STNodeProperty("Clamp","This is Clamp")]
public bool Clamp {
get { return _Clamp; }
set { _Clamp = value; m_ctrl_checkbox.Checked = value; }
}
private int _Fac = 50;
[STNodeProperty("Fac", "This is Fac")]
public int Fac {
get { return _Fac; }
set {
if (value < 0) value = 0;
if (value > 100) value = 100;
_Fac = value; m_ctrl_progess.Value = value;
}
}
private Color _Color1 = Color.LightGray;//默认的DescriptorType不支持颜色的显示 需要扩展
[STNodeProperty("Color1", "This is color1", DescriptorType = typeof(WinNodeEditorDemo.DescriptorForColor))]
public Color Color1 {
get { return _Color1; }
set { _Color1 = value; m_ctrl_btn_1.BackColor = value; }
}
private Color _Color2 = Color.LightGray;
[STNodeProperty("Color2", "This is color2", DescriptorType = typeof(WinNodeEditorDemo.DescriptorForColor))]
public Color Color2 {
get { return _Color2; }
set { _Color2 = value; m_ctrl_btn_2.BackColor = value; }
}
public enum ColorMixType {
Mix,
Value,
Color,
Hue,
Add,
Subtract
}
private STNodeSelectEnumBox m_ctrl_select; //自定义控件
private STNodeProgress m_ctrl_progess;
private STNodeCheckBox m_ctrl_checkbox;
private STNodeColorButton m_ctrl_btn_1;
private STNodeColorButton m_ctrl_btn_2;
protected override void OnCreate() {
base.OnCreate();
this.TitleColor = Color.FromArgb(200, Color.DarkKhaki);
this.Title = "MixRGB";
this.AutoSize = false;
this.Size = new Size(140, 142);
this.OutputOptions.Add("Color", typeof(Color), true);
this.InputOptions.Add(STNodeOption.Empty); //空白节点 仅站位 不参与绘制与事件触发
this.InputOptions.Add(STNodeOption.Empty);
this.InputOptions.Add(STNodeOption.Empty);
this.InputOptions.Add("", typeof(float), true);
this.InputOptions.Add("Color1", typeof(Color), true);
this.InputOptions.Add("Color2", typeof(Color), true);
m_ctrl_progess = new STNodeProgress(); //创建控件并添加到节点中
m_ctrl_progess.Text = "Fac";
m_ctrl_progess.DisplayRectangle = new Rectangle(10, 61, 120, 18);
m_ctrl_progess.ValueChanged += (s, e) => this._Fac = m_ctrl_progess.Value;
this.Controls.Add(m_ctrl_progess);
m_ctrl_checkbox = new STNodeCheckBox();
m_ctrl_checkbox.Text = "Clamp";
m_ctrl_checkbox.DisplayRectangle = new Rectangle(10, 40, 120, 20);
m_ctrl_checkbox.ValueChanged += (s, e) => this._Clamp = m_ctrl_checkbox.Checked;
this.Controls.Add(m_ctrl_checkbox);
m_ctrl_btn_1 = new STNodeColorButton();
m_ctrl_btn_1.Text = "";
m_ctrl_btn_1.BackColor = this._Color1;
m_ctrl_btn_1.DisplayRectangle = new Rectangle(80, 82, 50, 16);
m_ctrl_btn_1.ValueChanged += (s, e) => this._Color1 = m_ctrl_btn_1.BackColor;
this.Controls.Add(m_ctrl_btn_1);
m_ctrl_btn_2 = new STNodeColorButton();
m_ctrl_btn_2.Text = "";
m_ctrl_btn_2.BackColor = this._Color2;
m_ctrl_btn_2.DisplayRectangle = new Rectangle(80, 102, 50, 16);
m_ctrl_btn_2.ValueChanged += (s, e) => this._Color2 = m_ctrl_btn_2.BackColor;
this.Controls.Add(m_ctrl_btn_2);
m_ctrl_select = new STNodeSelectEnumBox();
m_ctrl_select.DisplayRectangle = new Rectangle(10, 21, 120, 18);
m_ctrl_select.Enum = this._MixType;
m_ctrl_select.ValueChanged += (s, e) => this._MixType = (ColorMixType)m_ctrl_select.Enum;
this.Controls.Add(m_ctrl_select);
}
protected override void OnOwnerChanged() { //当控件被添加时候 向编辑器提交自己的数据类型希望显示的颜色
base.OnOwnerChanged();
if (this.Owner == null) return;
this.Owner.SetTypeColor(typeof(float), Color.Gray);
this.Owner.SetTypeColor(typeof(Color), Color.Yellow);
}
}
}

View File

@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
namespace WinNodeEditorDemo.Blender
{
/// <summary>
/// 此类仅演示 作为MixRGB节点的下拉选择框弹出菜单
/// </summary>
public class FrmEnumSelect : Form
{
private Point m_pt;
private int m_nWidth;
private float m_scale;
private List<object> m_lst = new List<object>();
private StringFormat m_sf;
public Enum Enum { get; set; }
private bool m_bClosed;
public FrmEnumSelect(Enum e, Point pt, int nWidth, float scale) {
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
foreach (var v in Enum.GetValues(e.GetType())) m_lst.Add(v);
this.Enum = e;
m_pt = pt;
m_scale = scale;
m_nWidth = nWidth;
m_sf = new StringFormat();
m_sf.LineAlignment = StringAlignment.Center;
this.ShowInTaskbar = false;
this.BackColor = Color.FromArgb(255, 34, 34, 34);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
}
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
this.Location = m_pt;
this.Width = (int)(m_nWidth * m_scale);
this.Height = (int)(m_lst.Count * 20 * m_scale);
}
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
Graphics g = e.Graphics;
g.ScaleTransform(m_scale, m_scale);
Rectangle rect = new Rectangle(0, 0, this.Width, 20);
foreach (var v in m_lst) {
g.DrawString(v.ToString(), this.Font, Brushes.White, rect, m_sf);
rect.Y += rect.Height;
}
}
protected override void OnMouseClick(MouseEventArgs e) {
base.OnMouseClick(e);
int nIndex = e.Y / (int)(20 * m_scale);
if (nIndex >= 0 && nIndex < m_lst.Count) this.Enum = (Enum)m_lst[nIndex];
this.DialogResult = System.Windows.Forms.DialogResult.OK;
m_bClosed = true;
}
protected override void OnMouseLeave(EventArgs e) {
base.OnMouseLeave(e);
if (m_bClosed) return;
//this.DialogResult = System.Windows.Forms.DialogResult.None;
this.Close();
}
}
}

View File

@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using ST.Library.UI.NodeEditor;
namespace WinNodeEditorDemo.Blender
{
/// <summary>
/// 此类仅演示 作为MixRGB节点的复选框控件
/// </summary>
public class STNodeCheckBox : STNodeControl
{
private bool _Checked;
public bool Checked {
get { return _Checked; }
set {
_Checked = value;
this.Invalidate();
}
}
public event EventHandler ValueChanged;
protected virtual void OnValueChanged(EventArgs e) {
if (this.ValueChanged != null) this.ValueChanged(this, e);
}
protected override void OnMouseClick(System.Windows.Forms.MouseEventArgs e) {
base.OnMouseClick(e);
this.Checked = !this.Checked;
this.OnValueChanged(new EventArgs());
}
protected override void OnPaint(DrawingTools dt) {
//base.OnPaint(dt);
Graphics g = dt.Graphics;
g.FillRectangle(Brushes.Gray, 0, 5, 10, 10);
m_sf.Alignment = StringAlignment.Near;
g.DrawString(this.Text, this.Font, Brushes.LightGray, new Rectangle(15, 0, this.Width - 20, 20), m_sf);
if (this.Checked) {
g.FillRectangle(Brushes.Black, 2, 7, 6, 6);
}
}
}
}

View File

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using ST.Library.UI.NodeEditor;
namespace WinNodeEditorDemo.Blender
{
/// <summary>
/// 此类仅演示 作为MixRGB节点的颜色选择按钮
/// </summary>
public class STNodeColorButton : STNodeControl
{
public event EventHandler ValueChanged;
protected virtual void OnValueChanged(EventArgs e) {
if (this.ValueChanged != null) this.ValueChanged(this, e);
}
protected override void OnMouseClick(System.Windows.Forms.MouseEventArgs e) {
base.OnMouseClick(e);
ColorDialog cd = new ColorDialog();
if (cd.ShowDialog() != DialogResult.OK) return;
//this._Color = cd.Color;
this.BackColor = cd.Color;
this.OnValueChanged(new EventArgs());
}
}
}

View File

@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ST.Library.UI.NodeEditor;
using System.Drawing;
namespace WinNodeEditorDemo.Blender
{
/// <summary>
/// 此类仅演示 作为MixRGB节点的进度条控件
/// </summary>
public class STNodeProgress : STNodeControl
{
private int _Value = 50;
public int Value {
get { return _Value; }
set {
_Value = value;
this.Invalidate();
}
}
private bool m_bMouseDown;
public event EventHandler ValueChanged;
protected virtual void OnValueChanged(EventArgs e) {
if (this.ValueChanged != null) this.ValueChanged(this, e);
}
protected override void OnPaint(DrawingTools dt) {
base.OnPaint(dt);
Graphics g = dt.Graphics;
g.FillRectangle(Brushes.Gray, this.ClientRectangle);
g.FillRectangle(Brushes.CornflowerBlue, 0, 0, (int)((float)this._Value / 100 * this.Width), this.Height);
m_sf.Alignment = StringAlignment.Near;
g.DrawString(this.Text, this.Font, Brushes.White, this.ClientRectangle, m_sf);
m_sf.Alignment = StringAlignment.Far;
g.DrawString(((float)this._Value / 100).ToString("F2"), this.Font, Brushes.White, this.ClientRectangle, m_sf);
}
protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs e) {
base.OnMouseDown(e);
m_bMouseDown = true;
}
protected override void OnMouseUp(System.Windows.Forms.MouseEventArgs e) {
base.OnMouseUp(e);
m_bMouseDown = false;
}
protected override void OnMouseMove(System.Windows.Forms.MouseEventArgs e) {
base.OnMouseMove(e);
if (!m_bMouseDown) return;
int v = (int)((float)e.X / this.Width * 100);
if (v < 0) v = 0;
if (v > 100) v = 100;
this._Value = v;
this.OnValueChanged(new EventArgs());
this.Invalidate();
}
}
}

View File

@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using ST.Library.UI.NodeEditor;
namespace WinNodeEditorDemo.Blender
{
/// <summary>
/// 此类仅演示 作为MixRGB节点的下拉框控件
/// </summary>
public class STNodeSelectEnumBox : STNodeControl
{
private Enum _Enum;
public Enum Enum {
get { return _Enum; }
set {
_Enum = value;
this.Invalidate();
}
}
public event EventHandler ValueChanged;
protected virtual void OnValueChanged(EventArgs e) {
if (this.ValueChanged != null) this.ValueChanged(this, e);
}
protected override void OnPaint(DrawingTools dt) {
Graphics g = dt.Graphics;
dt.SolidBrush.Color = Color.FromArgb(80, 0, 0, 0);
g.FillRectangle(dt.SolidBrush, this.ClientRectangle);
m_sf.Alignment = StringAlignment.Near;
g.DrawString(this.Enum.ToString(), this.Font, Brushes.White, this.ClientRectangle, m_sf);
g.FillPolygon(Brushes.Gray, new Point[]{
new Point(this.Right - 25, 7),
new Point(this.Right - 15, 7),
new Point(this.Right - 20, 12)
});
}
protected override void OnMouseClick(System.Windows.Forms.MouseEventArgs e) {
base.OnMouseClick(e);
Point pt = new Point(this.Left + this.Owner.Left, this.Top + this.Owner.Top + this.Owner.TitleHeight);
pt = this.Owner.Owner.CanvasToControl(pt);
pt = this.Owner.Owner.PointToScreen(pt);
FrmEnumSelect frm = new FrmEnumSelect(this.Enum, pt, this.Width, this.Owner.Owner.CanvasScale);
var v = frm.ShowDialog();
if (v != System.Windows.Forms.DialogResult.OK) return;
this.Enum = frm.Enum;
this.OnValueChanged(new EventArgs());
}
}
}

View File

@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ST.Library.UI.NodeEditor;
using System.Drawing;
namespace WinNodeEditorDemo
{
/// <summary>
/// 此节点仅演示UI自定义以及控件 并不包含功能
/// </summary>
[STNode("/", "DebugST", "2212233137@qq.com", "st233.com", "此节点仅演示UI自定义以及控件,并不包含功能.")]
public class CalcNode : STNode
{
private StringFormat m_f;
protected override void OnCreate() {
base.OnCreate();
m_sf = new StringFormat();
m_sf.LineAlignment = StringAlignment.Center;
this.Title = "Calculator";
this.AutoSize = false; //注意需要先设置AutoSize=false 才能够进行大小设置
this.Size = new Size(218, 308);
var ctrl = new STNodeControl();
ctrl.Text = ""; //此控件为显示屏幕
ctrl.Location = new Point(13, 31);
ctrl.Size = new Size(190, 50);
this.Controls.Add(ctrl);
ctrl.Paint += (s, e) => {
m_sf.Alignment = StringAlignment.Far;
STNodeControl c = s as STNodeControl;
Graphics g = e.DrawingTools.Graphics;
g.DrawString("0", ctrl.Font, Brushes.White, c.ClientRectangle, m_sf);
};
string[] strs = { //按钮文本
"MC", "MR", "MS", "M+",
"M-", "←", "CE", "C", "+", "√",
"7", "8", "9", "/", "%",
"4", "5", "6", "*", "1/x",
"1", "2", "3", "-", "=",
"0", " ", ".", "+" };
Point p = new Point(13, 86);
for (int i = 0; i < strs.Length; i++) {
if (strs[i] == " ") continue;
ctrl = new STNodeControl();
ctrl.Text = strs[i];
ctrl.Size = new Size(34, 27);
ctrl.Left = 13 + (i % 5) * 39;
ctrl.Top = 86 + (i / 5) * 32;
if (ctrl.Text == "=") ctrl.Height = 59;
if (ctrl.Text == "0") ctrl.Width = 73;
this.Controls.Add(ctrl);
if (i == 8) ctrl.Paint += (s, e) => {
m_sf.Alignment = StringAlignment.Center;
STNodeControl c = s as STNodeControl;
Graphics g = e.DrawingTools.Graphics;
g.DrawString("_", ctrl.Font, Brushes.White, c.ClientRectangle, m_sf);
};
ctrl.MouseClick += (s, e) => System.Windows.Forms.MessageBox.Show(((STNodeControl)s).Text);
}
this.OutputOptions.Add("Result", typeof(int), false);
}
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ST.Library.UI.NodeEditor;
namespace WinNodeEditorDemo
{
[STNode("/")]
public class EmptyOptionTestNode : STNode
{
protected override void OnCreate() {
base.OnCreate();
this.Title = "EmptyTest";
this.InputOptions.Add(STNodeOption.Empty);
this.InputOptions.Add("string", typeof(string), false);
}
}
}

View File

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ST.Library.UI.NodeEditor;
using System.Drawing;
namespace WinNodeEditorDemo.ImageNode
{
/// <summary>
/// 图片节点基类 用于确定节点风格 标题颜色 以及 数据类型颜色
/// </summary>
public abstract class ImageBaseNode : STNode
{
/// <summary>
/// 需要作为显示绘制的图片
/// </summary>
protected Image m_img_draw;
/// <summary>
/// 输出节点
/// </summary>
protected STNodeOption m_op_img_out;
protected override void OnCreate() {
base.OnCreate();
m_op_img_out = this.OutputOptions.Add("", typeof(Image), false);
this.AutoSize = false; //此节点需要定制UI 所以无需AutoSize
//this.Size = new Size(320,240);
this.Width = 160; //手动设置节点大小
this.Height = 120;
this.TitleColor = Color.FromArgb(200, Color.DarkCyan);
}
protected override void OnOwnerChanged() { //向编辑器提交数据类型颜色
base.OnOwnerChanged();
if (this.Owner == null) return;
this.Owner.SetTypeColor(typeof(Image), Color.DarkCyan);
}
}
}

View File

@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ST.Library.UI.NodeEditor;
using System.Drawing;
using System.Drawing.Imaging;
namespace WinNodeEditorDemo.ImageNode
{
[STNode("/Image")]
public class ImageChannelNode : ImageBaseNode
{
private STNodeOption m_op_img_in; //输入的节点
private STNodeOption m_op_img_r; //R图 输出节点
private STNodeOption m_op_img_g; //G图 输出节点
private STNodeOption m_op_img_b; //B图 输出节点
protected override void OnCreate() {
base.OnCreate();
this.Title = "ImageChannel";
m_op_img_in = this.InputOptions.Add("", typeof(Image), true);
m_op_img_r = this.OutputOptions.Add("R", typeof(Image), false);
m_op_img_g = this.OutputOptions.Add("G", typeof(Image), false);
m_op_img_b = this.OutputOptions.Add("B", typeof(Image), false);
//当输入节点有数据输入时候
m_op_img_in.DataTransfer += new STNodeOptionEventHandler(m_op_img_in_DataTransfer);
}
void m_op_img_in_DataTransfer(object sender, STNodeOptionEventArgs e) {
//如果当前不是连接状态 或者 接受到的数据为空
if (e.Status != ConnectionStatus.Connected || e.TargetOption.Data == null) {
m_op_img_out.TransferData(null); //向所有输出节点输出空数据
m_op_img_r.TransferData(null);
m_op_img_g.TransferData(null);
m_op_img_b.TransferData(null);
m_img_draw = null; //需要绘制显示的图片置为空
} else {
Bitmap bmp = (Bitmap)e.TargetOption.Data; //否则计算图片的RGB图像
Bitmap bmp_r = new Bitmap(bmp.Width, bmp.Height);
Bitmap bmp_g = new Bitmap(bmp.Width, bmp.Height);
Bitmap bmp_b = new Bitmap(bmp.Width, bmp.Height);
BitmapData bmpData = bmp.LockBits(new Rectangle(Point.Empty, bmp.Size), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
BitmapData bmpData_r = bmp_r.LockBits(new Rectangle(Point.Empty, bmp.Size), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
BitmapData bmpData_g = bmp_g.LockBits(new Rectangle(Point.Empty, bmp.Size), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
BitmapData bmpData_b = bmp_b.LockBits(new Rectangle(Point.Empty, bmp.Size), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
byte[] byColor = new byte[bmpData.Height * bmpData.Stride];
byte[] byColor_r = new byte[byColor.Length];
byte[] byColor_g = new byte[byColor.Length];
byte[] byColor_b = new byte[byColor.Length];
System.Runtime.InteropServices.Marshal.Copy(bmpData.Scan0, byColor, 0, byColor.Length);
for (int y = 0; y < bmpData.Height; y++) {
int ny = y * bmpData.Stride;
for (int x = 0; x < bmpData.Width; x++) {
int nx = x << 2;
byColor_b[ny + nx] = byColor[ny + nx];
byColor_g[ny + nx + 1] = byColor[ny + nx + 1];
byColor_r[ny + nx + 2] = byColor[ny + nx + 2];
byColor_r[ny + nx + 3] = byColor_g[ny + nx + 3] = byColor_b[ny + nx + 3] = byColor[ny + nx + 3];
}
}
bmp.UnlockBits(bmpData);
System.Runtime.InteropServices.Marshal.Copy(byColor_r, 0, bmpData_r.Scan0, byColor_r.Length);
System.Runtime.InteropServices.Marshal.Copy(byColor_g, 0, bmpData_g.Scan0, byColor_g.Length);
System.Runtime.InteropServices.Marshal.Copy(byColor_b, 0, bmpData_b.Scan0, byColor_b.Length);
bmp_r.UnlockBits(bmpData_r);
bmp_g.UnlockBits(bmpData_g);
bmp_b.UnlockBits(bmpData_b);
m_op_img_out.TransferData(bmp); //out选项 输出原图
m_op_img_r.TransferData(bmp_r); //R选项输出R图
m_op_img_g.TransferData(bmp_g);
m_op_img_b.TransferData(bmp_b);
m_img_draw = bmp; //需要绘制显示的图片
}
}
protected override void OnDrawBody(DrawingTools dt) {
base.OnDrawBody(dt);
Graphics g = dt.Graphics;
Rectangle rect = new Rectangle(this.Left + 10, this.Top + 30, 120, 80);
g.FillRectangle(Brushes.Gray, rect);
if (m_img_draw != null) g.DrawImage(m_img_draw, rect);
}
}
}

View File

@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ST.Library.UI.NodeEditor;
using System.Drawing;
using System.Windows.Forms;
using System.Reflection;
namespace WinNodeEditorDemo.ImageNode
{
[STNode("Image", "Crystal_lz", "2212233137@qq.com", "st233.com", "Image Node")]
public class ImageInputNode : ImageBaseNode
{
private string _FileName;//默认的DescriptorType不支持文件路径的选择 所以需要扩展
[STNodeProperty("InputImage", "Click to select a image", DescriptorType = typeof(OpenFileDescriptor))]
public string FileName {
get { return _FileName; }
set {
Image img = null; //当文件名被设置时 加载图片并 向输出节点输出
if (!string.IsNullOrEmpty(value)) {
img = Image.FromFile(value);
}
if (m_img_draw != null) m_img_draw.Dispose();
m_img_draw = img;
_FileName = value;
m_op_img_out.TransferData(m_img_draw, true);
this.Invalidate();
}
}
protected override void OnCreate() {
base.OnCreate();
this.Title = "ImageInput";
}
protected override void OnDrawBody(DrawingTools dt) {
base.OnDrawBody(dt);
Graphics g = dt.Graphics;
Rectangle rect = new Rectangle(this.Left + 10, this.Top + 30, 140, 80);
g.FillRectangle(Brushes.Gray, rect);
if (m_img_draw != null) g.DrawImage(m_img_draw, rect);
}
}
/// <summary>
/// 对默认Descriptor进行扩展 使得支持文件路径选择
/// </summary>
public class OpenFileDescriptor : STNodePropertyDescriptor
{
private Rectangle m_rect_open; //需要绘制"打开"按钮的区域
private StringFormat m_sf;
public OpenFileDescriptor() {
m_sf = new StringFormat();
m_sf.Alignment = StringAlignment.Center;
m_sf.LineAlignment = StringAlignment.Center;
}
protected override void OnSetItemLocation() { //当在STNodePropertyGrid上确定此属性需要显示的区域时候
base.OnSetItemLocation(); //计算出"打开"按钮需要绘制的区域
m_rect_open = new Rectangle(
this.RectangleR.Right - 20,
this.RectangleR.Top,
20,
this.RectangleR.Height);
}
protected override void OnMouseClick(System.Windows.Forms.MouseEventArgs e) {
if (m_rect_open.Contains(e.Location)) { //点击在"打开"区域 则弹出文件选择框
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "*.jpg|*.jpg|*.png|*.png";
if (ofd.ShowDialog() != DialogResult.OK) return;
this.SetValue(ofd.FileName);
} else base.OnMouseClick(e); //否则默认处理方式 弹出文本输入框
}
protected override void OnDrawValueRectangle(DrawingTools dt) {
base.OnDrawValueRectangle(dt); //在STNodePropertyGrid绘制此属性区域时候将"打开"按钮绘制上去
dt.Graphics.FillRectangle(Brushes.Gray, m_rect_open);
dt.Graphics.DrawString("+", this.Control.Font, Brushes.White, m_rect_open, m_sf);
}
}
}

View File

@ -0,0 +1,68 @@
<Window x:Class="WpfNodeEdittorDemo.MainWindow"
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfNodeEdittorDemo"
xmlns:st ="clr-namespace:ST.Library.UI.NodeEditor;assembly=ST.Library.UI"
mc:Ignorable="d"
PreviewKeyDown="UserControl_PreviewKeyDown"
Title="MainWindow" Height="720" Width="1280" Initialized="Window_Initialized">
<Grid x:Name="Grid1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Margin="5" Grid.Row="0" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal">
<Button x:Name="ButtonOpen" Content="加载" Click="Button_Click_Open" Width="70" />
<Button x:Name="ButtonNew" Content="打开" Click="Button_Click_New" Width="70" />
<Button x:Name="ButtonClear" Content="清除" Click="Button_Click_Clear" Width="70"/>
<Button x:Name="ButtonSave" Content="保存" Click="Button_Click_Save" Width="70" Margin="0,0,10,0"/>
<Button Content="自动对齐" Click="AutoAlignment_Click" Width="70"/>
</StackPanel>
<Grid Grid.Column="2">
<Slider x:Name="ZoomSlider" Width="200" Value="{Binding STNodeEditorHelper.CanvasScale}" Maximum="3" Minimum="0.45" HorizontalAlignment="Right"/>
</Grid>
</Grid>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions >
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="300"/>
</Grid.ColumnDefinitions>
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="300"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
</Grid>
<WindowsFormsHost Grid.Column="0" >
<st:STNodeEditor x:Name="STNodeEditorMain" MouseDown="STNodeEditorMain_MouseDown" MouseUp="STNodeEditorMain_MouseUp" MouseMove="STNodeEditorMain_MouseMove" MouseWheel="STNodeEditorMain_MouseWheel"/>
</WindowsFormsHost>
<Grid Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="250"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ScrollViewer Grid.Row="0" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<WindowsFormsHost Grid.Row="0">
<st:STNodeTreeView x:Name="STNodeTreeView1" />
</WindowsFormsHost>
</ScrollViewer>
<WindowsFormsHost Grid.Row="2">
<st:STNodePropertyGrid x:Name="STNodePropertyGrid1" />
</WindowsFormsHost>
</Grid>
</Grid>
</Grid>
</Window>

View File

@ -0,0 +1,262 @@

using ColorVision.Engine.Templates.Flow;
using ST.Library.UI.NodeEditor;
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Windows;
using System.Windows.Input;
namespace WpfNodeEdittorDemo
{
public class ActionCommand
{
public string Header { get; set; }
public Action UndoAction { get; set; }
public Action RedoAction { get; set; }
public ActionCommand(Action undoAction, Action redoAction)
{
UndoAction = undoAction;
RedoAction = redoAction;
}
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Undo, (s, e) => Undo(), (s, e) => { e.CanExecute = UndoStack.Count > 0; }));
this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Redo, (s, e) => Redo(), (s, e) => { e.CanExecute = RedoStack.Count > 0; }));
}
#region ActionCommand
public ObservableCollection<ActionCommand> UndoStack { get; set; } = new ObservableCollection<ActionCommand>();
public ObservableCollection<ActionCommand> RedoStack { get; set; } = new ObservableCollection<ActionCommand>();
public void ClearActionCommand()
{
UndoStack.Clear();
RedoStack.Clear();
}
public void AddActionCommand(ActionCommand actionCommand)
{
UndoStack.Add(actionCommand);
RedoStack.Clear();
}
public void Undo()
{
if (UndoStack.Count > 0)
{
var undoAction = UndoStack[^1]; // Access the last element
UndoStack.RemoveAt(UndoStack.Count - 1); // Remove the last element
undoAction.UndoAction();
RedoStack.Add(undoAction);
}
}
public void Redo()
{
if (RedoStack.Count > 0)
{
var redoAction = RedoStack[^1]; // Access the last element
RedoStack.RemoveAt(RedoStack.Count - 1); // Remove the last element
redoAction.RedoAction();
UndoStack.Add(redoAction);
}
}
#endregion
private void Window_Initialized(object sender, EventArgs e)
{
STNodePropertyGrid1.Text = "Node_Property";
STNodeTreeView1.LoadAssembly( System.Windows.Forms.Application.ExecutablePath.Replace("exe","dll"));
STNodeEditorMain.LoadAssembly(System.Windows.Forms.Application.ExecutablePath.Replace("exe", "dll"));
STNodeEditorHelper STNodeEditorHelper = new STNodeEditorHelper(this, STNodeEditorMain, STNodeTreeView1, STNodePropertyGrid1);
}
private void UserControl_PreviewKeyDown(object sender, KeyEventArgs e)
{
}
private void Button_Click_Open(object sender, RoutedEventArgs e)
{
System.Windows.Forms.OpenFileDialog ofd = new();
ofd.Filter = "*.stn|*.stn";
ofd.RestoreDirectory = true;
if (ofd.ShowDialog() != System.Windows.Forms.DialogResult.OK) return;
ButtonSave.Visibility = Visibility.Visible;
OpenFlow(ofd.FileName);
}
string FileFlow;
public void OpenFlow(string flowName)
{
FileFlow = flowName;
STNodeEditorMain.Nodes.Clear();
STNodeEditorMain.LoadCanvas(flowName);
Title = "流程编辑器 - " + new FileInfo(flowName).Name;
}
private bool IsMouseDown;
private System.Drawing.Point lastMousePosition;
private void STNodeEditorMain_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
lastMousePosition = e.Location;
System.Drawing.PointF m_pt_down_in_canvas = new System.Drawing.PointF();
m_pt_down_in_canvas.X = ((float)e.X - STNodeEditorMain.CanvasOffsetX) / STNodeEditorMain.CanvasScale;
m_pt_down_in_canvas.Y = ((float)e.Y - STNodeEditorMain.CanvasOffsetY) / STNodeEditorMain.CanvasScale;
NodeFindInfo nodeFindInfo = STNodeEditorMain.FindNodeFromPoint(m_pt_down_in_canvas);
if (!string.IsNullOrEmpty(nodeFindInfo.Mark))
{
}
else if (nodeFindInfo.Node != null)
{
}
else if (nodeFindInfo.NodeOption != null)
{
}
else if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
IsMouseDown = true;
}
}
private void STNodeEditorMain_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
{
IsMouseDown = false;
}
private void STNodeEditorMain_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (Keyboard.Modifiers.HasFlag(ModifierKeys.Control) && IsMouseDown)
{ // 计算鼠标移动的距离
int deltaX = e.X - lastMousePosition.X;
int deltaY = e.Y - lastMousePosition.Y;
// 更新画布偏移
STNodeEditorMain.MoveCanvas(
STNodeEditorMain.CanvasOffsetX + deltaX,
STNodeEditorMain.CanvasOffsetY + deltaY,
bAnimation: false,
CanvasMoveArgs.All
);
// 更新最后的鼠标位置
lastMousePosition = e.Location;
}
}
private void STNodeEditorMain_MouseWheel(object sender, System.Windows.Forms.MouseEventArgs e)
{
var mousePosition = STNodeEditorMain.PointToClient(e.Location);
if (e.Delta < 0)
{
STNodeEditorMain.ScaleCanvas(STNodeEditorMain.CanvasScale - 0.05f, mousePosition.X, mousePosition.Y);
}
else
{
STNodeEditorMain.ScaleCanvas(STNodeEditorMain.CanvasScale + 0.05f, mousePosition.X, mousePosition.Y);
}
}
private void Button_Click_Clear(object sender, RoutedEventArgs e)
{
FileFlow = string.Empty;
STNodeEditorMain.Nodes.Clear();
}
private void Button_Click_Save(object sender, RoutedEventArgs e)
{
Save();
}
private void Save()
{
if (string.IsNullOrEmpty(FileFlow) || !File.Exists(FileFlow))
{
using (System.Windows.Forms.SaveFileDialog saveFileDialog = new System.Windows.Forms.SaveFileDialog())
{
saveFileDialog.Filter = "*.stn|*.stn";
saveFileDialog.Title = "Select a File to Save";
if (saveFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
FileFlow = saveFileDialog.FileName;
}
else
{
// User cancelled the dialog
return;
}
}
}
SaveToFile(FileFlow);
MessageBox.Show("保存成功");
}
public void SaveToFile(string filePath)
{
// 获取画布数据
byte[] data = STNodeEditorMain.GetCanvasData();
// 检查数据是否为空
if (data == null || data.Length == 0)
{
Console.WriteLine("No data to save.");
return;
}
try
{
// 创建文件路径的目录(如果不存在)
string directory = Path.GetDirectoryName(filePath);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
// 将数据写入指定文件路径
File.WriteAllBytes(filePath, data);
Console.WriteLine("File saved successfully.");
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred while saving the file: {ex.Message}");
}
}
private void Button_Click_New(object sender, RoutedEventArgs e)
{
System.Windows.Forms.OpenFileDialog ofd = new();
ofd.Filter = "*.stn|*.stn";
ofd.RestoreDirectory = true;
if (ofd.ShowDialog() != System.Windows.Forms.DialogResult.OK) return;
OpenFlow(ofd.FileName);
}
private void AutoAlignment_Click(object sender, RoutedEventArgs e)
{
}
}
}

View File

@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ST.Library.UI.NodeEditor;
using System.Drawing;
namespace WinNodeEditorDemo.NumberNode
{
[STNode("/Number/", "Crystal_lz", "2212233137@qq.com", "www.st233.com", "This node can get two numbers add result")]
public class NumberAddNode : NumberNode
{
private STNodeOption m_in_num1;
private STNodeOption m_in_num2;
private STNodeOption m_out_num;
private int m_nNum1, m_nNum2;
private StringFormat m_sf;
protected override void OnCreate() {
base.OnCreate();
this.Title = "NumberAdd";
m_sf = new StringFormat();
m_sf.LineAlignment = StringAlignment.Center;
m_in_num1 = new STNodeOption("", typeof(int), true);//只能有一个连线
m_in_num2 = new STNodeOption("", typeof(int), true);//只能有一个连线
m_out_num = new STNodeOption("", typeof(int), false);//可以多个连线
this.InputOptions.Add(m_in_num1);
this.InputOptions.Add(m_in_num2);
this.OutputOptions.Add(m_out_num);
m_in_num1.DataTransfer += new STNodeOptionEventHandler(m_in_num_DataTransfer);
m_in_num2.DataTransfer += new STNodeOptionEventHandler(m_in_num_DataTransfer);
}
//当有数据传入时
void m_in_num_DataTransfer(object sender, STNodeOptionEventArgs e) {
//判断连线是否是连接状态(建立连线 断开连线 都会触发该事件)
if (e.Status == ConnectionStatus.Connected) {
if (sender == m_in_num1) {
if (e.TargetOption.Data != null) m_nNum1 = (int)e.TargetOption.Data;//TargetOption为触发此事件的Option
} else {
if (e.TargetOption.Data != null) m_nNum2 = (int)e.TargetOption.Data;
}
} else {
if (sender == m_in_num1) m_nNum1 = 0; else m_nNum2 = 0;
}
//向输出选项上的所有连线传输数据 输出选项上的所有连线都会触发 DataTransfer 事件
m_out_num.TransferData(m_nNum1 + m_nNum2); //m_out_num.Data 将被自动设置
this.Invalidate();
}
/// <summary>
/// 当绘制选项文本时候 将数字绘制 因为STNodeOption.Text被protected修饰 STNode无法进行设置
/// 因为作者并不建议对已经添加在STNode上的选项进行修改 尤其是在AutoSize被设置的情况下
/// 若有需求 应当采用其他方式 比如:重绘 或者添加STNodeControl来显示变化的文本信息
/// </summary>
/// <param name="dt">绘制工具</param>
/// <param name="op">需要绘制的选项</param>
protected override void OnDrawOptionText(DrawingTools dt, STNodeOption op) {
base.OnDrawOptionText(dt, op);
string strText = "";
if (op == m_in_num1) {
m_sf.Alignment = StringAlignment.Near;
strText = m_nNum1.ToString();
} else if (op == m_in_num2) {
m_sf.Alignment = StringAlignment.Near;
strText = m_nNum2.ToString();
} else {
m_sf.Alignment = StringAlignment.Far;
strText = (m_nNum1 + m_nNum2).ToString();
}
dt.Graphics.DrawString(strText, this.Font, Brushes.White, op.TextRectangle, m_sf);
}
}
}

View File

@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ST.Library.UI.NodeEditor;
using System.Drawing;
namespace WinNodeEditorDemo.NumberNode
{
/// <summary>
/// 此节点通过Number属性提供一个整数的输入
/// </summary>
[STNode("/Number","Crystal_lz","2212233137@qq.com","st233.com","Number input node")]
public class NumberInputNode : NumberNode
{
private int _Number;
[STNodeProperty("Input","this is input number")]
public int Number {
get { return _Number; }
set {
_Number = value;
m_op_number.TransferData(value); //将数据向下传递
this.Invalidate();
}
}
private STNodeOption m_op_number; //输出选项
private StringFormat m_sf = new StringFormat();
protected override void OnCreate() {
base.OnCreate();
this.Title = "NumberInput";
m_op_number = new STNodeOption("", typeof(int), false);
this.OutputOptions.Add(m_op_number);
m_sf = new StringFormat();
m_sf.LineAlignment = StringAlignment.Center;
m_sf.Alignment = StringAlignment.Far;
}
/// <summary>
/// 当绘制选项文本时候 将数字绘制 因为STNodeOption.Text被protected修饰 STNode无法进行设置
/// 因为作者并不建议对已经添加在STNode上的选项进行修改 尤其是在AutoSize被设置的情况下
/// 若有需求 应当采用其他方式 比如:重绘 或者添加STNodeControl来显示变化的文本信息
/// </summary>
/// <param name="dt">绘制工具</param>
/// <param name="op">需要绘制的选项</param>
protected override void OnDrawOptionText(DrawingTools dt, STNodeOption op) {
base.OnDrawOptionText(dt, op);
dt.Graphics.DrawString(this._Number.ToString(), this.Font, Brushes.White, op.TextRectangle, m_sf);
}
}
}

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ST.Library.UI.NodeEditor;
using System.Drawing;
namespace WinNodeEditorDemo.NumberNode
{
/// <summary>
/// Number节点基类 用于确定节点风格 标题颜色 以及 数据类型颜色
/// </summary>
public abstract class NumberNode : STNode
{
protected override void OnCreate() {
base.OnCreate();
this.TitleColor = Color.FromArgb(200, Color.CornflowerBlue);
}
protected override void OnOwnerChanged() {
base.OnOwnerChanged();
if (this.Owner != null) this.Owner.SetTypeColor(typeof(int), Color.CornflowerBlue);
}
}
}

View File

@ -0,0 +1,514 @@
#pragma warning disable CS8603,CS8604
using ST.Library.UI.NodeEditor;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
namespace ColorVision.Engine.Templates.Flow
{
public class STNodeEditorHelper
{
public STNodeEditor STNodeEditor { get; set; }
public STNodePropertyGrid STNodePropertyGrid1 { get; set; }
public STNodeTreeView STNodeTreeView1 { get; set; }
public STNodeEditorHelper(Control Paraent,STNodeEditor sTNodeEditor, STNodeTreeView sTNodeTreeView1, STNodePropertyGrid sTNodePropertyGrid)
{
STNodeEditor = sTNodeEditor;
STNodeTreeView1 = sTNodeTreeView1;
STNodePropertyGrid1 = sTNodePropertyGrid;
STNodeEditor.NodeAdded += StNodeEditor1_NodeAdded;
STNodeEditor.ActiveChanged += STNodeEditorMain_ActiveChanged;
AddContentMenu();
Paraent.CommandBindings.Add(new CommandBinding(ApplicationCommands.Delete, (s, e) =>
{
foreach (var item in STNodeEditor.GetSelectedNode())
STNodeEditor.Nodes.Remove(item);
} , (s, e) => { e.CanExecute = sTNodeEditor.GetSelectedNode().Length > 0; }));
Paraent.CommandBindings.Add(new CommandBinding(ApplicationCommands.New, (s, e) => sTNodeEditor.Nodes.Clear(), (s, e) => { e.CanExecute = true; }));
Paraent.CommandBindings.Add(new CommandBinding(ApplicationCommands.Copy, (s, e) => Copy(), (s, e) => { e.CanExecute = true; }));
Paraent.CommandBindings.Add(new CommandBinding(ApplicationCommands.Paste, (s, e) => Paste(), (s, e) => { e.CanExecute = CopyNodes.Count >0;}));
Paraent.CommandBindings.Add(new CommandBinding(ApplicationCommands.SelectAll, (s, e) => SelectAll(), (s, e) => { e.CanExecute = true; }));
Paraent.CommandBindings.Add(new CommandBinding(ApplicationCommands.Close, (s, e) => sTNodeEditor.Nodes.Clear(), (s, e) => { e.CanExecute = true; }));
}
private List<STNode> CopyNodes = new List<STNode>();
public void SelectAll()
{
foreach (var item in STNodeEditor.Nodes.OfType<STNode>())
{
STNodeEditor.AddSelectedNode(item);
}
}
public void Copy()
{
CopyNodes.Clear();
foreach (var item in STNodeEditor.GetSelectedNode())
{
CopyNodes.Add(item);
}
}
public void Paste()
{
int offset = 10;
foreach (var item in CopyNodes)
{
Type type = item.GetType();
STNode sTNode1 = (STNode)Activator.CreateInstance(type);
if (sTNode1 != null)
{
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
if (property.CanRead && property.CanWrite)
{
object value = property.GetValue(item);
property.SetValue(sTNode1, value);
}
}
sTNode1.Left = item.Left + offset;
sTNode1.Top = item.Top + offset;
sTNode1.IsSelected = true;
STNodeEditor.Nodes.Add(sTNode1);
if (CopyNodes.Count == 1)
{
item.IsSelected = false;
STNodeEditor.RemoveSelectedNode(item);
STNodeEditor.AddSelectedNode(sTNode1);
STNodeEditor.SetActiveNode(sTNode1);
}
else
{
STNodeEditor.RemoveSelectedNode(item);
STNodeEditor.AddSelectedNode(sTNode1);
}
}
}
CopyNodes.Clear();
foreach (var item in STNodeEditor.GetSelectedNode())
{
CopyNodes.Add(item);
}
}
#region Activate
private void STNodeEditorMain_ActiveChanged(object? sender, EventArgs e)
{
STNodePropertyGrid1.SetNode(STNodeEditor.ActiveNode);
}
#endregion
#region ContextMenu
public void AddNodeContext()
{
foreach (var item in STNodeEditor.Nodes)
{
if (item is STNode node)
{
node.ContextMenuStrip = new System.Windows.Forms.ContextMenuStrip();
node.ContextMenuStrip.Items.Add("复制", null, (s, e1) => CopySTNode(node));
node.ContextMenuStrip.Items.Add("删除", null, (s, e1) => STNodeEditor.Nodes.Remove(node));
node.ContextMenuStrip.Items.Add("LockOption", null, (s, e1) => STNodeEditor.ActiveNode.LockOption = !STNodeEditor.ActiveNode.LockOption);
node.ContextMenuStrip.Items.Add("LockLocation", null, (s, e1) => STNodeEditor.ActiveNode.LockLocation = !STNodeEditor.ActiveNode.LockLocation);
}
}
}
private void StNodeEditor1_NodeAdded(object sender, STNodeEditorEventArgs e)
{
STNode node = e.Node;
node.ContextMenuStrip = new System.Windows.Forms.ContextMenuStrip();
node.ContextMenuStrip.Items.Add("删除", null, (s, e1) => STNodeEditor.Nodes.Remove(node));
node.ContextMenuStrip.Items.Add("复制", null, (s, e1) => CopySTNode(node));
node.ContextMenuStrip.Items.Add("LockOption", null, (s, e1) => STNodeEditor.ActiveNode.LockOption = !STNodeEditor.ActiveNode.LockOption);
node.ContextMenuStrip.Items.Add("LockLocation", null, (s, e1) => STNodeEditor.ActiveNode.LockLocation = !STNodeEditor.ActiveNode.LockLocation);
}
public void CopySTNode(STNode sTNode)
{
Type type = sTNode.GetType();
STNode sTNode1 = (STNode)Activator.CreateInstance(type);
if (sTNode1 != null)
{
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
if (property.CanRead && property.CanWrite)
{
object value = property.GetValue(sTNode);
property.SetValue(sTNode1, value);
}
}
sTNode1.Left = sTNode.Left;
sTNode1.Top = sTNode.Top;
STNodeEditor.Nodes.Add(sTNode1);
}
}
public void AddContentMenu()
{
STNodeEditor.ContextMenuStrip = new System.Windows.Forms.ContextMenuStrip();
Type STNodeTreeViewtype = STNodeTreeView1.GetType();
// 获取私有字段信息
FieldInfo fieldInfo = STNodeTreeViewtype.GetField("m_dic_all_type", BindingFlags.NonPublic | BindingFlags.Instance);
if (fieldInfo != null)
{
// 获取字段的值
var value = fieldInfo.GetValue(STNodeTreeView1);
Dictionary<string, List<Type>> values = new Dictionary<string, List<Type>>();
if (value is Dictionary<Type, string> m_dic_all_type)
{
foreach (var item in m_dic_all_type)
{
if (values.TryGetValue(item.Value, out List<Type>? value1))
{
value1.Add(item.Key);
}
else
{
values.Add(item.Value, new List<Type>() { item.Key });
}
}
foreach (var nodetype in values.OrderBy(x => x.Key, Comparer<string>.Create((x, y) =>string.Compare(x,y))))
{
string header = nodetype.Key.Replace("WpfNodeEdittorDemo/", "");
var toolStripItem = new System.Windows.Forms.ToolStripMenuItem(header);
foreach (var type in nodetype.Value)
{
if (type.IsSubclassOf(typeof(STNode)))
{
if (Activator.CreateInstance(type) is STNode sTNode)
{
toolStripItem.DropDownItems.Add(sTNode.Title, null, (s, e) =>
{
STNode sTNode1 = (STNode)Activator.CreateInstance(type);
if (sTNode1 != null)
{
var p = STNodeEditor.PointToClient(lastMousePosition);
p = STNodeEditor.ControlToCanvas(p);
sTNode1.Left = p.X;
sTNode1.Top = p.Y;
STNodeEditor.Nodes.Add(sTNode1);
}
});
}
}
}
STNodeEditor.ContextMenuStrip.Items.Add(toolStripItem);
}
}
}
STNodeEditor.ContextMenuStrip.Opening += (s, e) =>
{
if (IsOptionDisConnected) e.Cancel = true;
if (IsHover())
e.Cancel = true;
IsOptionDisConnected = false;
};
STNodeEditor.OptionDisConnected += (s, e) =>
{
IsOptionDisConnected = true;
};
}
bool IsOptionDisConnected;
private System.Drawing.Point lastMousePosition;
public bool IsHover()
{
lastMousePosition = System.Windows.Forms.Cursor.Position;
var p = STNodeEditor.PointToClient(System.Windows.Forms.Cursor.Position);
p = STNodeEditor.ControlToCanvas(p);
foreach (var item in STNodeEditor.Nodes)
{
if (item is STNode sTNode)
{
bool result = sTNode.Rectangle.Contains(p);
if (result)
return true;
if (sTNode.GetInputOptions() is STNodeOption[] inputOptions)
{
foreach (STNodeOption inputOption in inputOptions)
{
if (inputOption != STNodeOption.Empty && inputOption.DotRectangle.Contains(p))
{
return true;
}
}
}
if (sTNode.GetOutputOptions() is STNodeOption[] outputOptions)
{
foreach (STNodeOption outputOption in outputOptions)
{
if (outputOption != STNodeOption.Empty && outputOption.DotRectangle.Contains(p))
{
return true;
}
}
}
}
}
return false;
}
#endregion
#region AutoLayout
public ConnectionInfo[] ConnectionInfo { get; set; }
public float CanvasScale { get => STNodeEditor.CanvasScale; set { STNodeEditor.ScaleCanvas(value, STNodeEditor.CanvasValidBounds.X + STNodeEditor.CanvasValidBounds.Width / 2, STNodeEditor.CanvasValidBounds.Y + STNodeEditor.CanvasValidBounds.Height / 2); } }
public void AutoSize()
{
// Calculate the centers
var boundsCenterX = STNodeEditor.Bounds.Width / 2;
var boundsCenterY = STNodeEditor.Bounds.Height / 2;
// Calculate the scale factor to fit CanvasValidBounds within Bounds
var scaleX = (float)STNodeEditor.Bounds.Width / (float)STNodeEditor.CanvasValidBounds.Width;
var scaleY = (float)STNodeEditor.Bounds.Height / (float)STNodeEditor.CanvasValidBounds.Height;
CanvasScale = Math.Min(scaleX, scaleY);
CanvasScale = CanvasScale > 1 ? 1 : CanvasScale;
// Apply the scale
STNodeEditor.ScaleCanvas(CanvasScale, STNodeEditor.CanvasValidBounds.X + STNodeEditor.CanvasValidBounds.Width / 2, STNodeEditor.CanvasValidBounds.Y + STNodeEditor.CanvasValidBounds.Height / 2);
var validBoundsCenterX = STNodeEditor.CanvasValidBounds.Width / 2;
var validBoundsCenterY = STNodeEditor.CanvasValidBounds.Height / 2;
// Calculate the offsets to move CanvasValidBounds to the center of Bounds
var offsetX = boundsCenterX - validBoundsCenterX * CanvasScale - 50 * CanvasScale;
var offsetY = boundsCenterY - validBoundsCenterY * CanvasScale - 50 * CanvasScale;
// Move the canvas
STNodeEditor.MoveCanvas(offsetX, STNodeEditor.CanvasOffset.Y, bAnimation: true, CanvasMoveArgs.Left);
STNodeEditor.MoveCanvas(offsetX, offsetY, bAnimation: true, CanvasMoveArgs.Top);
}
public void ApplyTreeLayout(int startX, int startY, int horizontalSpacing, int verticalSpacing)
{
ConnectionInfo = STNodeEditor.GetConnectionInfo();
STNode rootNode = null;
if (rootNode == null) return;
int currentY = startY;
HashSet<STNode> MoreParens = new HashSet<STNode>();
void LayoutNode(STNode node, int current)
{
int depeth = GetMaxDepth(node);
// 设置当前节点的位置
node.Left = startX + depeth * horizontalSpacing;
node.Top = current;
var parent = GetParent(node);
// 递归布局子节点
var children = GetChildren(node);
foreach (var child in children)
{
if (GetParent(child).Count > 1)
{
MoreParens.Add(child);
}
else
{
LayoutNode(child, currentY);
var childrenWithout1 = GetChildrenWithout(node);
if (childrenWithout1.Count > 1)
{
currentY += verticalSpacing;
}
}
}
var childrenWithout = GetChildrenWithout(node);
if (childrenWithout.Count > 1)
{
currentY = childrenWithout.Last().Top;
}
// 调整父节点位置到子节点的中心
if (childrenWithout.Count != 0)
{
int firstChildY = childrenWithout.First().Top;
int lastChildY = childrenWithout.Last().Top;
node.Top = (firstChildY + lastChildY) / 2;
}
if (parent.Count > 1)
{
int firstChildY = parent.First().Top;
int lastChildY = parent.Last().Top;
node.Top = (firstChildY + lastChildY) / 2;
}
}
void MoreParentsLayoutNode(STNode node)
{
node.Left = startX + GetMaxDepth(node) * horizontalSpacing;
var parent = GetParent(node);
// 递归布局子节点
var children = GetChildren(node);
int minParentY = parent.Min(c => c.Top);
int maxParentY = parent.Max(c => c.Top);
node.Top = (minParentY + maxParentY) / 2;
SetCof(node, verticalSpacing);
int currenty = node.Top;
foreach (var child in children)
{
LayoutNode(child, currenty);
currenty += verticalSpacing;
}
MoreParens.Remove(node);
}
LayoutNode(rootNode, currentY);
while (MoreParens.Count > 0)
{
foreach (var item in MoreParens.Cast<STNode>().ToList())
{
MoreParentsLayoutNode(item);
}
}
}
public void SetCof(STNode node, int verticalSpacing)
{
foreach (var item in STNodeEditor.Nodes)
{
if (item is STNode onode)
{
if (onode != node && onode.Left == node.Left && onode.Top == node.Top)
{
onode.Top += verticalSpacing;
SetCof(node, verticalSpacing);
}
}
}
}
public int GetMaxDepth(STNode node)
{
var parent = GetParent(node);
if (parent.Count == 0)
{
return 0;
}
return parent.Max(c => GetMaxDepth(c)) + 1;
}
List<STNode> GetParent(STNode node)
{
var list = ConnectionInfo.Where(c => c.Input.Owner == node);
List<STNode> children = new();
foreach (var item in list)
{
children.Add(item.Output.Owner);
}
return children;
}
List<STNode> GetChildrenWithout(STNode node)
{
var list = ConnectionInfo.Where(c => c.Output.Owner == node);
List<STNode> children = new();
foreach (var item in list)
{
if (GetParent(item.Input.Owner).Count == 1)
{
children.Add(item.Input.Owner);
}
}
return children;
}
List<STNode> GetChildren(STNode node)
{
var list = ConnectionInfo.Where(c => c.Output.Owner == node);
List<STNode> children = new();
foreach (var item in list)
{
children.Add(item.Input.Owner);
}
return children;
}
private bool IsPathExists(STNode startNode, STNode endNode)
{
var visited = new HashSet<STNode>();
var queue = new Queue<STNode>();
queue.Enqueue(startNode);
while (queue.Count > 0)
{
var currentNode = queue.Dequeue();
if (currentNode == endNode)
{
return true;
}
visited.Add(currentNode);
var children = GetChildren(currentNode);
foreach (var child in children)
{
if (!visited.Contains(child))
{
queue.Enqueue(child);
}
}
}
return false;
}
#endregion
}
}

View File

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ST.Library.UI.NodeEditor;
namespace WinNodeEditorDemo
{
/// <summary>
/// 类库自带的STNodeHub并未被STNodeAttribute标记 无法被STNodeTreeView显示 所以需要扩展
/// </summary>
[STNode("/", "Crystal_lz", "2212233137@qq.com", "st233.com", "This is single Hub")]
public class STNodeHubSingle : STNodeHub
{
public STNodeHubSingle()
: base(true) {
this.Title = "S_HUB";
}
}
/// <summary>
/// 类库自带的STNodeHub并未被STNodeAttribute标记 无法被STNodeTreeView显示 所以需要扩展
/// </summary>
[STNode("/", "Crystal_lz", "2212233137@qq.com", "st233.com", "This multi is Hub")]
public class STNodeHubMulti : STNodeHub
{
public STNodeHubMulti()
: base(false) {
this.Title = "M_HUB";
}
}
}

View File

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>disable</ImplicitUsings>
<UseWPF>true</UseWPF>
<UseWindowsForms>True</UseWindowsForms>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\ST.Library.UI\ST.Library.UI.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="Blender\FrmEnumSelect.cs">
<SubType>Form</SubType>
</Compile>
</ItemGroup>
</Project>