Commit 5b3bb857 by liulongfei

初始化项目

parents
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## 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/
# 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
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("VIZ.Framework.Common.Resource")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("VIZ.Framework.Common.Resource")]
[assembly: AssemblyCopyright("Copyright © 2022")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
//将 ComVisible 设置为 false 将使此程序集中的类型
//对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型,
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
//若要开始生成可本地化的应用程序,请设置
//.csproj 文件中的 <UICulture>CultureYouAreCodingWith</UICulture>
//例如,如果您在源文件中使用的是美国英语,
//使用的是美国英语,请将 <UICulture> 设置为 en-US。 然后取消
//对以下 NeutralResourceLanguage 特性的注释。 更新
//以下行中的“en-US”以匹配项目文件中的 UICulture 设置。
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly:ThemeInfo(
ResourceDictionaryLocation.None, //主题特定资源词典所处位置
//(未在页面中找到资源时使用,
//或应用程序资源字典中找到时使用)
ResourceDictionaryLocation.SourceAssembly //常规资源词典所处位置
//(未在页面中找到资源时使用,
//、应用程序或任何主题专用资源字典中找到时使用)
)]
// 程序集的版本信息由下列四个值组成:
//
// 主版本
// 次版本
// 生成号
// 修订号
//
//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
//通过使用 "*",如下所示:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本: 4.0.30319.42000
//
// 对此文件的更改可能导致不正确的行为,如果
// 重新生成代码,则所做更改将丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace VIZ.Framework.Common.Resource.Properties {
/// <summary>
/// 强类型资源类,用于查找本地化字符串等。
/// </summary>
// 此类是由 StronglyTypedResourceBuilder
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
// (以 /str 作为命令选项),或重新生成 VS 项目。
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// 返回此类使用的缓存 ResourceManager 实例。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if ((resourceMan == null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("VIZ.Framework.Common.Resource.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// 重写当前线程的 CurrentUICulture 属性,对
/// 使用此强类型资源类的所有资源查找执行重写。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
\ No newline at end of file
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace VIZ.Framework.Common.Resource.Properties
{
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
{
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default
{
get
{
return defaultInstance;
}
}
}
}
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>
\ No newline at end of file
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="VMessageBox_Button_Yes" TargetType="Button">
<Setter Property="FocusVisualStyle" Value="{x:Null}"></Setter>
<Setter Property="Width" Value="140"></Setter>
<Setter Property="Height" Value="45"></Setter>
<Setter Property="Margin" Value="10,0,10,0"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="bd" CornerRadius="10" Background="#aa007cff">
<ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center" TextElement.FontSize="18" TextElement.Foreground="White"></ContentPresenter>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="bd" Property="Background" Value="#ff007cff"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="VMessageBox_Button_Cancel" TargetType="Button">
<Setter Property="FocusVisualStyle" Value="{x:Null}"></Setter>
<Setter Property="Width" Value="140"></Setter>
<Setter Property="Height" Value="45"></Setter>
<Setter Property="Margin" Value="10,0,10,0"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="bd" CornerRadius="10" Background="#aadcdcdc">
<ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center" TextElement.FontSize="18" TextElement.Foreground="Black"></ContentPresenter>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="bd" Property="Background" Value="#ffdcdcdc"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
\ No newline at end of file
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="Button_Close" TargetType="Button">
<Setter Property="FocusVisualStyle" Value="{x:Null}"></Setter>
<Setter Property="Width" Value="50"></Setter>
<Setter Property="Height" Value="40"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="bd" BorderBrush="Transparent" BorderThickness="1" Background="Transparent">
<Grid x:Name="grid" HorizontalAlignment="Center" VerticalAlignment="Center" Width="20" Height="20" RenderTransformOrigin="0.5,0.5">
<Grid.RenderTransform>
<RotateTransform Angle="0"/>
</Grid.RenderTransform>
<Rectangle Width="20" Height="2" Fill="White" HorizontalAlignment="Center" VerticalAlignment="Center" RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<RotateTransform Angle="45"/>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Width="20" Height="2" Fill="White" HorizontalAlignment="Center" VerticalAlignment="Center" RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<RotateTransform Angle="-45"/>
</Rectangle.RenderTransform>
</Rectangle>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="bd" Property="Background" Value="#22ffffff"></Setter>
</Trigger>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="grid" Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)"
Duration="00:00:00.3" To="180"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="grid" Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)"
Duration="00:00:00.3" To="0"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="Button_Min" TargetType="Button">
<Setter Property="FocusVisualStyle" Value="{x:Null}"></Setter>
<Setter Property="Width" Value="50"></Setter>
<Setter Property="Height" Value="40"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="bd" BorderBrush="Transparent" BorderThickness="1" Background="Transparent">
<Grid x:Name="grid" HorizontalAlignment="Center" VerticalAlignment="Center" Width="20" Height="20" RenderTransformOrigin="0.5,0.5">
<Grid.RenderTransform>
<RotateTransform Angle="0"/>
</Grid.RenderTransform>
<Rectangle Width="20" Height="2" Fill="White" HorizontalAlignment="Center" VerticalAlignment="Center" RenderTransformOrigin="0.5,0.5"></Rectangle>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="bd" Property="Background" Value="#22ffffff"></Setter>
</Trigger>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="grid" Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)"
Duration="00:00:00.3" To="180"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="grid" Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)"
Duration="00:00:00.3" To="0"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="Button_Setting" TargetType="Button">
<Setter Property="FocusVisualStyle" Value="{x:Null}"></Setter>
<Setter Property="Width" Value="50"></Setter>
<Setter Property="Height" Value="40"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="bd" BorderBrush="Transparent" BorderThickness="1" Background="Transparent">
<Grid x:Name="grid" HorizontalAlignment="Center" VerticalAlignment="Center" Width="20" Height="20" RenderTransformOrigin="0.5,0.5">
<Grid.RenderTransform>
<RotateTransform Angle="0"/>
</Grid.RenderTransform>
<Image Width="20" Height="20" HorizontalAlignment="Center" VerticalAlignment="Center"
Source="/VIZ.Framework.Common.Resource;component/Icons/setting_32x32.png"></Image>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="bd" Property="Background" Value="#22ffffff"></Setter>
</Trigger>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="grid" Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)"
Duration="00:00:00.3" To="180"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="grid" Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)"
Duration="00:00:00.3" To="0"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
\ No newline at end of file
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="ListBoxItem" x:Key="ListBoxItem_None">
<Setter Property="FocusVisualStyle" Value="{x:Null}"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border Background="Transparent">
<ContentPresenter HorizontalAlignment="Stretch" VerticalAlignment="Stretch"></ContentPresenter>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.7"></Setter>
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="ListBoxItem" x:Key="ListBoxItem_None_IsSelected">
<Setter Property="FocusVisualStyle" Value="{x:Null}"></Setter>
<Setter Property="IsSelected" Value="{Binding Path=IsSelected,Mode=TwoWay}"></Setter>
<Setter Property="Background" Value="#660000ff"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Grid>
<Border x:Name="bd" Opacity="0" Background="{TemplateBinding Background}"></Border>
<Border x:Name="bdSelected" Opacity="0" Background="{TemplateBinding Background}"></Border>
<ContentPresenter HorizontalAlignment="Stretch" VerticalAlignment="Stretch"></ContentPresenter>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="bd" Property="Opacity" Value="0.2"></Setter>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="bdSelected" Property="Opacity" Value="0.2"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.7"></Setter>
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="ListBox" x:Key="ListBox_None">
<Setter Property="FocusVisualStyle" Value="{x:Null}"></Setter>
<Setter Property="ItemContainerStyle" Value="{StaticResource ListBoxItem_None}"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBox">
<ScrollViewer Focusable="false" Padding="{TemplateBinding Padding}">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</ScrollViewer>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.7"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
\ No newline at end of file
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:core="clr-namespace:VIZ.Framework.Core;assembly=VIZ.Framework.Core"
xmlns:common="clr-namespace:VIZ.Framework.Common;assembly=VIZ.Framework.Common">
<core:Bool2VisibilityConverter x:Key="Bool2VisibilityConverter" TrueVisibility="Visible" FalseVisibility="Collapsed"></core:Bool2VisibilityConverter>
<Style TargetType="RepeatButton" x:Key="NumberBox_Up">
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RepeatButton">
<Border x:Name="border" Background="Transparent">
<Image Source="/VIZ.Framework.Common.Resource;component/Icons/triangle_up_16x16.png" Width="6" Height="13"
VerticalAlignment="Bottom" HorizontalAlignment="Center" Margin="0,0,0,5"></Image>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="Background" Value="#22FFFFFF"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="RepeatButton" x:Key="NumberBox_Down">
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RepeatButton">
<Border x:Name="border" Background="Transparent">
<Image Source="/VIZ.Framework.Common.Resource;component/Icons/triangle_down_16x16.png" Width="6" Height="13"
VerticalAlignment="Top" HorizontalAlignment="Center" Margin="0,5,0,0"></Image>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="Background" Value="#22FFFFFF"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="common:NumberBox">
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Background" Value="#ff0e131e"/>
<Setter Property="BorderBrush" Value="#ff3d4758"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="16"></Setter>
<Setter Property="BorderThickness" Value="0,0,0,1"/>
<Setter Property="VerticalContentAlignment" Value="Center"></Setter>
<Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="AllowDrop" Value="true"/>
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="common:NumberBox">
<Border x:Name="border" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" SnapsToDevicePixels="True">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<ScrollViewer x:Name="PART_ContentHost" Focusable="false" HorizontalScrollBarVisibility="Hidden" Margin="5,0,5,0"
VerticalScrollBarVisibility="Hidden" VerticalContentAlignment="Center" Grid.RowSpan="2"/>
<RepeatButton x:Name="PART_Up" Style="{StaticResource NumberBox_Up}" Width="30"
Visibility="{TemplateBinding IsShowUpAndDownButton,Converter={StaticResource Bool2VisibilityConverter}}"
Grid.Column="1" Grid.Row="0" Content="Up"></RepeatButton>
<RepeatButton x:Name="PART_Down" Style="{StaticResource NumberBox_Down}" Width="30"
Visibility="{TemplateBinding IsShowUpAndDownButton,Converter={StaticResource Bool2VisibilityConverter}}"
Grid.Column="1" Grid.Row="1" Content="Down"></RepeatButton>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Opacity" TargetName="border" Value="0.56"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
\ No newline at end of file
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="TextBox_None" TargetType="{x:Type TextBox}">
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="AllowDrop" Value="true"/>
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Border x:Name="border" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" SnapsToDevicePixels="True">
<ScrollViewer x:Name="PART_ContentHost" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Opacity" TargetName="border" Value="0.56"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
\ No newline at end of file
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
</ResourceDictionary>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{76EF480A-E486-41B7-B7A5-2A849FC8D5BF}</ProjectGuid>
<OutputType>library</OutputType>
<RootNamespace>VIZ.Framework.Common.Resource</RootNamespace>
<AssemblyName>VIZ.Framework.Common.Resource</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<LangVersion>7.3</LangVersion>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<LangVersion>7.3</LangVersion>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
</ItemGroup>
<ItemGroup>
<Page Include="Style\Button\Button_MessageBox.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Style\Button\Button_WindowTop.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Style\ListBox\ListBox_None.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Style\NumberBox\NumberBox.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Style\ScrollView\ScrollView_Default.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Style\TextBox\TextBox_None.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Themes\Generic.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<None Include="app.config" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<Resource Include="Icons\delay_32x32.png" />
</ItemGroup>
<ItemGroup>
<Folder Include="Images\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\VIZ.Framework.Common\VIZ.Framework.Common.csproj">
<Project>{92834c05-703e-4f05-9224-f36220939d8f}</Project>
<Name>VIZ.Framework.Common</Name>
</ProjectReference>
<ProjectReference Include="..\VIZ.Framework.Core\VIZ.Framework.Core.csproj">
<Project>{75b39591-4bc3-4b09-bd7d-ec9f67efa96e}</Project>
<Name>VIZ.Framework.Core</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Resource Include="Icons\triangle_up_16x16.png" />
</ItemGroup>
<ItemGroup>
<Resource Include="Icons\triangle_down_16x16.png" />
</ItemGroup>
<ItemGroup>
<Resource Include="Icons\setting_32x32.png" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<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>
\ No newline at end of file
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;
using VIZ.Framework.Core;
namespace VIZ.Framework.Common
{
/// <summary>
/// 消息框
/// </summary>
[TemplatePart(Name = nameof(PART_YesButton), Type = typeof(Button))]
[TemplatePart(Name = nameof(PART_CancelButton), Type = typeof(Button))]
public class MessageBoxEx : ContentControl
{
static MessageBoxEx()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MessageBoxEx), new FrameworkPropertyMetadata(typeof(MessageBoxEx)));
}
#region PART
/// <summary>
/// 确定按钮
/// </summary>
private Button PART_YesButton;
/// <summary>
/// 取消按钮
/// </summary>
private Button PART_CancelButton;
#endregion
#region Buttons -- 按钮组合
/// <summary>
/// 按钮组合
/// </summary>
public MessageBoxExButtons Buttons
{
get { return (MessageBoxExButtons)GetValue(ButtonsProperty); }
set { SetValue(ButtonsProperty, value); }
}
/// <summary>
/// Using a DependencyProperty as the backing store for Buttons. This enables animation, styling, binding, etc...
/// </summary>
public static readonly DependencyProperty ButtonsProperty =
DependencyProperty.Register("Buttons", typeof(MessageBoxExButtons), typeof(MessageBoxEx), new PropertyMetadata(MessageBoxExButtons.YES));
#endregion
#region Title -- 标题
/// <summary>
/// 标题
/// </summary>
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
/// <summary>
/// Using a DependencyProperty as the backing store for Title. This enables animation, styling, binding, etc...
/// </summary>
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(MessageBoxEx), new PropertyMetadata(null));
#endregion
#region Message -- 消息
/// <summary>
/// 消息
/// </summary>
public string Message
{
get { return (string)GetValue(MessageProperty); }
set { SetValue(MessageProperty, value); }
}
/// <summary>
/// Using a DependencyProperty as the backing store for Message. This enables animation, styling, binding, etc...
/// </summary>
public static readonly DependencyProperty MessageProperty =
DependencyProperty.Register("Message", typeof(string), typeof(MessageBoxEx), new PropertyMetadata(null));
#endregion
#region Result -- 返回值
/// <summary>
/// 返回值
/// </summary>
public MessageBoxExResult Result
{
get { return (MessageBoxExResult)GetValue(ResultProperty); }
set { SetValue(ResultProperty, value); }
}
/// <summary>
/// Using a DependencyProperty as the backing store for Result. This enables animation, styling, binding, etc...
/// </summary>
public static readonly DependencyProperty ResultProperty =
DependencyProperty.Register("Result", typeof(MessageBoxExResult), typeof(MessageBoxEx), new PropertyMetadata(MessageBoxExResult.YES));
#endregion
/// <summary>
/// 创建模板时触发
/// </summary>
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this.PART_YesButton = this.Template.FindName(nameof(PART_YesButton), this) as Button;
this.PART_CancelButton = this.Template.FindName(nameof(PART_CancelButton), this) as Button;
if (this.PART_YesButton != null)
{
this.PART_YesButton.Click -= PART_YesButton_Click;
this.PART_YesButton.Click += PART_YesButton_Click;
}
if (this.PART_CancelButton != null)
{
this.PART_CancelButton.Click -= PART_CancelButton_Click;
this.PART_CancelButton.Click += PART_CancelButton_Click;
}
}
/// <summary>
/// 点击取消按钮
/// </summary>
private void PART_CancelButton_Click(object sender, RoutedEventArgs e)
{
this.Result = MessageBoxExResult.CANCEL;
Window window = WPFHelper.GetAncestorByType<Window>(this);
window?.Close();
}
/// <summary>
/// 点击确定按钮
/// </summary>
private void PART_YesButton_Click(object sender, RoutedEventArgs e)
{
this.Result = MessageBoxExResult.YES;
Window window = WPFHelper.GetAncestorByType<Window>(this);
window?.Close();
}
/// <summary>
/// 显示提示框(线程安全)
/// </summary>
/// <param name="title">标题</param>
/// <param name="message">消息</param>
/// <param name="buttons">按钮组</param>
/// <param name="ownerWindow">所属窗口</param>
/// <returns>返回值</returns>
public static MessageBoxExResult ShowDialog(string title, string message, MessageBoxExButtons buttons, Window ownerWindow)
{
MessageBoxExResult result = MessageBoxExResult.YES;
WPFHelper.Invoke(() =>
{
MessageBoxExWindow window = new MessageBoxExWindow();
window.box.Title = title;
window.box.Message = message;
window.box.Buttons = buttons;
window.Owner = ownerWindow;
window.ShowDialog();
result = window.box.Result;
});
return result;
}
/// <summary>
/// 显示提示框(线程安全)
/// </summary>
/// <param name="title">标题</param>
/// <param name="message">消息</param>
/// <param name="buttons">按钮组</param>
/// <returns>返回值</returns>
public static MessageBoxExResult ShowDialog(string title, string message, MessageBoxExButtons buttons)
{
MessageBoxExResult result = MessageBoxExResult.YES;
WPFHelper.Invoke(() =>
{
MessageBoxExWindow window = new MessageBoxExWindow();
window.box.Title = title;
window.box.Message = message;
window.box.Buttons = buttons;
window.Owner = Application.Current?.MainWindow;
window.ShowDialog();
result = window.box.Result;
});
return result;
}
/// <summary>
/// 显示提示框(线程安全)
/// </summary>
/// <param name="title">标题</param>
/// <param name="message">消息</param>
/// <returns>返回值</returns>
public static MessageBoxExResult ShowDialog(string title, string message)
{
return ShowDialog(title, message, MessageBoxExButtons.YES);
}
/// <summary>
/// 显示提示框(线程安全)
/// </summary>
/// <param name="message">消息</param>
/// <returns>返回值</returns>
public static MessageBoxExResult ShowDialog(string message)
{
return ShowDialog("提示", message);
}
/// <summary>
/// 显示提示框(线程安全)
/// </summary>
/// <param name="message">消息</param>
/// <param name="ownerWindow">所属窗口</param>
/// <returns>返回值</returns>
public static MessageBoxExResult ShowDialog(string message, Window ownerWindow)
{
return ShowDialog("提示", message, MessageBoxExButtons.YES, ownerWindow);
}
}
}
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:common="clr-namespace:VIZ.Framework.Common">
<Style x:Key="VMessageBox_Button_Yes" TargetType="Button">
<Setter Property="FocusVisualStyle" Value="{x:Null}"></Setter>
<Setter Property="Width" Value="140"></Setter>
<Setter Property="Height" Value="45"></Setter>
<Setter Property="Margin" Value="10,0,10,0"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="bd" CornerRadius="10" Background="#aa007cff">
<TextBlock Text="确定" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="18" Foreground="White"></TextBlock>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="bd" Property="Background" Value="#ff007cff"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="VMessageBox_Button_Cancel" TargetType="Button">
<Setter Property="FocusVisualStyle" Value="{x:Null}"></Setter>
<Setter Property="Width" Value="140"></Setter>
<Setter Property="Height" Value="45"></Setter>
<Setter Property="Margin" Value="10,0,10,0"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="bd" CornerRadius="10" Background="#aadcdcdc">
<TextBlock Text="取消" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="18" Foreground="Black"></TextBlock>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="bd" Property="Background" Value="#ffdcdcdc"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="common:MessageBoxEx">
<Setter Property="FocusVisualStyle" Value="{x:Null}"></Setter>
<Setter Property="Width" Value="400"></Setter>
<Setter Property="Height" Value="300"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="common:MessageBoxEx">
<Border Background="White" BorderBrush="#22000000" BorderThickness="1" CornerRadius="40">
<Border.Effect>
<DropShadowEffect Opacity="0.36"/>
</Border.Effect>
<Grid Margin="30,20,30,20">
<Grid.RowDefinitions>
<RowDefinition Height="80"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="80"></RowDefinition>
</Grid.RowDefinitions>
<common:DragWindowBar Grid.RowSpan="3" Background="Transparent"></common:DragWindowBar>
<TextBlock Text="{TemplateBinding Title}" Foreground="Black" FontSize="40" VerticalAlignment="Center" HorizontalAlignment="Left"
FontWeight="Black"></TextBlock>
<TextBlock Grid.Row="1" Text="{TemplateBinding Message}" Foreground="Black" FontSize="24" TextWrapping="Wrap" TextTrimming="CharacterEllipsis"></TextBlock>
<StackPanel Grid.Row="2" VerticalAlignment="Center" HorizontalAlignment="Right" Orientation="Horizontal">
<Button x:Name="PART_CancelButton" Grid.Row="2" Style="{StaticResource VMessageBox_Button_Cancel}" Content="取消" Visibility="Hidden"></Button>
<Button x:Name="PART_YesButton" Grid.Row="2" Style="{StaticResource VMessageBox_Button_Yes}" Content="确定"></Button>
</StackPanel>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="Buttons" Value="YES_CANCEL">
<Setter TargetName="PART_CancelButton" Property="Visibility" Value="Visible"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VIZ.Framework.Common
{
/// <summary>
/// 消息框按钮组
/// </summary>
public enum MessageBoxExButtons
{
/// <summary>
/// 确定
/// </summary>
YES,
/// <summary>
/// 确定 & 取消
/// </summary>
YES_CANCEL
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VIZ.Framework.Common
{
/// <summary>
/// 消息框返回
/// </summary>
public enum MessageBoxExResult
{
/// <summary>
/// 确定
/// </summary>
YES,
/// <summary>
/// <summary>
/// 取消
/// </summary>
CANCEL
}
}
<Window x:Class="VIZ.Framework.Common.MessageBoxExWindow"
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:VIZ.Framework.Common"
mc:Ignorable="d" WindowStartupLocation="CenterScreen" WindowState="Normal" WindowStyle="None" AllowsTransparency="True"
Title="VMessageBoxWindow" Height="400" Width="600" Background="Transparent">
<local:MessageBoxEx x:Name="box" Width="580" Height="380"></local:MessageBoxEx>
</Window>
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 VIZ.Framework.Common
{
/// <summary>
/// MessageBoxExWindow.xaml 的交互逻辑
/// </summary>
public partial class MessageBoxExWindow : Window
{
public MessageBoxExWindow()
{
InitializeComponent();
}
}
}
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("VIZ.Framework.Common")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("VIZ.Framework.Common")]
[assembly: AssemblyCopyright("Copyright © 2022")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
//将 ComVisible 设置为 false 将使此程序集中的类型
//对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型,
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
//若要开始生成可本地化的应用程序,请设置
//.csproj 文件中的 <UICulture>CultureYouAreCodingWith</UICulture>
//例如,如果您在源文件中使用的是美国英语,
//使用的是美国英语,请将 <UICulture> 设置为 en-US。 然后取消
//对以下 NeutralResourceLanguage 特性的注释。 更新
//以下行中的“en-US”以匹配项目文件中的 UICulture 设置。
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly:ThemeInfo(
ResourceDictionaryLocation.None, //主题特定资源词典所处位置
//(未在页面中找到资源时使用,
//或应用程序资源字典中找到时使用)
ResourceDictionaryLocation.SourceAssembly //常规资源词典所处位置
//(未在页面中找到资源时使用,
//、应用程序或任何主题专用资源字典中找到时使用)
)]
// 程序集的版本信息由下列四个值组成:
//
// 主版本
// 次版本
// 生成号
// 修订号
//
//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
//通过使用 "*",如下所示:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本: 4.0.30319.42000
//
// 对此文件的更改可能导致不正确的行为,如果
// 重新生成代码,则所做更改将丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace VIZ.Framework.Common.Properties {
/// <summary>
/// 强类型资源类,用于查找本地化字符串等。
/// </summary>
// 此类是由 StronglyTypedResourceBuilder
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
// (以 /str 作为命令选项),或重新生成 VS 项目。
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// 返回此类使用的缓存 ResourceManager 实例。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if ((resourceMan == null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("VIZ.Framework.Common.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// 重写当前线程的 CurrentUICulture 属性,对
/// 使用此强类型资源类的所有资源查找执行重写。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
\ No newline at end of file
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace VIZ.Framework.Common.Properties
{
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
{
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default
{
get
{
return defaultInstance;
}
}
}
}
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>
\ No newline at end of file
using System;
namespace VIZ.Framework.Common
{
public static class Disposer
{
public static void SafeDispose<T>(ref T resource) where T : class
{
if (resource == null)
{
return;
}
var disposer = resource as IDisposable;
if (disposer != null)
{
try
{
disposer.Dispose();
}
catch
{
}
}
resource = null;
}
}
}
using System;
using System.Runtime.InteropServices;
namespace VIZ.Framework.Common
{
public static class NativeMethods
{
[DllImport("user32.dll", SetLastError = false)]
public static extern IntPtr GetDesktopWindow();
}
}
using SharpDX.Direct2D1;
using System;
using System.Collections.Generic;
namespace VIZ.Framework.Common
{
public class ResourceCache
{
// - field -----------------------------------------------------------------------
private Dictionary<string, Func<RenderTarget, object>> generators = new Dictionary<string, Func<RenderTarget, object>>();
private Dictionary<string, object> resources = new Dictionary<string, object>();
private RenderTarget renderTarget = null;
// - property --------------------------------------------------------------------
public RenderTarget RenderTarget
{
get { return renderTarget; }
set { renderTarget = value; UpdateResources(); }
}
public int Count
{
get { return resources.Count; }
}
public object this[string key]
{
get { return resources[key]; }
}
public Dictionary<string, object>.KeyCollection Keys
{
get { return resources.Keys; }
}
public Dictionary<string, object>.ValueCollection Values
{
get { return resources.Values; }
}
// - public methods --------------------------------------------------------------
public void Add(string key, Func<RenderTarget, object> gen)
{
object resOld;
if (resources.TryGetValue(key, out resOld))
{
Disposer.SafeDispose(ref resOld);
generators.Remove(key);
resources.Remove(key);
}
if (renderTarget == null)
{
generators.Add(key, gen);
resources.Add(key, null);
}
else
{
var res = gen(renderTarget);
generators.Add(key, gen);
resources.Add(key, res);
}
}
public void Clear()
{
foreach (var key in resources.Keys)
{
var res = resources[key];
Disposer.SafeDispose(ref res);
}
generators.Clear();
resources.Clear();
}
public bool ContainsKey(string key)
{
return resources.ContainsKey(key);
}
public bool ContainsValue(object val)
{
return resources.ContainsValue(val);
}
public Dictionary<string, object>.Enumerator GetEnumerator()
{
return resources.GetEnumerator();
}
public bool Remove(string key)
{
object res;
if (resources.TryGetValue(key, out res))
{
Disposer.SafeDispose(ref res);
generators.Remove(key);
resources.Remove(key);
return true;
}
else
{
return false;
}
}
public bool TryGetValue(string key, out object res)
{
return resources.TryGetValue(key, out res);
}
// - private methods -------------------------------------------------------------
private void UpdateResources()
{
if (renderTarget == null) { return; }
foreach (var g in generators)
{
var key = g.Key;
var gen = g.Value;
var res = gen(renderTarget);
object resOld;
if (resources.TryGetValue(key, out resOld))
{
Disposer.SafeDispose(ref resOld);
resources.Remove(key);
}
resources.Add(key, res);
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VIZ.Framework.Common
{
/// <summary>
/// 图片渲染格式
/// </summary>
public enum WicBitmapFormat
{
/// <summary>
/// jpg格式
/// </summary>
JPG
}
}
using SharpDX.WIC;
using SharpDX.Direct2D1;
using SharpDX.DXGI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SharpDX;
using System.IO;
using SharpDX.IO;
namespace VIZ.Framework.Common
{
/// <summary>
/// WIC图片辅助类
/// </summary>
public static class WicBitmapHelper
{
/// <summary>
/// 渲染至WicBitmap
/// </summary>
/// <param name="width">宽度</param>
/// <param name="height">高度</param>
/// <param name="render">渲染方法</param>
/// <param name="plugins">渲染插件</param>
/// <returns>WicBitmap</returns>
public static SharpDX.WIC.Bitmap RenderToBitmap(int width, int height, Action<VideoRenderContext> render, List<string> plugins = null)
{
ImagingFactory wicFactory = new ImagingFactory();
SharpDX.Direct2D1.Factory d2dFactory = new SharpDX.Direct2D1.Factory();
SharpDX.WIC.Bitmap wicBitmap = new SharpDX.WIC.Bitmap(wicFactory, width, height, SharpDX.WIC.PixelFormat.Format32bppBGR, BitmapCreateCacheOption.CacheOnLoad);
RenderTargetProperties renderTargetProperties = new RenderTargetProperties(RenderTargetType.Default, new SharpDX.Direct2D1.PixelFormat(Format.Unknown, SharpDX.Direct2D1.AlphaMode.Unknown), 0, 0, RenderTargetUsage.None, FeatureLevel.Level_DEFAULT);
WicRenderTarget d2dRenderTarget = new WicRenderTarget(d2dFactory, wicBitmap, renderTargetProperties);
d2dRenderTarget.BeginDraw();
VideoRenderContext context = new VideoRenderContext();
context.Mode = VideoRenderMode.Image;
context.Target = d2dRenderTarget;
context.RenderImagePlugins = plugins;
render.Invoke(context);
d2dRenderTarget.EndDraw();
d2dRenderTarget.Dispose();
d2dFactory.Dispose();
wicFactory.Dispose();
return wicBitmap;
}
/// <summary>
/// 渲染至文件
/// </summary>
/// <param name="width">宽度</param>
/// <param name="height">高度</param>
/// <param name="fileName">文件名</param>
/// <param name="format">图片格式</param>
/// <param name="render">渲染行为</param>
public static void RenderToFile(int width, int height, string fileName, WicBitmapFormat format, Action<WicRenderTarget> render)
{
ImagingFactory wicFactory = new ImagingFactory();
SharpDX.Direct2D1.Factory d2dFactory = new SharpDX.Direct2D1.Factory();
SharpDX.WIC.Bitmap wicBitmap = new SharpDX.WIC.Bitmap(wicFactory, width, height, SharpDX.WIC.PixelFormat.Format32bppBGR, BitmapCreateCacheOption.CacheOnLoad);
RenderTargetProperties renderTargetProperties = new RenderTargetProperties(RenderTargetType.Default, new SharpDX.Direct2D1.PixelFormat(Format.Unknown, SharpDX.Direct2D1.AlphaMode.Unknown), 0, 0, RenderTargetUsage.None, FeatureLevel.Level_DEFAULT);
WicRenderTarget d2dRenderTarget = new WicRenderTarget(d2dFactory, wicBitmap, renderTargetProperties);
d2dRenderTarget.BeginDraw();
render.Invoke(d2dRenderTarget);
d2dRenderTarget.EndDraw();
if (File.Exists(fileName))
{
File.Delete(fileName);
}
WICStream stream = new WICStream(wicFactory, fileName, NativeFileAccess.Write);
BitmapEncoder encoder = GetBitmapFrameEncoder(wicFactory, format);
encoder.Initialize(stream);
// Create a Frame encoder
BitmapFrameEncode bitmapFrameEncode = new BitmapFrameEncode(encoder);
bitmapFrameEncode.Initialize();
bitmapFrameEncode.SetSize(width, height);
var pixelFormatGuid = SharpDX.WIC.PixelFormat.FormatDontCare;
bitmapFrameEncode.SetPixelFormat(ref pixelFormatGuid);
bitmapFrameEncode.WriteSource(wicBitmap);
bitmapFrameEncode.Commit();
encoder.Commit();
bitmapFrameEncode.Dispose();
encoder.Dispose();
stream.Dispose();
d2dRenderTarget.Dispose();
wicBitmap.Dispose();
d2dFactory.Dispose();
wicFactory.Dispose();
}
/// <summary>
/// 获取保存图片格式
/// </summary>
/// <param name="factory">图片工厂</param>
/// <param name="format">图片格式</param>
/// <returns>图片格式</returns>
private static BitmapEncoder GetBitmapFrameEncoder(ImagingFactory factory, WicBitmapFormat format)
{
switch (format)
{
case WicBitmapFormat.JPG:
return new JpegBitmapEncoder(factory);
default:
return null;
}
}
}
}
using SharpDX;
using SharpDX.Direct2D1;
using SharpDX.Direct3D;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows;
using System.Windows.Threading;
namespace VIZ.Framework.Common
{
public abstract class D2dControl : System.Windows.Controls.Image
{
// - field -----------------------------------------------------------------------
private SharpDX.Direct3D11.Device device;
private Texture2D renderTarget;
private Dx11ImageSource d3DSurface;
private RenderTarget d2DRenderTarget;
private SharpDX.Direct2D1.Factory d2DFactory;
private readonly Stopwatch renderTimer = new Stopwatch();
protected ResourceCache resCache = new ResourceCache();
private long lastFrameTime = 0;
private long lastRenderTime = 0;
private int frameCount = 0;
private int frameCountHistTotal = 0;
private Queue<int> frameCountHist = new Queue<int>();
// - property --------------------------------------------------------------------
public static bool IsInDesignMode
{
get
{
var prop = DesignerProperties.IsInDesignModeProperty;
var isDesignMode = (bool)DependencyPropertyDescriptor.FromProperty(prop, typeof(FrameworkElement)).Metadata.DefaultValue;
return isDesignMode;
}
}
private static readonly DependencyPropertyKey FpsPropertyKey = DependencyProperty.RegisterReadOnly(
"Fps",
typeof(int),
typeof(D2dControl),
new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.None)
);
public static readonly DependencyProperty FpsProperty = FpsPropertyKey.DependencyProperty;
public int Fps
{
get { return (int)GetValue(FpsProperty); }
protected set { SetValue(FpsPropertyKey, value); }
}
public static DependencyProperty RenderWaitProperty = DependencyProperty.Register(
"RenderWait",
typeof(int),
typeof(D2dControl),
new FrameworkPropertyMetadata(2, OnRenderWaitChanged)
);
public int RenderWait
{
get { return (int)GetValue(RenderWaitProperty); }
set { SetValue(RenderWaitProperty, value); }
}
// - public methods --------------------------------------------------------------
public D2dControl()
{
base.Loaded += Window_Loaded;
base.Unloaded += Window_Closing;
base.Stretch = System.Windows.Media.Stretch.Fill;
}
public abstract void Render(RenderTarget target);
// - event handler ---------------------------------------------------------------
private void Window_Loaded(object sender, RoutedEventArgs e)
{
if (D2dControl.IsInDesignMode)
{
return;
}
StartD3D();
StartRendering();
}
private void Window_Closing(object sender, RoutedEventArgs e)
{
if (D2dControl.IsInDesignMode)
{
return;
}
StopRendering();
EndD3D();
}
private void OnRendering(object sender, EventArgs e)
{
if (!renderTimer.IsRunning)
{
return;
}
PrepareAndCallRender();
d3DSurface.InvalidateD3DImage();
lastRenderTime = renderTimer.ElapsedMilliseconds;
}
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
CreateAndBindTargets();
base.OnRenderSizeChanged(sizeInfo);
}
private void OnIsFrontBufferAvailableChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (d3DSurface.IsFrontBufferAvailable)
{
StartRendering();
this.CreateAndBindTargets();
}
else
{
StopRendering();
}
}
private static void OnRenderWaitChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = (D2dControl)d;
control.d3DSurface.RenderWait = (int)e.NewValue;
}
// - private methods -------------------------------------------------------------
private void StartD3D()
{
device = new SharpDX.Direct3D11.Device(DriverType.Hardware, DeviceCreationFlags.BgraSupport);
d3DSurface = new Dx11ImageSource();
d3DSurface.IsFrontBufferAvailableChanged += OnIsFrontBufferAvailableChanged;
CreateAndBindTargets();
base.Source = d3DSurface;
}
private void EndD3D()
{
d3DSurface.IsFrontBufferAvailableChanged -= OnIsFrontBufferAvailableChanged;
base.Source = null;
Disposer.SafeDispose(ref d2DRenderTarget);
Disposer.SafeDispose(ref d2DFactory);
Disposer.SafeDispose(ref d3DSurface);
Disposer.SafeDispose(ref renderTarget);
Disposer.SafeDispose(ref device);
}
private void CreateAndBindTargets()
{
if (d3DSurface == null)
{
return;
}
d3DSurface.SetRenderTarget(null);
Disposer.SafeDispose(ref d2DRenderTarget);
Disposer.SafeDispose(ref d2DFactory);
Disposer.SafeDispose(ref renderTarget);
var width = Math.Max((int)ActualWidth, 100);
var height = Math.Max((int)ActualHeight, 100);
var renderDesc = new Texture2DDescription
{
BindFlags = BindFlags.RenderTarget | BindFlags.ShaderResource,
Format = Format.B8G8R8A8_UNorm,
Width = width,
Height = height,
MipLevels = 1,
SampleDescription = new SampleDescription(1, 0),
Usage = ResourceUsage.Default,
OptionFlags = ResourceOptionFlags.Shared,
CpuAccessFlags = CpuAccessFlags.None,
ArraySize = 1
};
renderTarget = new Texture2D(device, renderDesc);
var surface = renderTarget.QueryInterface<Surface>();
d2DFactory = new SharpDX.Direct2D1.Factory();
var rtp = new RenderTargetProperties(new PixelFormat(Format.Unknown, SharpDX.Direct2D1.AlphaMode.Premultiplied));
d2DRenderTarget = new RenderTarget(d2DFactory, surface, rtp);
resCache.RenderTarget = d2DRenderTarget;
d3DSurface.SetRenderTarget(renderTarget);
device.ImmediateContext.Rasterizer.SetViewport(0, 0, width, height, 0.0f, 1.0f);
}
private void StartRendering()
{
if (renderTimer.IsRunning)
{
return;
}
System.Windows.Media.CompositionTarget.Rendering -= OnRendering;
System.Windows.Media.CompositionTarget.Rendering += OnRendering;
renderTimer.Start();
}
private void StopRendering()
{
if (!renderTimer.IsRunning)
{
return;
}
System.Windows.Media.CompositionTarget.Rendering -= OnRendering;
renderTimer.Stop();
}
private void PrepareAndCallRender()
{
if (device == null)
{
return;
}
d2DRenderTarget.BeginDraw();
Render(d2DRenderTarget);
d2DRenderTarget.EndDraw();
CalcFps();
device.ImmediateContext.Flush();
}
private void CalcFps()
{
frameCount++;
if (renderTimer.ElapsedMilliseconds - lastFrameTime > 1000)
{
frameCountHist.Enqueue(frameCount);
frameCountHistTotal += frameCount;
if (frameCountHist.Count > 5)
{
frameCountHistTotal -= frameCountHist.Dequeue();
}
Fps = frameCountHistTotal / frameCountHist.Count;
frameCount = 0;
lastFrameTime = renderTimer.ElapsedMilliseconds;
}
}
}
}
using SharpDX.Direct3D9;
using System;
using System.Threading;
using System.Windows.Interop;
using VIZ.Framework.Core;
using VIZ.Framework.Storage;
using VIZ.Framework.Domain;
namespace VIZ.Framework.Common
{
public class Dx11ImageSource : D3DImage, IDisposable
{
// - field -----------------------------------------------------------------------
private static int ActiveClients;
private static Direct3DEx D3DContext;
private static DeviceEx D3DDevice;
private Texture renderTarget;
// - property --------------------------------------------------------------------
public int RenderWait { get; set; } = 5; // default: 5ms
// - public methods --------------------------------------------------------------
public Dx11ImageSource()
{
this.RenderWait = ApplicationDomain.IniStorage.GetValue<VideoConfig, int>(p => p.VIDEO_RENDER_WAIT);
StartD3D();
Dx11ImageSource.ActiveClients++;
}
public void Dispose()
{
SetRenderTarget(null);
Disposer.SafeDispose(ref renderTarget);
Dx11ImageSource.ActiveClients--;
EndD3D();
}
public void InvalidateD3DImage()
{
if (renderTarget != null)
{
base.Lock();
if (RenderWait != 0)
{
Thread.Sleep(RenderWait);
}
base.AddDirtyRect(new System.Windows.Int32Rect(0, 0, base.PixelWidth, base.PixelHeight));
base.Unlock();
}
}
public void SetRenderTarget(SharpDX.Direct3D11.Texture2D target)
{
if (renderTarget != null)
{
renderTarget = null;
base.Lock();
base.SetBackBuffer(D3DResourceType.IDirect3DSurface9, IntPtr.Zero);
base.Unlock();
}
if (target == null)
{
return;
}
var format = Dx11ImageSource.TranslateFormat(target);
var handle = GetSharedHandle(target);
if (!IsShareable(target))
{
throw new ArgumentException("Texture must be created with ResouceOptionFlags.Shared");
}
if (format == Format.Unknown)
{
throw new ArgumentException("Texture format is not compatible with OpenSharedResouce");
}
if (handle == IntPtr.Zero)
{
throw new ArgumentException("Invalid handle");
}
renderTarget = new Texture(Dx11ImageSource.D3DDevice, target.Description.Width, target.Description.Height, 1, Usage.RenderTarget, format, Pool.Default, ref handle);
using (var surface = renderTarget.GetSurfaceLevel(0))
{
base.Lock();
base.SetBackBuffer(D3DResourceType.IDirect3DSurface9, surface.NativePointer);
base.Unlock();
}
}
// - private methods -------------------------------------------------------------
private void StartD3D()
{
if (Dx11ImageSource.ActiveClients != 0)
{
return;
}
var presentParams = GetPresentParameters();
var createFlags = CreateFlags.HardwareVertexProcessing | CreateFlags.Multithreaded | CreateFlags.FpuPreserve;
Dx11ImageSource.D3DContext = new Direct3DEx();
Dx11ImageSource.D3DDevice = new DeviceEx(D3DContext, 0, DeviceType.Hardware, IntPtr.Zero, createFlags, presentParams);
}
private void EndD3D()
{
if (Dx11ImageSource.ActiveClients != 0)
{
return;
}
Disposer.SafeDispose(ref renderTarget);
Disposer.SafeDispose(ref Dx11ImageSource.D3DDevice);
Disposer.SafeDispose(ref Dx11ImageSource.D3DContext);
}
private static void ResetD3D()
{
if (Dx11ImageSource.ActiveClients == 0)
{
return;
}
var presentParams = GetPresentParameters();
Dx11ImageSource.D3DDevice.ResetEx(ref presentParams);
}
private static PresentParameters GetPresentParameters()
{
var presentParams = new PresentParameters();
presentParams.Windowed = true;
presentParams.SwapEffect = SwapEffect.Discard;
presentParams.DeviceWindowHandle = NativeMethods.GetDesktopWindow();
presentParams.PresentationInterval = PresentInterval.Default;
return presentParams;
}
private IntPtr GetSharedHandle(SharpDX.Direct3D11.Texture2D texture)
{
using (var resource = texture.QueryInterface<SharpDX.DXGI.Resource>())
{
return resource.SharedHandle;
}
}
private static Format TranslateFormat(SharpDX.Direct3D11.Texture2D texture)
{
switch (texture.Description.Format)
{
case SharpDX.DXGI.Format.R10G10B10A2_UNorm: return SharpDX.Direct3D9.Format.A2B10G10R10;
case SharpDX.DXGI.Format.R16G16B16A16_Float: return SharpDX.Direct3D9.Format.A16B16G16R16F;
case SharpDX.DXGI.Format.B8G8R8A8_UNorm: return SharpDX.Direct3D9.Format.A8R8G8B8;
default: return SharpDX.Direct3D9.Format.Unknown;
}
}
private static bool IsShareable(SharpDX.Direct3D11.Texture2D texture)
{
return (texture.Description.OptionFlags & SharpDX.Direct3D11.ResourceOptionFlags.Shared) != 0;
}
}
}
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:VIZ.Framework.Common">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/VIZ.Framework.Common;component/MessageBox/MessageBoxEx.xaml"></ResourceDictionary>
<ResourceDictionary Source="/VIZ.Framework.Common;component/Widgets/VideoTimeBar/VideoTimeBar.xaml"></ResourceDictionary>
<ResourceDictionary Source="/VIZ.Framework.Common;component/Widgets/NavigationControl/NavigationControl.xaml"></ResourceDictionary>
<ResourceDictionary Source="/VIZ.Framework.Common;component/Widgets/NumberBox/NumberBox.xaml"></ResourceDictionary>
<ResourceDictionary Source="/VIZ.Framework.Common;component/Widgets/ColorPickButton/ColorPickButton.xaml"></ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
using SharpDX.Direct2D1;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VIZ.Framework.Common
{
/// <summary>
/// 视频插件
/// </summary>
public interface IVideoPlugin
{
/// <summary>
/// 插件名称
/// </summary>
string Name { get; }
/// <summary>
/// 视频控件
/// </summary>
VideoControl VideoControl { get; }
/// <summary>
/// 视频渲染
/// </summary>
VideoCustomRender VideoRender { get; }
/// <summary>
/// 是否启用
/// </summary>
bool IsEnabled { get; set; }
/// <summary>
/// 附加
/// </summary>
void Attach();
/// <summary>
/// 卸载
/// </summary>
void Detach();
/// <summary>
/// 渲染
/// </summary>
/// <param name="context">渲染上下文</param>
void Render(VideoRenderContext context);
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VIZ.Framework.Common
{
/// <summary>
/// 跟踪框扩展
/// </summary>
public static class ClipBoxExpand
{
/// <summary>
/// 更新跟踪框
/// </summary>
/// <param name="videoControl">视频控件</param>
/// <param name="clipBoxInfo">裁剪框信息</param>
public static void UpdateClipBox(this VideoControl videoControl, ClipBoxInfo clipBoxInfo)
{
if (videoControl.videoRender == null)
return;
ClipBoxPlugin plugin = videoControl.GetPlugin<ClipBoxPlugin>(VideoControlPluginNames.ClipBox);
if (plugin == null)
return;
plugin.Update(clipBoxInfo);
}
/// <summary>
/// 清理跟踪框
/// </summary>
/// <param name="videoControl">视频控件</param>
public static void ClearClipBox(this VideoControl videoControl)
{
if (videoControl.videoRender == null)
return;
ClipBoxPlugin plugin = videoControl.GetPlugin<ClipBoxPlugin>(VideoControlPluginNames.ClipBox);
if (plugin == null)
return;
plugin.Update(null);
}
}
}
using SharpDX.Direct2D1;
using SharpDX.Mathematics.Interop;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using VIZ.Framework.Core;
namespace VIZ.Framework.Common
{
/// <summary>
/// 裁剪框插件
/// </summary>
public class ClipBoxPlugin : VideoPluginBase
{
/// <summary>
/// 裁剪框插件
/// </summary>
/// <param name="videoControl">视频控件</param>
public ClipBoxPlugin(VideoControl videoControl) : base(videoControl)
{
}
/// <summary>
/// 名称
/// </summary>
public override string Name => VideoControlPluginNames.ClipBox;
/// <summary>
/// 跟踪框信息
/// </summary>
private volatile ClipBoxInfo clipBoxInfo;
/// <summary>
/// 更新数据
/// </summary>
/// <param name="clipBoxInfo">跟踪框信息</param>
public void Update(ClipBoxInfo clipBoxInfo)
{
this.clipBoxInfo = clipBoxInfo;
}
/// <summary>
/// 渲染
/// </summary>
/// <param name="context">渲染上下文</param>
public override void Render(VideoRenderContext context)
{
// 画面渲染区域
ClipBoxInfo info = this.clipBoxInfo;
if (info == null)
return;
float half = info.DrawingBorderWidth / 2f;
RawRectangleF drawRect;
RawRectangleF fillRect;
if (context.Mode == VideoRenderMode.UI)
{
drawRect = ImageHelper.ConvertImageRectToUiRect(context.VideoRenderInfo.Frame.Width, context.VideoRenderInfo.Frame.Height, context.VideoRenderInfo.DrawingRect, info.SrcRect.ToRect()).ToRawRectangleF();
fillRect = context.VideoRenderInfo.ControlRect.ToRawRectangleF();
}
else
{
drawRect = info.SrcRect;
fillRect = new RawRectangleF(0, 0, context.VideoRenderInfo.Frame.Width, context.VideoRenderInfo.Frame.Height);
}
drawRect = new RawRectangleF(drawRect.Left + half, drawRect.Top + half, drawRect.Right - half, drawRect.Bottom - half);
List<RawRectangleF> maskList = new List<RawRectangleF>();
// left
if (drawRect.Left > context.VideoRenderInfo.DrawingRect.Left + half)
{
maskList.Add(new RawRectangleF(
(float)context.VideoRenderInfo.DrawingRect.Left, (float)context.VideoRenderInfo.DrawingRect.Top,
drawRect.Left - half, (float)context.VideoRenderInfo.DrawingRect.Bottom));
}
// right
if (drawRect.Right < fillRect.Right - half)
{
maskList.Add(new RawRectangleF(
drawRect.Right + half, (float)context.VideoRenderInfo.DrawingRect.Top,
(float)context.VideoRenderInfo.DrawingRect.Right, (float)context.VideoRenderInfo.DrawingRect.Bottom));
}
// top
if (drawRect.Top > half)
{
maskList.Add(new RawRectangleF(
drawRect.Left - half, (float)context.VideoRenderInfo.DrawingRect.Top,
drawRect.Right + half, drawRect.Top - half));
}
// bottom
if (drawRect.Bottom < fillRect.Bottom - half)
{
maskList.Add(new RawRectangleF(
drawRect.Left - half, drawRect.Bottom + half,
drawRect.Right + half, (float)context.VideoRenderInfo.DrawingRect.Bottom));
}
// 绘制
SolidColorBrush maskBrush = new SolidColorBrush(context.Target, info.MaskColor);
foreach (RawRectangleF mask in maskList)
{
context.Target.FillRectangle(mask, maskBrush);
}
SolidColorBrush brush = new SolidColorBrush(context.Target, info.DrawingBorderColor);
context.Target.DrawRectangle(drawRect, brush, info.DrawingBorderWidth);
}
}
}
using SharpDX.Mathematics.Interop;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VIZ.Framework.Common
{
/// <summary>
/// 裁剪框信息
/// </summary>
public class ClipBoxInfo
{
/// <summary>
/// 源区域
/// </summary>
public RawRectangleF SrcRect { get; set; }
/// <summary>
/// 绘制边框颜色
/// </summary>
public RawColor4 DrawingBorderColor { get; set; }
/// <summary>
/// 绘制边框宽度
/// </summary>
public float DrawingBorderWidth { get; set; }
/// <summary>
/// 遮罩颜色
/// </summary>
public RawColor4 MaskColor { get; set; }
}
}
using SharpDX.Mathematics.Interop;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VIZ.Framework.Common
{
/// <summary>
/// 框选框选择事件参数
/// </summary>
public class SelectionBoxSelectedEventArgs : EventArgs
{
/// <summary>
/// 绘制区域
/// </summary>
public RawRectangleF DrawingRect { get; set; }
/// <summary>
/// 源区域
/// </summary>
public RawRectangleF SrcRect { get; set; }
}
}
using SharpDX.Mathematics.Interop;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace VIZ.Framework.Common
{
/// <summary>
/// 框选框信息
/// </summary>
public class SelectionBoxInfo
{
/// <summary>
/// 绘制区域
/// </summary>
public RawRectangleF DrawingRect { get; set; }
/// <summary>
/// 绘制边框颜色
/// </summary>
public RawColor4 DrawingBorderColor { get; set; }
/// <summary>
/// 绘制边框宽度
/// </summary>
public float DrawingBorderWidth { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VIZ.Framework.Common
{
/// <summary>
/// 框选框插件扩展
/// </summary>
public static class SelectionBoxExpand
{
/// <summary>
/// 更新框选框
/// </summary>
/// <param name="videoControl">视频控件</param>
/// <param name="selectionBoxInfo">框选框信息</param>
public static void UpdateSelectionBox(this VideoControl videoControl, SelectionBoxInfo selectionBoxInfo)
{
if (videoControl.videoRender == null)
return;
SelectionBoxPlugin plugin = videoControl.GetPlugin<SelectionBoxPlugin>(VideoControlPluginNames.SelectionBox);
if (plugin == null)
return;
plugin.Update(selectionBoxInfo);
}
/// <summary>
/// 清理框选框
/// </summary>
/// <param name="videoControl">视频控件</param>
public static void ClearSelectionBox(this VideoControl videoControl)
{
if (videoControl.videoRender == null)
return;
SelectionBoxPlugin plugin = videoControl.GetPlugin<SelectionBoxPlugin>(VideoControlPluginNames.SelectionBox);
if (plugin == null)
return;
plugin.Update(null);
}
}
}
using SharpDX.Direct2D1;
using SharpDX.Mathematics.Interop;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using VIZ.Framework.Core;
using VIZ.Framework.Storage;
using VIZ.Framework.Domain;
namespace VIZ.Framework.Common
{
/// <summary>
/// 框选框插件
/// </summary>
public class SelectionBoxPlugin : VideoPluginBase
{
/// <summary>
/// 框选框插件
/// </summary>
/// <param name="videoControl">视频控件</param>
public SelectionBoxPlugin(VideoControl videoControl) : base(videoControl)
{
this.drawingWidth = ApplicationDomain.IniStorage.GetValue<VideoConfig, int>(p => p.VIDEO_SELECTION_BOX_BORDER_WIDTH);
this.drawingColor = ApplicationDomain.IniStorage.GetValue<VideoConfig, RawColor4>(p => p.VIDEO_SELECTION_BOX_BORDER_COLOR);
}
/// <summary>
/// 名称
/// </summary>
public override string Name => VideoControlPluginNames.SelectionBox;
/// <summary>
/// 选中时触发
/// </summary>
public event EventHandler<SelectionBoxSelectedEventArgs> Selected;
/// <summary>
/// 框选框信息
/// </summary>
private volatile SelectionBoxInfo selectionBoxInfo;
/// <summary>
/// 鼠标左键按下的坐标
/// </summary>
private Point? mouseLeftButtonDownPoint;
/// <summary>
/// 绘制颜色
/// </summary>
private RawColor4 drawingColor;
/// <summary>
/// 绘制宽度
/// </summary>
private float drawingWidth;
/// <summary>
/// 更新数据
/// </summary>
/// <param name="trackingBoxInfos">跟踪框信息</param>
public void Update(SelectionBoxInfo selectionBoxInfo)
{
this.selectionBoxInfo = selectionBoxInfo;
}
/// <summary>
/// 渲染
/// </summary>
/// <param name="context">渲染上下文</param>
public override void Render(VideoRenderContext context)
{
SelectionBoxInfo info = this.selectionBoxInfo;
if (info == null)
return;
SolidColorBrush brush = new SolidColorBrush(context.Target, info.DrawingBorderColor);
RawRectangleF rect;
if (context.Mode == VideoRenderMode.UI)
{
rect = info.DrawingRect;
}
else
{
rect = ImageHelper.ConvertUiRectToImageRect(context.VideoRenderInfo.Frame.Width, context.VideoRenderInfo.Frame.Height, context.VideoRenderInfo.DrawingRect, info.DrawingRect.ToRect()).ToRawRectangleF();
}
context.Target.DrawRectangle(rect, brush, info.DrawingBorderWidth);
}
/// <summary>
/// 附加
/// </summary>
public override void Attach()
{
this.VideoRender.PreviewMouseLeftButtonDown -= VideoRender_PreviewMouseLeftButtonDown;
this.VideoRender.PreviewMouseLeftButtonDown += VideoRender_PreviewMouseLeftButtonDown;
this.VideoRender.PreviewMouseLeftButtonUp -= VideoRender_PreviewMouseLeftButtonUp;
this.VideoRender.PreviewMouseLeftButtonUp += VideoRender_PreviewMouseLeftButtonUp;
this.VideoRender.PreviewMouseMove -= VideoRender_PreviewMouseMove;
this.VideoRender.PreviewMouseMove += VideoRender_PreviewMouseMove;
}
/// <summary>
/// 卸载
/// </summary>
public override void Detach()
{
this.VideoRender.PreviewMouseLeftButtonDown -= VideoRender_PreviewMouseLeftButtonDown;
}
/// <summary>
/// 鼠标左键点击
/// </summary>
private void VideoRender_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (!this.IsEnabled)
return;
this.mouseLeftButtonDownPoint = e.GetPosition(this.VideoRender);
}
/// <summary>
/// 鼠标左键抬起
/// </summary>
private void VideoRender_PreviewMouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (this.mouseLeftButtonDownPoint == null || !this.IsEnabled)
return;
VideoRenderInfo renderInfo = this.VideoRender.RenderInfo;
if (renderInfo == null)
{
this.selectionBoxInfo = null;
return;
}
if (this.Selected == null)
return;
Point beginPoint = this.mouseLeftButtonDownPoint.Value;
Point endPoint = e.GetPosition(this.VideoRender);
this.mouseLeftButtonDownPoint = null;
SelectionBoxSelectedEventArgs args = new SelectionBoxSelectedEventArgs();
float left = (float)Math.Min(beginPoint.X, endPoint.X);
float right = (float)Math.Max(beginPoint.X, endPoint.X);
float top = (float)Math.Min(beginPoint.Y, endPoint.Y);
float bottom = (float)Math.Max(beginPoint.Y, endPoint.Y);
args.DrawingRect = new RawRectangleF(left, top, right, bottom);
args.SrcRect = ImageHelper.ConvertUiRectToImageRect(renderInfo.Frame.Width, renderInfo.Frame.Height, renderInfo.DrawingRect, args.DrawingRect.ToRect()).ToRawRectangleF();
this.Selected.Invoke(this, args);
}
/// <summary>
/// 鼠标移动
/// </summary>
private void VideoRender_PreviewMouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
if (this.mouseLeftButtonDownPoint == null || !this.IsEnabled)
return;
Point beginPoint = this.mouseLeftButtonDownPoint.Value;
Point endPoint = e.GetPosition(this.VideoRender);
float left = (float)Math.Min(beginPoint.X, endPoint.X);
float right = (float)Math.Max(beginPoint.X, endPoint.X);
float top = (float)Math.Min(beginPoint.Y, endPoint.Y);
float bottom = (float)Math.Max(beginPoint.Y, endPoint.Y);
SelectionBoxInfo info = new SelectionBoxInfo();
info.DrawingRect = new RawRectangleF(left, top, right, bottom);
info.DrawingBorderColor = this.drawingColor;
info.DrawingBorderWidth = this.drawingWidth;
this.selectionBoxInfo = info;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace VIZ.Framework.Common
{
/// <summary>
/// 跟踪框点击事件参数
/// </summary>
public class TrackingBoxClickEventArgs : EventArgs
{
/// <summary>
/// 鼠标点击位置
/// </summary>
public Point MousePoint { get; set; }
/// <summary>
/// 命中的跟踪框信息
/// </summary>
public List<TrackingBoxInfo> HitTrackingBoxInfos { get; set; }
}
}
using SharpDX.Mathematics.Interop;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VIZ.Framework.Common
{
/// <summary>
/// 跟踪框信息
/// </summary>
public class TrackingBoxInfo
{
/// <summary>
/// 编号
/// </summary>
public string ID { get; set; }
/// <summary>
/// 源区域
/// </summary>
public RawRectangleF SrcRect { get; set; }
/// <summary>
/// 绘制边框颜色
/// </summary>
public RawColor4 DrawingBorderColor { get; set; }
/// <summary>
/// 绘制边框宽度
/// </summary>
public float DrawingBorderWidth { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VIZ.Framework.Common
{
/// <summary>
/// 跟踪框扩展
/// </summary>
public static class TrackingBoxExpand
{
/// <summary>
/// 更新跟踪框
/// </summary>
/// <param name="videoControl">视频控件</param>
/// <param name="trackingBoxInfos">跟踪框信息</param>
public static void UpdateTrackingBox(this VideoControl videoControl, List<TrackingBoxInfo> trackingBoxInfos)
{
if (videoControl.videoRender == null)
return;
TrackingBoxPlugin plugin = videoControl.GetPlugin<TrackingBoxPlugin>(VideoControlPluginNames.TrackingBox);
if (plugin == null)
return;
plugin.Update(trackingBoxInfos);
}
/// <summary>
/// 清理跟踪框
/// </summary>
/// <param name="videoControl">视频控件</param>
public static void ClearTrackingBox(this VideoControl videoControl)
{
if (videoControl.videoRender == null)
return;
TrackingBoxPlugin plugin = videoControl.GetPlugin<TrackingBoxPlugin>(VideoControlPluginNames.TrackingBox);
if (plugin == null)
return;
plugin.Update(null);
}
/// <summary>
/// 命中测试
/// </summary>
/// <param name="trackingBoxInfos">跟踪框信息结合</param>
/// <param name="point">视频坐标</param>
/// <returns>命中的跟踪框</returns>
public static List<TrackingBoxInfo> HitTest(List<TrackingBoxInfo> trackingBoxInfos, System.Windows.Point point)
{
List<TrackingBoxInfo> result = new List<TrackingBoxInfo>();
if (trackingBoxInfos == null || trackingBoxInfos.Count == 0)
return result;
foreach (TrackingBoxInfo item in trackingBoxInfos)
{
if (point.X >= item.SrcRect.Left && point.X <= item.SrcRect.Right && point.Y >= item.SrcRect.Top && point.Y <= item.SrcRect.Bottom)
{
result.Add(item);
}
}
return result;
}
}
}
using SharpDX.Direct2D1;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using VIZ.Framework.Core;
namespace VIZ.Framework.Common
{
/// <summary>
/// 跟踪框插件
/// </summary>
public class TrackingBoxPlugin : VideoPluginBase
{
/// <summary>
/// 跟踪框插件
/// </summary>
/// <param name="videoControl">视频控件</param>
public TrackingBoxPlugin(VideoControl videoControl) : base(videoControl)
{
}
/// <summary>
/// 名称
/// </summary>
public override string Name => VideoControlPluginNames.TrackingBox;
/// <summary>
/// 点击跟踪框时触发
/// </summary>
public event EventHandler<TrackingBoxClickEventArgs> TrackingBoxClick;
/// <summary>
/// 跟踪框信息
/// </summary>
private volatile List<TrackingBoxInfo> trackingBoxInfos;
/// <summary>
/// 更新数据
/// </summary>
/// <param name="trackingBoxInfos">跟踪框信息</param>
public void Update(List<TrackingBoxInfo> trackingBoxInfos)
{
this.trackingBoxInfos = trackingBoxInfos;
}
/// <summary>
/// 渲染
/// </summary>
/// <param name="context">渲染上下文</param>
public override void Render(VideoRenderContext context)
{
// 画面渲染区域
List<TrackingBoxInfo> infos = this.trackingBoxInfos;
if (infos == null)
return;
foreach (TrackingBoxInfo info in infos)
{
// 跟踪框绘制区域转化为UI绘制区域
SharpDX.Mathematics.Interop.RawRectangleF rect;
if (context.Mode == VideoRenderMode.UI)
{
Rect drawRect = ImageHelper.ConvertImageRectToUiRect(context.VideoRenderInfo.Frame.Width, context.VideoRenderInfo.Frame.Height, context.VideoRenderInfo.DrawingRect, info.SrcRect.ToRect());
rect = drawRect.ToRawRectangleF();
}
else
{
rect = info.SrcRect;
}
// 绘制跟踪框
SolidColorBrush brush = new SolidColorBrush(context.Target, info.DrawingBorderColor);
context.Target.DrawRectangle(rect, brush, info.DrawingBorderWidth);
}
}
/// <summary>
/// 附加
/// </summary>
public override void Attach()
{
this.VideoRender.PreviewMouseLeftButtonDown -= VideoRender_PreviewMouseLeftButtonDown;
this.VideoRender.PreviewMouseLeftButtonDown += VideoRender_PreviewMouseLeftButtonDown;
}
/// <summary>
/// 卸载
/// </summary>
public override void Detach()
{
this.VideoRender.PreviewMouseLeftButtonDown -= VideoRender_PreviewMouseLeftButtonDown;
}
/// <summary>
/// 鼠标左键点击
/// </summary>
private void VideoRender_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (this.TrackingBoxClick == null || !this.IsEnabled)
return;
VideoRenderInfo renderInfo = this.VideoRender.RenderInfo;
if (renderInfo == null)
return;
Point uiPoint = e.GetPosition(this.VideoRender);
Point bmpPoint = ImageHelper.ConvertUiPointToImagePoint(renderInfo.Frame.Width, renderInfo.Frame.Height, renderInfo.DrawingRect, uiPoint);
TrackingBoxClickEventArgs args = new TrackingBoxClickEventArgs();
args.MousePoint = uiPoint;
args.HitTrackingBoxInfos = TrackingBoxExpand.HitTest(this.trackingBoxInfos, bmpPoint);
// 触发视频矩形框点击事件
this.TrackingBoxClick.Invoke(this, args);
}
}
}
using SharpDX.Direct2D1;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VIZ.Framework.Common
{
/// <summary>
/// 视频插件基类
/// </summary>
public abstract class VideoPluginBase : IVideoPlugin
{
/// <summary>
/// 视频插件基类
/// </summary>
/// <param name="videoControl">视频控件</param>
public VideoPluginBase(VideoControl videoControl)
{
this.VideoControl = videoControl;
this.VideoRender = videoControl.videoRender;
}
/// <summary>
/// 插件名称
/// </summary>
public abstract string Name { get; }
/// <summary>
/// 视频控件
/// </summary>
public VideoControl VideoControl { get; private set; }
/// <summary>
/// 视频渲染
/// </summary>
public VideoCustomRender VideoRender { get; private set; }
/// <summary>
/// 是否启用
/// </summary>
public bool IsEnabled { get; set; } = true;
/// <summary>
/// 附加
/// </summary>
public virtual void Attach()
{
}
/// <summary>
/// 卸载
/// </summary>
public virtual void Detach()
{
}
/// <summary>
/// 渲染
/// </summary>
/// <param name="context">渲染上下文</param>
public abstract void Render(VideoRenderContext context);
}
}
<UserControl x:Class="VIZ.Framework.Common.VideoControl"
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:common="clr-namespace:VIZ.Framework.Common"
xmlns:converter="clr-namespace:VIZ.Framework.Core;assembly=VIZ.Framework.Core"
mc:Ignorable="d" x:Name="uc"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<converter:Bool2VisibilityConverter x:Key="Bool2VisibilityConverter"></converter:Bool2VisibilityConverter>
</UserControl.Resources>
<Grid>
<common:VideoCustomRender x:Name="videoRender"></common:VideoCustomRender>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="5,5,0,0" IsHitTestVisible="False"
Visibility="{Binding Path=IsShowFPS,ElementName=uc,Converter={StaticResource Bool2VisibilityConverter}}">
<TextBlock Text="FPS:" Foreground="Red" FontSize="24"></TextBlock>
<TextBlock Text="{Binding ElementName=videoRender,Path=Fps}" Foreground="Red" FontSize="24" Margin="10,0,0,0"></TextBlock>
</StackPanel>
</Grid>
</UserControl>
using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using VIZ.Framework.Core;
namespace VIZ.Framework.Common
{
/// <summary>
/// VideoControl.xaml 的交互逻辑
/// </summary>
public partial class VideoControl : UserControl
{
public VideoControl()
{
InitializeComponent();
this.videoRender.OwnerVideoControl = this;
}
// ===========================================================================================================
// === Property ===
// ===========================================================================================================
#region IsShowFPS -- 是否显示FPS
/// <summary>
/// 是否显示FPS
/// </summary>
public bool IsShowFPS
{
get { return (bool)GetValue(IsShowFPSProperty); }
set { SetValue(IsShowFPSProperty, value); }
}
/// <summary>
/// Using a DependencyProperty as the backing store for IsShowFPS. This enables animation, styling, binding, etc...
/// </summary>
public static readonly DependencyProperty IsShowFPSProperty =
DependencyProperty.Register("IsShowFPS", typeof(bool), typeof(VideoControl), new PropertyMetadata(false));
#endregion
// ===========================================================================================================
// === Public Function ===
// ===========================================================================================================
/// <summary>
/// 更新视频帧
/// </summary>
public void UpdateVideoFrame(IVideoFrame videoFrame)
{
this.videoRender?.UpdateVideoFrame(videoFrame);
}
/// <summary>
/// 清理视频帧
/// </summary>
public void ClearVideoFrame()
{
this.videoRender.UpdateVideoFrame(null);
}
/// <summary>
/// 渲染帧至图片
/// </summary>
/// <param name="plugins">需要处理渲染的插件名称<see cref="VideoControlPluginNames"/></param>
/// <returns>图片</returns>
public Mat RenderVideoToMat(List<string> plugins = null)
{
VideoRenderInfo renderInfo = this.videoRender.RenderInfo;
if (renderInfo == null)
return null;
return this.RenderVideoToMat(renderInfo.Frame.Width, renderInfo.Frame.Height, plugins);
}
/// <summary>
/// 渲染帧至图片
/// </summary>
/// <param name="width">图片宽度</param>
/// <param name="height">图片高度</param>
/// <param name="format">渲染格式</param>
/// <param name="plugins">需要处理渲染的插件名称<see cref="VideoControlPluginNames"/></param>
/// <returns>图片</returns>
public Mat RenderVideoToMat(int width, int height, List<string> plugins = null)
{
SharpDX.WIC.Bitmap bmp = WicBitmapHelper.RenderToBitmap(width, height, this.videoRender.Render, plugins);
Mat mat = new Mat(height, width, MatType.CV_8UC4);
unsafe
{
bmp.CopyPixels(width * 4, new IntPtr(mat.DataPointer), width * height * 4);
}
return mat;
}
/// <summary>
/// 附加插件
/// </summary>
/// <param name="plugin">插件</param>
public void AttachPlugin(IVideoPlugin plugin)
{
lock (this.videoRender.Plugins)
{
this.videoRender.Plugins.Add(plugin);
plugin.Attach();
}
}
/// <summary>
/// 卸载
/// </summary>
/// <param name="name">插件名称</param>
public void DetachPlugin(string name)
{
IVideoPlugin plugin = this.videoRender.Plugins.FirstOrDefault(p => p.Name == name);
if (plugin == null)
return;
lock (this.videoRender.Plugins)
{
this.videoRender.Plugins.Remove(plugin);
plugin.Detach();
}
}
/// <summary>
/// 卸载所有插件
/// </summary>
public void DetachPluginAll()
{
lock (this.videoRender.Plugins)
{
foreach (IVideoPlugin plugin in this.videoRender.Plugins)
{
plugin.Detach();
}
this.videoRender.Plugins.Clear();
}
}
/// <summary>
/// 获取插件
/// </summary>
/// <typeparam name="T">插件类型</typeparam>
/// <param name="name">插件名称</param>
/// <returns>插件</returns>
public T GetPlugin<T>(string name) where T : class, IVideoPlugin
{
return this.videoRender.Plugins.FirstOrDefault(p => p.Name == name) as T;
}
/// <summary>
/// 获取渲染信息
/// </summary>
public VideoRenderInfo GetRenderInfo()
{
return this.videoRender.RenderInfo;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VIZ.Framework.Common
{
/// <summary>
/// 视频控件插件名称
/// </summary>
public static class VideoControlPluginNames
{
/// <summary>
/// 跟踪框
/// </summary>
public const string TrackingBox = "TrackingBox";
/// <summary>
/// 框选框
/// </summary>
public const string SelectionBox = "SelectionBox";
/// <summary>
/// 裁剪框
/// </summary>
public const string ClipBox = "ClipBox";
}
}
using SharpDX.Direct2D1;
using SharpDX.DXGI;
using SharpDX.Mathematics.Interop;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using VIZ.Framework.Core;
using VIZ.Framework.Domain;
using VIZ.Framework.Storage;
namespace VIZ.Framework.Common
{
/// <summary>
/// 视频自定义渲染
/// ---------------------------------------------
/// 1. 视频帧渲染
/// 2. 矩形框信息渲染
/// 3. 渲染帧至图片
/// ---------------------------------------------
/// </summary>
public class VideoCustomRender : D2dControl
{
// ========================================================================================================
// === Property ===
// ========================================================================================================
/// <summary>
/// 是否启用视频帧渲染
/// </summary>
public bool IsRenderVideoFrameEnabled { get; set; } = true;
/// <summary>
/// 背景颜色
/// </summary>
private readonly RawColor4 BackgroundColor = ApplicationDomain.IniStorage.GetValue<VideoConfig, RawColor4>(p => p.VIDEO_BACKGROUND_COLOR);
// ========================================================================================================
/// <summary>
/// 所属渲染控件
/// </summary>
internal VideoControl OwnerVideoControl { get; set; }
/// <summary>
/// 待渲染信息
/// </summary>
internal volatile VideoRenderInfo RenderInfo;
/// <summary>
/// 插件集合
/// </summary>
internal List<IVideoPlugin> Plugins = new List<IVideoPlugin>();
/// <summary>
/// 渲染锁对象
/// </summary>
private object render_lock_object = new object();
// ========================================================================================================
// === Public Function ===
// ========================================================================================================
/// <summary>
/// 更新视频帧
/// </summary>
/// <param name="videoFrame">视频帧</param>
public void UpdateVideoFrame(IVideoFrame videoFrame)
{
if (videoFrame == null)
{
this.RenderInfo = null;
return;
}
// ----------------------------------------------------------------------------------------
// 计算绘制区域与大小
ImageHelper.AdjustSize((int)this.ActualWidth, (int)this.ActualHeight, videoFrame.Width, videoFrame.Height, out int width, out int height);
int x = (int)((this.ActualWidth - width) / 2);
int y = (int)((this.ActualHeight - height) / 2);
// ----------------------------------------------------------------------------------------
// 绘制信息
VideoRenderInfo info = new VideoRenderInfo();
info.Frame = videoFrame;
info.ControlRect = new Rect(0, 0, this.ActualWidth, this.ActualHeight);
info.DrawingRect = new Rect(x, y, width, height);
lock (this.render_lock_object)
{
this.RenderInfo?.Dispose();
this.RenderInfo = info;
}
}
// ========================================================================================================
/// <summary>
/// 渲染
/// </summary>
/// <param name="target">渲染目标</param>
public override void Render(RenderTarget target)
{
VideoRenderContext context = new VideoRenderContext();
context.Target = target;
context.Mode = VideoRenderMode.UI;
this.Render(context);
}
/// <summary>
/// 渲染
/// </summary>
/// <param name="context">渲染上下文</param>
public void Render(VideoRenderContext context)
{
lock (this.render_lock_object)
{
context.Target.Clear(this.BackgroundColor);
VideoRenderInfo renderInfo = this.RenderInfo;
if (renderInfo == null)
return;
context.VideoRenderInfo = renderInfo;
// 绘制视频
if (this.IsRenderVideoFrameEnabled)
{
this.RenderVideoFrame(context);
}
// 插件渲染
foreach (IVideoPlugin plugin in this.Plugins)
{
if (!plugin.IsEnabled)
continue;
if (context.Mode == VideoRenderMode.Image && (context.RenderImagePlugins == null || !context.RenderImagePlugins.Contains(plugin.Name)))
continue;
plugin.Render(context);
}
}
}
/// <summary>
/// 绘制视频
/// </summary>
/// <param name="context">渲染上下文</param>
private void RenderVideoFrame(VideoRenderContext context)
{
BitmapProperties properties = new BitmapProperties(new SharpDX.Direct2D1.PixelFormat(Format.B8G8R8A8_UNorm, SharpDX.Direct2D1.AlphaMode.Premultiplied));
SharpDX.Size2 size = new SharpDX.Size2(context.VideoRenderInfo.Frame.Width, context.VideoRenderInfo.Frame.Height);
SharpDX.Direct2D1.Bitmap bmp = new SharpDX.Direct2D1.Bitmap(context.Target, size, context.VideoRenderInfo.Frame.DataStream, context.VideoRenderInfo.Frame.Width * 4, properties);
if (context.Mode == VideoRenderMode.UI)
{
context.Target.DrawBitmap(bmp, context.VideoRenderInfo.DrawingRect.ToRawRectangleF(), 1f, BitmapInterpolationMode.Linear, null);
}
else
{
SharpDX.Mathematics.Interop.RawRectangleF rect = new SharpDX.Mathematics.Interop.RawRectangleF(0, 0, context.VideoRenderInfo.Frame.Width, context.VideoRenderInfo.Frame.Height);
context.Target.DrawBitmap(bmp, rect, 1f, BitmapInterpolationMode.Linear, null);
}
bmp.Dispose();
}
}
}
using SharpDX.Direct2D1;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VIZ.Framework.Common
{
/// <summary>
/// 视频渲染上下文
/// </summary>
public class VideoRenderContext
{
/// <summary>
/// 渲染目标
/// </summary>
public RenderTarget Target { get; set; }
/// <summary>
/// 视频渲染信息
/// </summary>
public VideoRenderInfo VideoRenderInfo { get; set; }
/// <summary>
/// 渲染模式
/// </summary>
public VideoRenderMode Mode { get; set; }
/// <summary>
/// 需要渲染至图片的插件
/// </summary>
public List<string> RenderImagePlugins { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using SharpDX;
using SharpDX.Direct2D1;
namespace VIZ.Framework.Common
{
/// <summary>
/// 视频渲染信息
/// </summary>
public class VideoRenderInfo : IDisposable
{
/// <summary>
/// 视频帧信息
/// </summary>
public IVideoFrame Frame { get; set; }
/// <summary>
/// 渲染区域
/// </summary>
public Rect DrawingRect { get; set; }
/// <summary>
/// 控件区域
/// </summary>
public Rect ControlRect { get; set; }
/// <summary>
/// 该对象是否已经被销毁
/// </summary>
private bool IsDisopsed { get; set; }
/// <summary>
/// 销毁
/// </summary>
public void Dispose()
{
if (this.IsDisopsed)
return;
this.IsDisopsed = true;
this.Frame?.Dispose();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VIZ.Framework.Common
{
/// <summary>
/// 视频渲染模式
/// </summary>
public enum VideoRenderMode
{
/// <summary>
/// 渲染至UI
/// </summary>
UI,
/// <summary>
/// 渲染至图片文件
/// </summary>
Image
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SharpDX;
using SharpDX.Direct2D1;
namespace VIZ.Framework.Common
{
/// <summary>
/// 视频帧
/// </summary>
public interface IVideoFrame : IDisposable
{
/// <summary>
/// 宽度
/// </summary>
int Width { get; set; }
/// <summary>
/// 高度
/// </summary>
int Height { get; set; }
/// <summary>
/// 数据长度
/// </summary>
int Length { get; set; }
/// <summary>
/// 时间戳
/// </summary>
long TimeStamp { get; set; }
/// <summary>
/// 是否是最后一帧
/// </summary>
bool IsEnd { get; set; }
/// <summary>
/// 画面数据
/// </summary>
DataStream DataStream { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VIZ.Framework.Common
{
/// <summary>
/// 视频流
/// </summary>
public interface IVideoStream : IDisposable
{
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NewTek;
namespace VIZ.Framework.Common
{
/// <summary>
/// NDI流 静态部分
/// </summary>
public partial class NDIStream
{
/// <summary>
/// 是否完成了初始化
/// </summary>
public static bool IsInited { get; private set; }
/// <summary>
/// 初始化
/// </summary>
/// <returns>是否初始化成功</returns>
public static bool Init()
{
if (!NDIlib.initialize())
return false;
IsInited = true;
return true;
}
public static void Destroy()
{
if (!IsInited)
return;
NDIlib.destroy();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using NewTek;
using System.Collections.Concurrent;
using System.Runtime.InteropServices;
using NewTek.NDI;
namespace VIZ.Framework.Common
{
/// <summary>
/// NDI流
/// </summary>
public partial class NDIStream : VideoStreamBase<NDIStreamOption>
{
/// <summary>
/// NDI流
/// </summary>
/// <param name="localRecevierName">本地接收者名称</param>
/// <param name="remoteSenderName">远程发送者名称</param>
/// <param name="option">设置</param>
public NDIStream(string localRecevierName, string remoteSenderName, NDIStreamOption option)
: base(option)
{
this.LocalRecevierName = localRecevierName;
this.RemoteSenderName = remoteSenderName;
// 查找NDI流任务
this.TaskDic.Add(NDIStreamTaskNames.FIND_STREAM, new NDIStreamFindStreamTask(this));
// 接收视频帧任务
this.TaskDic.Add(NDIStreamTaskNames.RECV_VIDEO, new NDIStreamRecvVideoTask(this));
// 处理视频帧任务
this.TaskDic.Add(NDIStreamTaskNames.EXECUTE_VIDEO, new NDIStreamExecuteVideoTask(this));
}
/* ========================================================================================================= */
/* === Const === */
/* ========================================================================================================= */
/// <summary>
/// the size of an NDIlib.source_t, for pointer offsets
/// </summary>
public readonly int SOURCE_SIZE_IN_BYTES = Marshal.SizeOf(typeof(NDIlib.source_t));
/* ========================================================================================================= */
/* === Property === */
/* ========================================================================================================= */
/// <summary>
/// NDI流信息集合
/// </summary>
public List<NDIStreamInfo> StreamInfos { get; private set; } = new List<NDIStreamInfo>();
/// <summary>
/// 远程NDI发送者名称
/// </summary>
public string RemoteSenderName { get; private set; }
/// <summary>
/// 本地NDI接收者名称
/// </summary>
public string LocalRecevierName { get; private set; }
/* ========================================================================================================= */
/* === Internal Field === */
/* ========================================================================================================= */
/// <summary>
/// 查找NDI流实例句柄
/// </summary>
internal IntPtr FindInstancePtr;
/// <summary>
/// 当前正在处理的NDI流源
/// </summary>
internal NDIlib.source_t? CurrentRemoteSenderSource;
/// <summary>
/// 接收NDI流实例句柄
/// </summary>
internal IntPtr RecvInstancePtr;
/// <summary>
/// 接收NDI流同步信息实例句柄
/// </summary>
internal IntPtr RecvSyncInstancePtr;
/* ========================================================================================================= */
/* === Field === */
/* ========================================================================================================= */
/// <summary>
/// 任务池
/// </summary>
private Dictionary<NDIStreamTaskNames, NDIStreamTaskBase> TaskDic = new Dictionary<NDIStreamTaskNames, NDIStreamTaskBase>();
/* ========================================================================================================= */
/* === Function === */
/* ========================================================================================================= */
/// <summary>
/// 开始
/// </summary>
public void Start()
{
NDIlib.find_create_t findCreateDesc = new NDIlib.find_create_t();
findCreateDesc.p_groups = IntPtr.Zero;
findCreateDesc.show_local_sources = true;
findCreateDesc.p_extra_ips = IntPtr.Zero;
// 创建NDI流查找实例句柄
this.FindInstancePtr = NDIlib.find_create_v2(ref findCreateDesc);
// 启动NDI流查找任务
this.TaskDic[NDIStreamTaskNames.FIND_STREAM].Start();
// 启动NDI视频帧接收任务
this.TaskDic[NDIStreamTaskNames.RECV_VIDEO].Start();
// 启动NDI视频帧处理任务
this.TaskDic[NDIStreamTaskNames.EXECUTE_VIDEO].Start();
}
/// <summary>
/// 停止
/// </summary>
public void Stop()
{
// 停止所有任务
foreach (var task in this.TaskDic.Values)
{
task.Stop();
}
// 重置当前NDI流信息
this.CurrentRemoteSenderSource = null;
// 清理流信息
lock (this.StreamInfos)
{
this.StreamInfos.Clear();
}
// 释放查找NDI流句柄
if (this.FindInstancePtr != IntPtr.Zero)
{
NDIlib.find_destroy(this.FindInstancePtr);
this.FindInstancePtr = IntPtr.Zero;
}
// 释放接收NDI流实例句柄
if (this.RecvInstancePtr != IntPtr.Zero)
{
NDIlib.recv_destroy(this.RecvInstancePtr);
this.RecvInstancePtr = IntPtr.Zero;
}
// 释放NDI流同步接收实例句柄
if (this.RecvSyncInstancePtr != IntPtr.Zero)
{
NDIlib.framesync_destroy(this.RecvSyncInstancePtr);
this.RecvSyncInstancePtr = IntPtr.Zero;
}
}
/// <summary>
/// 销毁
/// </summary>
public override void Dispose()
{
this.Stop();
}
/// <summary>
/// 切换远程发送者名称
/// </summary>
/// <param name="remoteSenderName">远程发送者名称</param>
/// <returns>是否成功切换</returns>
public bool ChangeRemoteSenderName(string remoteSenderName)
{
// -------------------------------------------------------------------------------
// 获取将要切换的NDI流信息
// 获取要切换的NDI流信息
NDIStreamInfo info = null;
lock (this.StreamInfos)
{
info = this.StreamInfos.FirstOrDefault(p => p.FullName == remoteSenderName);
}
if (info == null)
{
return false;
}
// -------------------------------------------------------------------------------
// 释放
// 释放接收NDI流实例句柄
if (this.RecvInstancePtr != IntPtr.Zero)
{
NDIlib.recv_destroy(this.RecvInstancePtr);
this.RecvInstancePtr = IntPtr.Zero;
}
// 释放NDI流同步接收实例句柄
if (this.RecvSyncInstancePtr != IntPtr.Zero)
{
NDIlib.framesync_destroy(this.RecvSyncInstancePtr);
this.RecvSyncInstancePtr = IntPtr.Zero;
}
// -------------------------------------------------------------------------------
// 初始化
// 设置NDI发送者名称
this.RemoteSenderName = remoteSenderName;
// 设置当前NDI发送源
this.CurrentRemoteSenderSource = info.Source;
// 创建NDI接收实例句柄 & NDI同步接收实例对象
NDIlib.recv_create_v3_t recvCreateDesc = new NDIlib.recv_create_v3_t();
recvCreateDesc.source_to_connect_to = info.Source;
recvCreateDesc.color_format = NDIlib.recv_color_format_e.recv_color_format_BGRX_BGRA;
recvCreateDesc.bandwidth = NDIlib.recv_bandwidth_e.recv_bandwidth_highest;
recvCreateDesc.allow_video_fields = false;
recvCreateDesc.p_ndi_recv_name = UTF.StringToUtf8(this.LocalRecevierName);
this.RecvInstancePtr = NDIlib.recv_create_v3(ref recvCreateDesc);
this.RecvSyncInstancePtr = NDIlib.framesync_create(this.RecvInstancePtr);
// -------------------------------------------------------------------------------
// 切换成功
return true;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NewTek;
namespace VIZ.Framework.Common
{
/// <summary>
/// NDI流信息
/// </summary>
public class NDIStreamInfo
{
/// <summary>
/// 机器
/// </summary>
public string Machine { get; set; }
/// <summary>
/// 名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 完整名称
/// </summary>
public string FullName { get; set; }
/// <summary>
/// NDI流信息
/// </summary>
public NDIlib.source_t Source { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VIZ.Framework.Common
{
/// <summary>
/// NDI流设置
/// </summary>
public class NDIStreamOption : VideoStreamOptionBase
{
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace VIZ.Framework.Common
{
/// <summary>
/// NDI流任务
/// </summary>
public abstract class NDIStreamTaskBase : VideoStreamTaskBase
{
/// <summary>
/// NDI工具任务
/// </summary>
/// <param name="stream">NDI流</param>
public NDIStreamTaskBase(NDIStream stream)
{
this.Stream = stream;
}
/// <summary>
/// 任务名称
/// </summary>
public abstract NDIStreamTaskNames Name { get; }
/// <summary>
/// NDI工具
/// </summary>
public NDIStream Stream { get; private set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VIZ.Framework.Common
{
/// <summary>
/// NDI流任务名称
/// </summary>
public enum NDIStreamTaskNames
{
/// <summary>
/// 查找NDI流
/// </summary>
FIND_STREAM,
/// <summary>
/// 接收视频
/// </summary>
RECV_VIDEO,
/// <summary>
/// 处理视频帧
/// </summary>
EXECUTE_VIDEO
}
}
using NewTek;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SharpDX;
using SharpDX.Direct2D1;
using SharpDX.Mathematics.Interop;
using OpenCvSharp;
namespace VIZ.Framework.Common
{
/// <summary>
/// NDI视频帧
/// </summary>
public class NDIStreamVideoFrame : VideoFrameBase
{
/// <summary>
/// NDI视频帧
/// </summary>
public NDIStreamVideoFrame()
{ }
/// <summary>
/// NDI视频帧
/// </summary>
/// <param name="color">颜色</param>
public NDIStreamVideoFrame(Scalar color) : base(color)
{ }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using NewTek;
namespace VIZ.Framework.Common
{
/// <summary>
/// 处理NDI视频帧任务
/// </summary>
public class NDIStreamExecuteVideoTask : NDIStreamTaskBase
{
/// <summary>
/// 处理NDI视频帧任务
/// </summary>
/// <param name="stream">NDI流</param>
public NDIStreamExecuteVideoTask(NDIStream stream) : base(stream)
{
}
/// <summary>
/// 任务名称
/// </summary>
public override NDIStreamTaskNames Name => NDIStreamTaskNames.EXECUTE_VIDEO;
/// <summary>
/// 执行
/// </summary>
protected override void Execute()
{
while (this.IsStarted)
{
if (this.Stream.VideoFrameQueue.Count <= this.Stream.Option.DelayFrame)
{
Thread.Sleep(2);
continue;
}
if (!this.Stream.VideoFrameQueue.TryDequeue(out IVideoFrame frame))
{
continue;
}
// 触发视频帧处理事件
this.Stream.TriggerExecuteVideoFrame(frame);
Thread.Sleep(10);
}
}
}
}
using NewTek;
using NewTek.NDI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace VIZ.Framework.Common
{
/// <summary>
/// 查找NDI流任务
/// </summary>
public class NDIStreamFindStreamTask : NDIStreamTaskBase
{
/// <summary>
/// 查找NDI流任务
/// </summary>
/// <param name="stream">NDI流</param>
public NDIStreamFindStreamTask(NDIStream stream) : base(stream)
{
}
/// <summary>
/// 任务名称
/// </summary>
public override NDIStreamTaskNames Name => NDIStreamTaskNames.FIND_STREAM;
/// <summary>
/// the size of an NDIlib.source_t, for pointer offsets
/// </summary>
private readonly int SOURCE_SIZE_IN_BYTES = Marshal.SizeOf(typeof(NDIlib.source_t));
/// <summary>
/// 执行
/// </summary>
protected override void Execute()
{
while (this.IsStarted)
{
this._Execute();
}
}
/// <summary>
/// 执行
/// </summary>
private void _Execute()
{
if (!NDIlib.find_wait_for_sources(this.Stream.FindInstancePtr, 500))
return;
lock (this.Stream.StreamInfos)
{
this.Stream.StreamInfos.Clear();
}
uint numSources = 0;
IntPtr sourcesPtr = NDIlib.find_get_current_sources(this.Stream.FindInstancePtr, ref numSources);
for (int i = 0; i < numSources; i++)
{
// 连续的数据结构
IntPtr p = IntPtr.Add(sourcesPtr, (i * SOURCE_SIZE_IN_BYTES));
// 指针转化为 NDIlib.source_t 数据结构
NDIlib.source_t src = (NDIlib.source_t)Marshal.PtrToStructure(p, typeof(NDIlib.source_t));
// NDI流名称
string ndiName = UTF.Utf8ToString(src.p_ndi_name);
// 添加NDI流信息
NDIStreamInfo info = new NDIStreamInfo();
info.FullName = ndiName;
info.Machine = ndiName.Split(new char[] { ' ' }, 2)[0];
info.Name = ndiName.Substring(ndiName.IndexOf(' ') + 1);
info.Source = src;
lock (this.Stream.StreamInfos)
{
this.Stream.StreamInfos.Add(info);
}
}
// 如果当前没有正在处理的流信息那么切换为当前RemoteSenderName指定的流信息
if (this.Stream.CurrentRemoteSenderSource == null)
{
this.Stream.ChangeRemoteSenderName(this.Stream.RemoteSenderName);
}
}
}
}
using NewTek;
using NewTek.NDI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Drawing;
using System.Drawing.Imaging;
namespace VIZ.Framework.Common
{
/// <summary>
/// 查找NDI流任务
/// </summary>
public class NDIStreamRecvVideoTask : NDIStreamTaskBase
{
/// <summary>
/// 查找NDI流任务
/// </summary>
/// <param name="stream">NDI流</param>
public NDIStreamRecvVideoTask(NDIStream stream) : base(stream)
{
}
/// <summary>
/// 任务名称
/// </summary>
public override NDIStreamTaskNames Name => NDIStreamTaskNames.RECV_VIDEO;
/// <summary>
/// 之前的视频帧时间戳
/// </summary>
private long old_video_timestamp = 0;
/// <summary>
/// 执行
/// </summary>
protected override void Execute()
{
while (this.IsStarted)
{
if (this.Stream.RecvInstancePtr == IntPtr.Zero || this.Stream.RecvSyncInstancePtr == IntPtr.Zero)
{
Thread.Sleep(1);
continue;
}
// 执行
this._Execute();
}
}
/// <summary>
/// 执行
/// </summary>
private void _Execute()
{
// 获取NDI视频帧
NDIlib.video_frame_v2_t frame = new NDIlib.video_frame_v2_t();
NDIlib.framesync_capture_video(this.Stream.RecvSyncInstancePtr, ref frame, NDIlib.frame_format_type_e.frame_format_type_progressive);
// 如果该帧的时间戳与之前的帧时间戳一致
// 那么认为没有获取到新的一帧画面
if (this.old_video_timestamp == frame.timestamp)
{
NDIlib.framesync_free_video(this.Stream.RecvSyncInstancePtr, ref frame);
Thread.Sleep(1);
return;
}
// 否则
// 获取到新的一帧画面
this.old_video_timestamp = frame.timestamp;
// 如果新的一帧画面的时间戳为0 || 帧没有数据
if (frame.timestamp == 0 || frame.p_data == IntPtr.Zero || frame.p_data == null)
{
NDIlib.framesync_free_video(this.Stream.RecvSyncInstancePtr, ref frame);
Thread.Sleep(1);
return;
}
// 视频帧队列最大容量
// 如果超过该容量,那么会抛弃对队列头部的处理
if (this.Stream.VideoFrameQueue.Count > NDIStream.VIDEO_FRAME_QUEUE_MAX_COUNT + this.Stream.Option.DelayFrame)
{
this.Stream.VideoFrameQueue.TryDequeue(out IVideoFrame remove);
remove?.Dispose();
}
NDIStreamVideoFrame videoFrame = new NDIStreamVideoFrame();
videoFrame.Width = frame.xres;
videoFrame.Height = frame.yres;
videoFrame.Length = frame.xres * frame.yres * 4;
videoFrame.TimeStamp = frame.timestamp;
videoFrame.DataStream = new SharpDX.DataStream(videoFrame.Length, true, true);
unsafe
{
Buffer.MemoryCopy(frame.p_data.ToPointer(), videoFrame.DataStream.DataPointer.ToPointer(), videoFrame.Length, videoFrame.Length);
}
this.Stream.VideoFrameQueue.Enqueue(videoFrame);
// 尝试释放帧数据
NDIlib.framesync_free_video(this.Stream.RecvSyncInstancePtr, ref frame);
Thread.Sleep(5);
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment