check point
This commit is contained in:
14
framework/Inspectron.Camera/CameraExtensions.cs
Normal file
14
framework/Inspectron.Camera/CameraExtensions.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using Inspectron.Camera.Emulation;
|
||||
using Ninject;
|
||||
|
||||
namespace Inspectron.Camera
|
||||
{
|
||||
public static class CameraExtensions
|
||||
{
|
||||
public static IKernel UseEmulationCamera(this IKernel self,string[] folders)
|
||||
{
|
||||
self.Rebind<ICameraProvider>().ToConstant(new EmulationCameraProvider(folders)).InSingletonScope();
|
||||
return self;
|
||||
}
|
||||
}
|
||||
}
|
||||
61
framework/Inspectron.Camera/CameraParameter.cs
Normal file
61
framework/Inspectron.Camera/CameraParameter.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Inspectron.Camera
|
||||
{
|
||||
/// <summary>
|
||||
/// Standard implementation of ICameraParameter interface
|
||||
/// </summary>
|
||||
public class CameraParameter : ICameraParameter, INotifyPropertyChanged
|
||||
{
|
||||
public CameraParameter(string name, Func<object> get, Action<object> set,
|
||||
IEnumerable<object> values = null)
|
||||
{
|
||||
Name = name;
|
||||
this.setter = set;
|
||||
this.getter = get;
|
||||
Values = new ReadOnlyCollection<object>(values != null ?
|
||||
values.ToList() : new List<object>());
|
||||
}
|
||||
|
||||
public string Name { get; private set; }
|
||||
|
||||
public object Value
|
||||
{
|
||||
get
|
||||
{
|
||||
return getter();
|
||||
}
|
||||
set
|
||||
{
|
||||
if (setter == null)
|
||||
throw new NotSupportedException("This is a read-only parameter");
|
||||
|
||||
setter(value);
|
||||
|
||||
RaisePropertyChanged("Value");
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsReadOnly { get { return setter == null; } }
|
||||
|
||||
public ReadOnlyCollection<object> Values { get; private set; }
|
||||
|
||||
|
||||
Action<object> setter;
|
||||
Func<object> getter;
|
||||
|
||||
private void RaisePropertyChanged(string prop)
|
||||
{
|
||||
if (PropertyChanged != null)
|
||||
PropertyChanged(this, new PropertyChangedEventArgs(prop));
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
}
|
||||
}
|
||||
167
framework/Inspectron.Camera/Emulation/EmulationCamera.cs
Normal file
167
framework/Inspectron.Camera/Emulation/EmulationCamera.cs
Normal file
@@ -0,0 +1,167 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Inspectron.Camera.Image;
|
||||
|
||||
namespace Inspectron.Camera.Emulation
|
||||
{
|
||||
public class EmulationCamera : ICamera
|
||||
{
|
||||
private string[] _selectedFiles = new string[0];
|
||||
private int _index = 0;
|
||||
private string _currentFolder;
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_index = 0;
|
||||
}
|
||||
|
||||
public EmulationCamera(ICameraProvider provider)
|
||||
{
|
||||
Provider = provider;
|
||||
}
|
||||
|
||||
public bool SingleRun { get; set; }=false;
|
||||
|
||||
|
||||
public void SetFolder(string imagesFolder)
|
||||
{
|
||||
try
|
||||
{
|
||||
_currentFolder = imagesFolder;
|
||||
var path = Path.Combine(@"..\Data\Emulation", imagesFolder);
|
||||
_selectedFiles = Directory.GetFiles(path, "*.bmp")
|
||||
.Concat(Directory.GetFiles(path, "*.png"))
|
||||
.Concat(Directory.GetFiles(path, "*.jpg"))
|
||||
.Where(x => !Path.GetFileNameWithoutExtension(x).Contains("_analysis"))
|
||||
.ToArray();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public void SetFolderAbsolute(string imagesFolder)
|
||||
{
|
||||
try
|
||||
{
|
||||
_currentFolder = imagesFolder;
|
||||
var path = imagesFolder;
|
||||
_selectedFiles = Directory.GetFiles(path, "*.bmp")
|
||||
.Concat(Directory.GetFiles(path, "*.png"))
|
||||
.Concat(Directory.GetFiles(path, "*.jpg"))
|
||||
.Where(x => !Path.GetFileNameWithoutExtension(x).Contains("_analysis"))
|
||||
.ToArray();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public string Name { get; set; } = "Emulation";
|
||||
|
||||
public void Open()
|
||||
{
|
||||
IsOpen = true;
|
||||
}
|
||||
|
||||
public bool IsOpen { get; private set; }
|
||||
|
||||
public void Close()
|
||||
{
|
||||
IsOpen = false;
|
||||
}
|
||||
|
||||
public bool IsGrabbingContinuous { get; }
|
||||
|
||||
public void StartGrabContinuous()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void StopGrabContinuous()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void LoadParameters(string parametersFile)
|
||||
{
|
||||
}
|
||||
|
||||
public void SaveParameters(string parametersFile)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SaveParametersToDevice()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public int CycleDelay { get; set; } = 1;
|
||||
public int PerImageDelay { get; set; } = 5000;
|
||||
public event Action OnLoopOver=delegate{};
|
||||
public IImage GrabSingle()
|
||||
{
|
||||
if (_selectedFiles.Length == 0) return null;
|
||||
var id = (_index) % _selectedFiles.Length;
|
||||
if (id == 0)
|
||||
{
|
||||
if(_index>0)
|
||||
OnLoopOver();
|
||||
if (SingleRun&&_index>0)
|
||||
{
|
||||
// infinite sleep
|
||||
Thread.Sleep(Timeout.Infinite);
|
||||
}
|
||||
Console.WriteLine("starting new emulation cycle");
|
||||
Thread.Sleep(CycleDelay);
|
||||
}
|
||||
else
|
||||
{
|
||||
Thread.Sleep(PerImageDelay);
|
||||
}
|
||||
|
||||
var path = _selectedFiles[id];
|
||||
_index++;
|
||||
var bmp = new Bitmap(path);
|
||||
|
||||
Console.WriteLine($"Getting image {path}");
|
||||
|
||||
return new BitmapImage(bmp);
|
||||
}
|
||||
|
||||
public event ImageGrabbedHandler ImageGrabbed;
|
||||
public ICameraCapabilities Capabilities { get; } = new EmulationCameraCapabilities();
|
||||
|
||||
public IEnumerable<ICameraParameter> Parameters => new List<ICameraParameter>()
|
||||
{
|
||||
new CameraParameter("Folder", () => this.CurrentFolder, x => CurrentFolder = (string) x)
|
||||
};
|
||||
|
||||
public ICameraProvider Provider { get; }
|
||||
|
||||
public string CurrentFolder
|
||||
{
|
||||
get => _currentFolder;
|
||||
set => _currentFolder = value;
|
||||
}
|
||||
|
||||
public int Index
|
||||
{
|
||||
get => _index;
|
||||
set => _index = value;
|
||||
}
|
||||
|
||||
public IEnumerable<(Bitmap bmp, string path)> GetImages()
|
||||
{
|
||||
foreach (string selectedFile in _selectedFiles)
|
||||
{
|
||||
yield return (new Bitmap(selectedFile), selectedFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Inspectron.Camera.Emulation
|
||||
{
|
||||
public class EmulationCameraCapabilities:ICameraCapabilities
|
||||
{
|
||||
public bool CanGrabSingle { get; } = true;
|
||||
public bool CanGrabContinuous { get; } = false;
|
||||
public bool CanSaveParametersToFile { get; } = false;
|
||||
public bool CanSaveParametersToDevice { get; } = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
|
||||
namespace Inspectron.Camera.Emulation
|
||||
{
|
||||
public class EmulationCameraProvider:ICameraProvider
|
||||
{
|
||||
|
||||
private EmulationCamera _emulationCamera;
|
||||
|
||||
public EmulationCameraProvider(params string[] emulationFolders)
|
||||
{
|
||||
List<ICamera> cameras = new List<ICamera>();
|
||||
foreach (string s in emulationFolders)
|
||||
{
|
||||
_emulationCamera = new EmulationCamera(this);
|
||||
_emulationCamera.Name = "Emulation "+Path.GetFileNameWithoutExtension(s);
|
||||
_emulationCamera.SetFolder(s);
|
||||
cameras.Add(_emulationCamera);
|
||||
}
|
||||
|
||||
Cameras=new ReadOnlyCollection<ICamera>(cameras);
|
||||
}
|
||||
|
||||
public string Name { get; } = "Emulation camera provider";
|
||||
public ReadOnlyCollection<ICamera> Discover()
|
||||
{
|
||||
return Cameras;
|
||||
}
|
||||
|
||||
public ReadOnlyCollection<ICamera> Cameras { get; }
|
||||
}
|
||||
}
|
||||
10
framework/Inspectron.Camera/Enums/EStandardRotation.cs
Normal file
10
framework/Inspectron.Camera/Enums/EStandardRotation.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace Inspectron.Camera.Enums
|
||||
{
|
||||
public enum EStandardRotation
|
||||
{
|
||||
Rot0,
|
||||
Rot90,
|
||||
Rot180,
|
||||
Rot270
|
||||
}
|
||||
}
|
||||
91
framework/Inspectron.Camera/ICamera.cs
Normal file
91
framework/Inspectron.Camera/ICamera.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
using System.Collections.Generic;
|
||||
using Inspectron.Camera.Image;
|
||||
|
||||
namespace Inspectron.Camera
|
||||
{
|
||||
public interface ICamera
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the name of the camera
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Opens the camera
|
||||
/// </summary>
|
||||
void Open();
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the camera is opened
|
||||
/// </summary>
|
||||
bool IsOpen { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Closes the camera
|
||||
/// </summary>
|
||||
void Close();
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the camera is live
|
||||
/// </summary>
|
||||
bool IsGrabbingContinuous { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Stats live asynchronous acquisition. Images are transmitted the ImageGrabbed Event
|
||||
/// </summary>
|
||||
void StartGrabContinuous();
|
||||
|
||||
/// <summary>
|
||||
/// Stops the asynchronous acquisition.
|
||||
/// </summary>
|
||||
void StopGrabContinuous();
|
||||
|
||||
/// <summary>
|
||||
/// Loads the parameters from a filename
|
||||
/// </summary>
|
||||
/// <param name="parameters_file"></param>
|
||||
void LoadParameters(string parametersFile);
|
||||
|
||||
/// <summary>
|
||||
/// Saves the camera parameters to the given filename
|
||||
/// </summary>
|
||||
/// <param name="parameters_file"></param>
|
||||
void SaveParameters(string parametersFile);
|
||||
|
||||
/// <summary>
|
||||
/// Saves the parameters to the camera memory
|
||||
/// </summary>
|
||||
void SaveParametersToDevice();
|
||||
|
||||
/// <summary>
|
||||
/// Synchronously grab an image
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IImage GrabSingle();
|
||||
|
||||
/// <summary>
|
||||
/// Event occuring when a new frame is acquired
|
||||
/// </summary>
|
||||
event ImageGrabbedHandler ImageGrabbed;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the capabilities of the camera
|
||||
/// </summary>
|
||||
ICameraCapabilities Capabilities { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a Ienumerable containing all of the camera
|
||||
/// </summary>
|
||||
IEnumerable<ICameraParameter> Parameters { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Reference to Parent Camera Provider
|
||||
/// </summary>
|
||||
ICameraProvider Provider { get; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Handler called when an image has been grabbed
|
||||
/// </summary>
|
||||
/// <param name="image">the grabbed image</param>
|
||||
public delegate void ImageGrabbedHandler(ICamera sender, IImage image);
|
||||
}
|
||||
25
framework/Inspectron.Camera/ICameraCapabilities.cs
Normal file
25
framework/Inspectron.Camera/ICameraCapabilities.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
namespace Inspectron.Camera
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface specifying the capabilities of the camera
|
||||
/// </summary>
|
||||
public interface ICameraCapabilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates that handle synchronous acquisition. (see ICamera Grab method)
|
||||
/// </summary>
|
||||
bool CanGrabSingle { get; }
|
||||
/// <summary>
|
||||
/// Indicates that handle live asynchronous acquisition. Images are transmitted the ImageGrabbed Event. (see ICamera StartLive / StopLive methods)
|
||||
/// </summary>
|
||||
bool CanGrabContinuous { get; }
|
||||
/// <summary>
|
||||
/// Indicates that the camera can save and load the current parameter set to a file
|
||||
/// </summary>
|
||||
bool CanSaveParametersToFile { get; }
|
||||
/// <summary>
|
||||
/// Indicates that the camera can save the current parameter set to memory
|
||||
/// </summary>
|
||||
bool CanSaveParametersToDevice { get; }
|
||||
}
|
||||
}
|
||||
27
framework/Inspectron.Camera/ICameraParameter.cs
Normal file
27
framework/Inspectron.Camera/ICameraParameter.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Inspectron.Camera
|
||||
{
|
||||
/// <summary>
|
||||
/// ICameraParameter interface, standard interface to list and change camera parameters
|
||||
/// </summary>
|
||||
public interface ICameraParameter
|
||||
{
|
||||
/// <summary>
|
||||
/// User friendly name
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
/// <summary>
|
||||
/// Getter and setter to values, null setter if readonly
|
||||
/// </summary>
|
||||
object Value { get; set; }
|
||||
/// <summary>
|
||||
/// Indicates that the parameter is readonly, Value setter is null
|
||||
/// </summary>
|
||||
bool IsReadOnly { get; }
|
||||
/// <summary>
|
||||
/// Values that can be chosen in case of fixed parameters set.
|
||||
/// </summary>
|
||||
ReadOnlyCollection<object> Values { get; }
|
||||
}
|
||||
}
|
||||
22
framework/Inspectron.Camera/ICameraProvider.cs
Normal file
22
framework/Inspectron.Camera/ICameraProvider.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Inspectron.Camera
|
||||
{
|
||||
public interface ICameraProvider
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
ReadOnlyCollection<ICamera> Discover();
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
ReadOnlyCollection<ICamera> Cameras { get; }
|
||||
}
|
||||
}
|
||||
35
framework/Inspectron.Camera/Image/BitmapImage.cs
Normal file
35
framework/Inspectron.Camera/Image/BitmapImage.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
|
||||
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
|
||||
namespace Inspectron.Camera.Image
|
||||
{
|
||||
public class BitmapImage : IImage
|
||||
{
|
||||
private readonly Bitmap _bitmap;
|
||||
|
||||
public BitmapImage(Bitmap bitmap)
|
||||
{
|
||||
_bitmap = bitmap;
|
||||
}
|
||||
|
||||
public int Width => _bitmap.Width;
|
||||
|
||||
public int Height => _bitmap.Height;
|
||||
|
||||
public int Channels => throw new System.NotImplementedException();
|
||||
|
||||
public Bitmap Bitmap => (Bitmap)_bitmap.Clone();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Save(string path)
|
||||
{
|
||||
_bitmap.Save(path,ImageFormat.Bmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
131
framework/Inspectron.Camera/Image/ByteImage.cs
Normal file
131
framework/Inspectron.Camera/Image/ByteImage.cs
Normal file
@@ -0,0 +1,131 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Inspectron.Camera.Image
|
||||
{
|
||||
public class ByteImage : IImage
|
||||
{
|
||||
|
||||
Byte[] data;
|
||||
int step;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a newinstance with the given image dimensions and pixel-data
|
||||
/// </summary>
|
||||
/// <param name="width">the image width in pixel</param>
|
||||
/// <param name="height">the image height in pixel</param>
|
||||
/// <param name="channels">the number of color channels (1-4)</param>
|
||||
/// <param name="channelDepth">the bit-depth per channel (8 or 16)</param>
|
||||
/// <param name="data">pointer to the pixel data in unmanaged memory</param>
|
||||
/// <param name="step">the number of bytes per image row</param>
|
||||
public ByteImage(int width, int height, int channels,
|
||||
Byte[] data, int step)
|
||||
{
|
||||
Width = width;
|
||||
Height = height;
|
||||
Channels = channels;
|
||||
|
||||
this.data = data;
|
||||
this.step = step;
|
||||
|
||||
ProcessBitmapCache();
|
||||
}
|
||||
|
||||
private void ProcessBitmapCache()
|
||||
{
|
||||
var image = new Bitmap(Width, Height,
|
||||
Channels == 1 ? PixelFormat.Format8bppIndexed : PixelFormat.Format24bppRgb);
|
||||
var lockedDst = image.LockBits(new Rectangle(0, 0, Width, Height), ImageLockMode.WriteOnly,
|
||||
image.PixelFormat);
|
||||
Marshal.Copy(data, 0, lockedDst.Scan0, data.Length);
|
||||
image.UnlockBits(lockedDst);
|
||||
if (Channels == 1)
|
||||
{
|
||||
image.Palette = _monoPalette;
|
||||
}
|
||||
|
||||
_cachedBMP = image;
|
||||
}
|
||||
|
||||
static ColorPalette GetGrayScalePalette()
|
||||
{
|
||||
Bitmap bmp = new Bitmap(1, 1, PixelFormat.Format8bppIndexed);
|
||||
|
||||
ColorPalette monoPalette = bmp.Palette;
|
||||
|
||||
Color[] entries = monoPalette.Entries;
|
||||
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
entries[i] = Color.FromArgb(i, i, i);
|
||||
}
|
||||
|
||||
return monoPalette;
|
||||
}
|
||||
|
||||
private static ColorPalette _monoPalette = GetGrayScalePalette();
|
||||
public int Width { get; private set; }
|
||||
public int Height { get; private set; }
|
||||
public int Channels { get; private set; }
|
||||
|
||||
public IImageLock Lock { get { return new ImageLock(this); } }
|
||||
private Bitmap _cachedBMP = null;
|
||||
public Bitmap Bitmap
|
||||
{
|
||||
get
|
||||
{
|
||||
|
||||
|
||||
return (Bitmap)_cachedBMP.Clone();
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] Data
|
||||
{
|
||||
get => data;
|
||||
set => data = value;
|
||||
}
|
||||
|
||||
public int Step
|
||||
{
|
||||
get => step;
|
||||
set => step = value;
|
||||
}
|
||||
|
||||
class ImageLock : IImageLock
|
||||
{
|
||||
ByteImage parent;
|
||||
GCHandle pinnedArray;
|
||||
internal ImageLock(ByteImage parent) { this.parent = parent; }
|
||||
|
||||
public int Step { get { return parent.step; } }
|
||||
|
||||
public IntPtr PixelData
|
||||
{
|
||||
get
|
||||
{
|
||||
pinnedArray = GCHandle.Alloc(parent.data, GCHandleType.Pinned);
|
||||
IntPtr pointer = pinnedArray.AddrOfPinnedObject();
|
||||
return pointer;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
pinnedArray.Free();
|
||||
}
|
||||
}
|
||||
public void Save(string path)
|
||||
{
|
||||
(new Bitmap(new MemoryStream(data))).Save(path,ImageFormat.Bmp);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
data = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
18
framework/Inspectron.Camera/Image/Extensions.cs
Normal file
18
framework/Inspectron.Camera/Image/Extensions.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
|
||||
namespace Inspectron.Camera.Image
|
||||
{
|
||||
public static class Extensions
|
||||
{
|
||||
public static ByteImage ToByteImage(this Bitmap self,int channels)
|
||||
{
|
||||
MemoryStream ms = new MemoryStream();
|
||||
var bmp = self;
|
||||
bmp.Save(ms, ImageFormat.Bmp);
|
||||
ByteImage bi = new ByteImage(bmp.Width, bmp.Height, channels, ms.ToArray(), bmp.Width);
|
||||
return bi;
|
||||
}
|
||||
}
|
||||
}
|
||||
14
framework/Inspectron.Camera/Image/IImage.cs
Normal file
14
framework/Inspectron.Camera/Image/IImage.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Drawing.Imaging;
|
||||
|
||||
namespace Inspectron.Camera.Image
|
||||
{
|
||||
public interface IImage:IDisposable
|
||||
{
|
||||
int Width { get; }
|
||||
int Height { get; }
|
||||
int Channels { get; }
|
||||
void Save(string path);
|
||||
System.Drawing.Bitmap Bitmap { get; }
|
||||
}
|
||||
}
|
||||
10
framework/Inspectron.Camera/Image/IImageLock.cs
Normal file
10
framework/Inspectron.Camera/Image/IImageLock.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace Inspectron.Camera.Image
|
||||
{
|
||||
public interface IImageLock:IDisposable
|
||||
{
|
||||
int Step { get; }
|
||||
IntPtr PixelData { get; }
|
||||
}
|
||||
}
|
||||
17
framework/Inspectron.Camera/Inspectron.Camera.csproj
Normal file
17
framework/Inspectron.Camera/Inspectron.Camera.csproj
Normal file
@@ -0,0 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Ninject" Version="3.3.4" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="5.0.2" />
|
||||
<PackageReference Include="Serilog" Version="4.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="NoImage\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
74
framework/Inspectron.Camera/NoImage/NoImageCamera.cs
Normal file
74
framework/Inspectron.Camera/NoImage/NoImageCamera.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Inspectron.Camera.Image;
|
||||
|
||||
namespace Inspectron.Camera.NoImage
|
||||
{
|
||||
public class NoImageCamera:ICamera
|
||||
{
|
||||
public NoImageCamera(ICameraProvider provider)
|
||||
{
|
||||
Provider = provider;
|
||||
}
|
||||
|
||||
private int _acquisitionDelay = 0;
|
||||
public string Name { get; }
|
||||
public void Open()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public bool IsOpen { get; }
|
||||
public void Close()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public bool IsGrabbingContinuous { get; private set; }
|
||||
|
||||
public void StartGrabContinuous()
|
||||
{
|
||||
IsGrabbingContinuous = true;
|
||||
}
|
||||
|
||||
public void StopGrabContinuous()
|
||||
{
|
||||
IsGrabbingContinuous = false;
|
||||
}
|
||||
|
||||
public void LoadParameters(string parametersFile)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public void SaveParameters(string parametersFile)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public void SaveParametersToDevice()
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public int AcquisitionDelay
|
||||
{
|
||||
get => _acquisitionDelay;
|
||||
set => _acquisitionDelay = value;
|
||||
}
|
||||
|
||||
public IImage GrabSingle()
|
||||
{
|
||||
Thread.Sleep(AcquisitionDelay);
|
||||
return null;
|
||||
}
|
||||
|
||||
public event ImageGrabbedHandler ImageGrabbed=delegate{};
|
||||
public ICameraCapabilities Capabilities =>new NoImageCameraCapabilities();
|
||||
public IEnumerable<ICameraParameter> Parameters =>new List<ICameraParameter>()
|
||||
{
|
||||
new CameraParameter("AcquisitionDelay",() => this.AcquisitionDelay,x=>AcquisitionDelay=(int)x)
|
||||
};
|
||||
public ICameraProvider Provider { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Inspectron.Camera.NoImage
|
||||
{
|
||||
public class NoImageCameraCapabilities:ICameraCapabilities
|
||||
{
|
||||
public bool CanGrabSingle { get; }=true;
|
||||
public bool CanGrabContinuous { get; } = true;
|
||||
public bool CanSaveParametersToFile { get; } = false;
|
||||
public bool CanSaveParametersToDevice { get; } = false;
|
||||
}
|
||||
}
|
||||
18
framework/Inspectron.Camera/NoImage/NoImageCameraProvider.cs
Normal file
18
framework/Inspectron.Camera/NoImage/NoImageCameraProvider.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
|
||||
namespace Inspectron.Camera.NoImage
|
||||
{
|
||||
public class NoImageCameraProvider : ICameraProvider
|
||||
{
|
||||
public string Name { get; } = "No image camera provider";
|
||||
public int CamerasToDiscover { get; set; } = 1;
|
||||
public ReadOnlyCollection<ICamera> Discover()
|
||||
{
|
||||
return Enumerable.Range(0, CamerasToDiscover).Select(x => (ICamera)new NoImageCamera(this)).ToList().AsReadOnly();
|
||||
}
|
||||
|
||||
public ReadOnlyCollection<ICamera> Cameras { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
using Inspectron.Fastbuffer.Interfaces;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Inspectron.Fastbuffer.Filesystems;
|
||||
|
||||
public class AsyncFilesystem : IFilesystem
|
||||
{
|
||||
private readonly DirectFilesystem _directFilesystem = new();
|
||||
private readonly ConcurrentDictionary<string, byte[]> _buffer = new();
|
||||
private readonly BlockingCollection<(string path, byte[] data)> _writeQueue = new();
|
||||
private readonly Thread _backgroundThread;
|
||||
private readonly object _lock = new();
|
||||
|
||||
public AsyncFilesystem()
|
||||
{
|
||||
_backgroundThread = new Thread(ProcessWriteQueue) { IsBackground = true };
|
||||
_backgroundThread.Name= "AsyncFilesystem";
|
||||
_backgroundThread.Start();
|
||||
}
|
||||
|
||||
public void Write(string path, byte[] data)
|
||||
{
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
_buffer[path] = data;
|
||||
_writeQueue.Add((path, data));
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] Read(string path)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_buffer.TryGetValue(path, out var data))
|
||||
{
|
||||
return data;
|
||||
}
|
||||
}
|
||||
return _directFilesystem.Read(path);
|
||||
}
|
||||
|
||||
public void Delete(string path)
|
||||
{
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
// pats is in buffer, so it's not written to disk yet
|
||||
if (_buffer.TryRemove(path, out _))
|
||||
{
|
||||
return;
|
||||
}
|
||||
_directFilesystem.Delete(path);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void ProcessWriteQueue()
|
||||
{
|
||||
foreach (var (path, data) in _writeQueue.GetConsumingEnumerable())
|
||||
{
|
||||
_directFilesystem.Write(path, data);
|
||||
lock (_lock)
|
||||
_buffer.TryRemove(path, out _);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_writeQueue.CompleteAdding();
|
||||
_backgroundThread.Join();
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
while (_writeQueue.TryTake(out _)) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using Inspectron.Fastbuffer.Interfaces;
|
||||
using Serilog;
|
||||
|
||||
namespace Inspectron.Fastbuffer.Filesystems;
|
||||
|
||||
public class DirectFilesystem:IFilesystem
|
||||
{
|
||||
public void Write(string path, byte[] data)
|
||||
{
|
||||
// ensure path
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||
File.WriteAllBytes(path, data);
|
||||
}
|
||||
|
||||
public byte[] Read(string path)
|
||||
{
|
||||
return File.ReadAllBytes(path);
|
||||
}
|
||||
|
||||
public void Delete(string path)
|
||||
{
|
||||
Log.Debug($"Deleting {path}");
|
||||
if (File.Exists(path))
|
||||
File.Delete(path);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
150
framework/Inspectron.Fastbuffer/IndexFile.cs
Normal file
150
framework/Inspectron.Fastbuffer/IndexFile.cs
Normal file
@@ -0,0 +1,150 @@
|
||||
using System.Text;
|
||||
using Inspectron.Fastbuffer.Interfaces;
|
||||
using Serilog;
|
||||
|
||||
namespace Inspectron.Fastbuffer;
|
||||
|
||||
public class IndexFile
|
||||
{
|
||||
private readonly string _indexFilePath;
|
||||
private readonly int _bufferSize;
|
||||
private readonly IFilesystem _filesystem;
|
||||
private readonly string? _bufferPath;
|
||||
|
||||
private const int RecordSize = 512;
|
||||
private const int IndexSize = 4;
|
||||
private const int HeaderSize = 4;
|
||||
|
||||
public IndexFile(string path, int bufferSize, IFilesystem filesystem)
|
||||
{
|
||||
_indexFilePath = path;
|
||||
_bufferSize = bufferSize;
|
||||
_filesystem = filesystem;
|
||||
ValidateFile();
|
||||
_bufferPath = Path.Combine(Path.GetDirectoryName(_indexFilePath), "..");
|
||||
}
|
||||
|
||||
private void ValidateFile()
|
||||
{
|
||||
if (!File.Exists(_indexFilePath))
|
||||
{
|
||||
CreateFile();
|
||||
}
|
||||
// check file size
|
||||
var fileInfo = new FileInfo(_indexFilePath);
|
||||
if (fileInfo.Length != _bufferSize * RecordSize + HeaderSize)
|
||||
{
|
||||
UpdateFile();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateFile()
|
||||
{
|
||||
using var fileStream = File.Open(_indexFilePath, FileMode.Open);
|
||||
var actualSize = fileStream.Length;
|
||||
if (actualSize < _bufferSize+ HeaderSize)
|
||||
{
|
||||
fileStream.Seek(0, SeekOrigin.End);
|
||||
var buffer = new byte[_bufferSize * RecordSize - actualSize];
|
||||
fileStream.Write(buffer, HeaderSize, buffer.Length);
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateFile()
|
||||
{
|
||||
using var fileStream = File.Create(_indexFilePath);
|
||||
var buffer = new byte[_bufferSize* RecordSize + HeaderSize];
|
||||
fileStream.Write(buffer, 0, buffer.Length);
|
||||
fileStream.Seek(0, SeekOrigin.Begin);
|
||||
var bytes = BitConverter.GetBytes(-1);
|
||||
fileStream.Write(bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
public int GetCurrentArtifactId()
|
||||
{
|
||||
using var fileStream = File.Open(_indexFilePath, FileMode.Open);
|
||||
using var reader = new BinaryReader(fileStream);
|
||||
return reader.ReadInt32();
|
||||
}
|
||||
|
||||
public void SetCurrentArtifactId(int id)
|
||||
{
|
||||
using var fileStream = File.Open(_indexFilePath, FileMode.Open);
|
||||
using var writer = new BinaryWriter(fileStream);
|
||||
writer.Write(id);
|
||||
}
|
||||
|
||||
public string GetArtifactPath(int id)
|
||||
{
|
||||
using var fileStream = File.Open(_indexFilePath, FileMode.Open);
|
||||
fileStream.Seek(id * RecordSize+HeaderSize, SeekOrigin.Begin);
|
||||
byte[] bytes = new byte[RecordSize];
|
||||
fileStream.Read(bytes, 0, RecordSize);
|
||||
return Encoding.UTF8.GetString(bytes).TrimEnd('\0');
|
||||
}
|
||||
|
||||
public int GetNextId()
|
||||
{
|
||||
var currentId = GetCurrentArtifactId();
|
||||
if (currentId + 1 >= _bufferSize)
|
||||
{
|
||||
currentId = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentId++;
|
||||
}
|
||||
return currentId;
|
||||
}
|
||||
|
||||
public void DeleteArtifact(int id)
|
||||
{
|
||||
var relPath = GetArtifactPath(id);
|
||||
var filePath = Path.Combine(_bufferPath, relPath);
|
||||
try
|
||||
{
|
||||
_filesystem.Delete(filePath);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error("Error deleting artifact {id}: {message}",id,e.Message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void WriteNextArtifact(byte[] data, string fileName)
|
||||
{
|
||||
var id = GetNextId();
|
||||
Log.Debug("Writing artifact {id}",id);
|
||||
|
||||
var existingPath = GetArtifactPath(id);
|
||||
Log.Debug("Existing path: {existingPath}", existingPath);
|
||||
|
||||
DeleteArtifact(id);
|
||||
var filePath = Path.Combine(_bufferPath, fileName);
|
||||
_filesystem.Write(filePath, data);
|
||||
SetCurrentArtifactId(id);
|
||||
using var fileStream = File.Open(_indexFilePath, FileMode.Open);
|
||||
fileStream.Seek(id * RecordSize+ HeaderSize, SeekOrigin.Begin);
|
||||
var bytes = Encoding.UTF8.GetBytes(fileName);
|
||||
fileStream.Write(bytes, 0, bytes.Length);
|
||||
var padding = new byte[RecordSize - bytes.Length];
|
||||
fileStream.Write(padding, 0, padding.Length);
|
||||
}
|
||||
|
||||
public byte[] ReadArtifact(int id)
|
||||
{
|
||||
using var fileStream = File.Open(_indexFilePath, FileMode.Open);
|
||||
fileStream.Seek(id * RecordSize+ HeaderSize, SeekOrigin.Begin);
|
||||
var bytes = new byte[RecordSize];
|
||||
fileStream.Read(bytes, 0, RecordSize);
|
||||
var fileName = Encoding.UTF8.GetString(bytes).TrimEnd('\0');
|
||||
var filePath = Path.Combine(_bufferPath, fileName);
|
||||
return _filesystem.Read(filePath);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
17
framework/Inspectron.Fastbuffer/Inspectron.Fastbuffer.csproj
Normal file
17
framework/Inspectron.Fastbuffer/Inspectron.Fastbuffer.csproj
Normal file
@@ -0,0 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Interfaces\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Serilog" Version="4.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
31
framework/Inspectron.Fastbuffer/Inspectron.Fastbuffer.sln
Normal file
31
framework/Inspectron.Fastbuffer/Inspectron.Fastbuffer.sln
Normal file
@@ -0,0 +1,31 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.10.34928.147
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Inspectron.Fastbuffer", "Inspectron.Fastbuffer.csproj", "{697FB408-0899-423C-8D67-C4ABDBED0C09}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Inspectron.Fastbuffer.Tests", "..\Inspectron.Fastbuffer.Tests\Inspectron.Fastbuffer.Tests.csproj", "{5D1F5AAE-20DC-4C4A-B361-D1D42230BDF7}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{697FB408-0899-423C-8D67-C4ABDBED0C09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{697FB408-0899-423C-8D67-C4ABDBED0C09}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{697FB408-0899-423C-8D67-C4ABDBED0C09}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{697FB408-0899-423C-8D67-C4ABDBED0C09}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5D1F5AAE-20DC-4C4A-B361-D1D42230BDF7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5D1F5AAE-20DC-4C4A-B361-D1D42230BDF7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5D1F5AAE-20DC-4C4A-B361-D1D42230BDF7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5D1F5AAE-20DC-4C4A-B361-D1D42230BDF7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {F3769CF4-894E-4BEE-9366-89E7505F27BF}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Inspectron.Fastbuffer.Interfaces;
|
||||
|
||||
public interface IFilesystem:IDisposable
|
||||
{
|
||||
public void Write(string path, byte[] data);
|
||||
public byte[] Read(string path);
|
||||
public void Delete(string path);
|
||||
}
|
||||
38
framework/Inspectron.Fastbuffer/IsolatedRingbuffer.cs
Normal file
38
framework/Inspectron.Fastbuffer/IsolatedRingbuffer.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using Inspectron.Fastbuffer.Interfaces;
|
||||
|
||||
namespace Inspectron.Fastbuffer;
|
||||
|
||||
public class IsolatedRingbuffer: IDisposable
|
||||
{
|
||||
private readonly string _path;
|
||||
private readonly int _bufferSize;
|
||||
private readonly IFilesystem _filesystem;
|
||||
private readonly string _indexPath;
|
||||
private readonly IndexFile _index;
|
||||
|
||||
public const string BufferDirectory = ".rb";
|
||||
public const string IndexFile = "index.idx";
|
||||
|
||||
public IsolatedRingbuffer(string path, int bufferSize, IFilesystem filesystem)
|
||||
{
|
||||
_path = path;
|
||||
_bufferSize = bufferSize;
|
||||
_filesystem = filesystem;
|
||||
|
||||
_indexPath = Path.Combine(_path, BufferDirectory,IndexFile );
|
||||
Directory.CreateDirectory(Path.Combine(_path, BufferDirectory));
|
||||
|
||||
_index = new IndexFile(_indexPath, _bufferSize, _filesystem);
|
||||
|
||||
}
|
||||
|
||||
public void Write(byte[] data, string fileName)
|
||||
{
|
||||
_index.WriteNextArtifact(data, fileName);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_filesystem.Dispose();
|
||||
}
|
||||
}
|
||||
52
framework/Inspectron.Fastbuffer/RingbufferRepository.cs
Normal file
52
framework/Inspectron.Fastbuffer/RingbufferRepository.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using Inspectron.Fastbuffer.Interfaces;
|
||||
|
||||
namespace Inspectron.Fastbuffer
|
||||
{
|
||||
public class RingbufferRepository
|
||||
{
|
||||
private readonly string _path;
|
||||
|
||||
private readonly string _goodIndexPath;
|
||||
private readonly string _badIndexPath;
|
||||
private readonly IndexFile _goodIndex;
|
||||
private readonly IndexFile _badIndex;
|
||||
public const string BufferDirectory = ".rb";
|
||||
public const string GoodIndexFile = "good.idx";
|
||||
public const string BadIndexFile = "bad.idx";
|
||||
public RingbufferRepository(string path, int goodBufferSize, int badBufferSize, IFilesystem filesystem)
|
||||
{
|
||||
_path = path;
|
||||
|
||||
_goodIndexPath = Path.Combine(_path, BufferDirectory, GoodIndexFile);
|
||||
_badIndexPath = Path.Combine(_path, BufferDirectory, BadIndexFile);
|
||||
Directory.CreateDirectory(Path.Combine(_path,BufferDirectory));
|
||||
_goodIndex = new IndexFile(_goodIndexPath, goodBufferSize, filesystem);
|
||||
_badIndex = new IndexFile(_badIndexPath, badBufferSize, filesystem);
|
||||
|
||||
var id= _goodIndex.GetCurrentArtifactId();
|
||||
Console.WriteLine(@"RB size: "+id);
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
var artifact = _goodIndex.GetArtifactPath(i);
|
||||
Console.WriteLine(@"Artifact: "+artifact);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public IndexFile GoodIndex => _goodIndex;
|
||||
|
||||
public IndexFile BadIndex => _badIndex;
|
||||
|
||||
public void WriteGood(byte[] data, string fileName)
|
||||
{
|
||||
_goodIndex.WriteNextArtifact(data, fileName);
|
||||
}
|
||||
|
||||
public void WriteBad(byte[] data, string fileName)
|
||||
{
|
||||
_badIndex.WriteNextArtifact(data, fileName);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
92
framework/Inspectron.Ringbuffer/Index/GroupIndex.cs
Normal file
92
framework/Inspectron.Ringbuffer/Index/GroupIndex.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Inspectron.Ringbuffer.Interfaces;
|
||||
|
||||
namespace Inspectron.Ringbuffer
|
||||
{
|
||||
public class GroupIndex:IIndex
|
||||
{
|
||||
private readonly int _groupPathPart;
|
||||
private readonly int _goodBadPathPart;
|
||||
|
||||
public GroupIndex(int groupPathPart,int goodBadPathPart)
|
||||
{
|
||||
_groupPathPart = groupPathPart;
|
||||
_goodBadPathPart = goodBadPathPart;
|
||||
}
|
||||
|
||||
|
||||
private readonly object _lock = new object();
|
||||
private readonly Dictionary<string, ProductResults> _groups = new Dictionary<string, ProductResults>();
|
||||
|
||||
|
||||
public void Track(string filePath)
|
||||
{
|
||||
|
||||
var parts = Path.GetDirectoryName(filePath).Split(Path.DirectorySeparatorChar);
|
||||
|
||||
string groupKey;
|
||||
try
|
||||
{
|
||||
groupKey = Path.Combine(Enumerable.Range(0, _groupPathPart + 1).Select(x => parts[x]).ToArray());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine($"Tracking {filePath}");
|
||||
Console.WriteLine(e);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
string badGoodPart;
|
||||
if (_goodBadPathPart == -1) badGoodPart = RingbufferPath.GOOD_DIR;
|
||||
else
|
||||
badGoodPart = parts[_goodBadPathPart];
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (!_groups.ContainsKey(groupKey))
|
||||
{
|
||||
_groups[groupKey] = new ProductResults();
|
||||
}
|
||||
|
||||
if (badGoodPart == RingbufferPath.GOOD_DIR)
|
||||
{
|
||||
_groups[groupKey].Good.Insert(0,filePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
_groups[groupKey].Bad.Insert(0, filePath);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public List<string> GetFilesToDelete(int allowedAmountGood, int allowedAmountBad)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
var filesToDelete = new List<string>();
|
||||
foreach (var p in _groups)
|
||||
{
|
||||
var goodToDelete = p.Value.Good.Skip(allowedAmountGood).ToList();
|
||||
var badToDelete = p.Value.Bad.Skip(allowedAmountBad).ToList();
|
||||
|
||||
goodToDelete.ForEach(x => p.Value.Good.Remove(x));
|
||||
badToDelete.ForEach(x => p.Value.Bad.Remove(x));
|
||||
filesToDelete.AddRange(goodToDelete);
|
||||
filesToDelete.AddRange(badToDelete);
|
||||
}
|
||||
|
||||
return filesToDelete;
|
||||
}
|
||||
}
|
||||
private class ProductResults
|
||||
{
|
||||
public List<string> Good { get; set; }=new List<string>();
|
||||
public List<string> Bad { get; set; }=new List<string>();
|
||||
}
|
||||
}
|
||||
}
|
||||
70
framework/Inspectron.Ringbuffer/Index/ProductIndex.cs
Normal file
70
framework/Inspectron.Ringbuffer/Index/ProductIndex.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Inspectron.Ringbuffer.Interfaces;
|
||||
|
||||
namespace Inspectron.Ringbuffer
|
||||
{
|
||||
/// <summary>
|
||||
/// Ringbuffer\Product\Good\File
|
||||
/// </summary>
|
||||
public class ProductIndex : IIndex
|
||||
{
|
||||
private readonly object _lock = new object();
|
||||
private readonly Dictionary<string, ProductResults> _products = new Dictionary<string, ProductResults>();
|
||||
|
||||
public void Track(string filePath)
|
||||
{
|
||||
var product = Path.GetDirectoryName(Path.GetDirectoryName(filePath)).Split(Path.DirectorySeparatorChar)
|
||||
.Last();
|
||||
var result = Path.GetDirectoryName(filePath).Split(Path.DirectorySeparatorChar).Last();
|
||||
lock (_lock)
|
||||
{
|
||||
if (!_products.ContainsKey(product))
|
||||
_products[product] = new ProductResults
|
||||
{
|
||||
Good = new List<string>(),
|
||||
Bad = new List<string>()
|
||||
};
|
||||
|
||||
if (result == RingbufferFolder.GOOD_DIR)
|
||||
{
|
||||
if (!_products[product].Good.Contains(filePath))
|
||||
_products[product].Good.Insert(0, filePath);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if (!_products[product].Bad.Contains(filePath))
|
||||
_products[product].Bad.Insert(0, filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<string> GetFilesToDelete(int allowedAmountGood, int allowedAmountBad)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
var filesToDelete = new List<string>();
|
||||
foreach (var p in _products)
|
||||
{
|
||||
var goodToDelete = p.Value.Good.Skip(allowedAmountGood).ToList();
|
||||
var badToDelete = p.Value.Bad.Skip(allowedAmountGood).ToList();
|
||||
|
||||
goodToDelete.ForEach(x => p.Value.Good.Remove(x));
|
||||
badToDelete.ForEach(x => p.Value.Bad.Remove(x));
|
||||
filesToDelete.AddRange(goodToDelete);
|
||||
filesToDelete.AddRange(badToDelete);
|
||||
}
|
||||
|
||||
return filesToDelete;
|
||||
}
|
||||
}
|
||||
|
||||
private class ProductResults
|
||||
{
|
||||
public List<string> Good { get; set; }
|
||||
public List<string> Bad { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
56
framework/Inspectron.Ringbuffer/Index/RootIndex.cs
Normal file
56
framework/Inspectron.Ringbuffer/Index/RootIndex.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Inspectron.Ringbuffer.Interfaces;
|
||||
|
||||
namespace Inspectron.Ringbuffer
|
||||
{
|
||||
/// <summary>
|
||||
/// Ringbuffer\Good\Product\File
|
||||
/// </summary>
|
||||
public class RootIndex : IIndex
|
||||
{
|
||||
private readonly List<string> _trackedGoodFiles = new List<string>();
|
||||
private readonly List<string> _trackedBadFiles = new List<string>();
|
||||
private readonly object _lock = new object();
|
||||
|
||||
public void Track(string filePath)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (IsGood(filePath))
|
||||
{
|
||||
if (!_trackedGoodFiles.Contains(filePath))
|
||||
_trackedGoodFiles.Insert(0, filePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_trackedBadFiles.Contains(filePath))
|
||||
_trackedBadFiles.Insert(0, filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public List<string> GetFilesToDelete(int allowedAmountGood, int allowedAmountBad)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
var resGood = _trackedGoodFiles.Skip(allowedAmountGood).ToList();
|
||||
var resBad = _trackedBadFiles.Skip(allowedAmountBad).ToList();
|
||||
|
||||
resGood.ForEach(x => { _trackedGoodFiles.Remove(x); });
|
||||
resBad.ForEach(x => { _trackedBadFiles.Remove(x); });
|
||||
|
||||
return resGood.Concat(resBad).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsGood(string filePath)
|
||||
{
|
||||
var folder = Path.GetDirectoryName(Path.GetDirectoryName(filePath)).Split(Path.DirectorySeparatorChar)
|
||||
.Last();
|
||||
return folder == RingbufferFolder.GOOD_DIR;
|
||||
}
|
||||
}
|
||||
}
|
||||
14
framework/Inspectron.Ringbuffer/Inspectron.Ringbuffer.csproj
Normal file
14
framework/Inspectron.Ringbuffer/Inspectron.Ringbuffer.csproj
Normal file
@@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<Configurations>Debug;Release;CPU</Configurations>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Factories\**" />
|
||||
<EmbeddedResource Remove="Factories\**" />
|
||||
<None Remove="Factories\**" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Inspectron.Ringbuffer.Interfaces
|
||||
{
|
||||
public interface IFileIdentity
|
||||
{
|
||||
string WriteTo(string directoryPath);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Inspectron.Ringbuffer.Interfaces
|
||||
{
|
||||
public interface IFileIdentityFactory<T>
|
||||
{
|
||||
IFileIdentity CreateFrom(T image);
|
||||
IFileVerifier CreateVerifier();
|
||||
}
|
||||
}
|
||||
10
framework/Inspectron.Ringbuffer/Interfaces/IFileVerifier.cs
Normal file
10
framework/Inspectron.Ringbuffer/Interfaces/IFileVerifier.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Inspectron.Ringbuffer.Interfaces
|
||||
{
|
||||
public interface IFileVerifier
|
||||
{
|
||||
bool IsMainFile(string filePath);
|
||||
List<string> LinkedFiles(string filePath);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Inspectron.Ringbuffer.Interfaces
|
||||
{
|
||||
public interface IFolderWritter
|
||||
{
|
||||
void WriteGood(IFileIdentity fileIdentity, string product);
|
||||
void WriteBad(IFileIdentity fileIdentity, string product);
|
||||
}
|
||||
}
|
||||
11
framework/Inspectron.Ringbuffer/Interfaces/IIndex.cs
Normal file
11
framework/Inspectron.Ringbuffer/Interfaces/IIndex.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Inspectron.Ringbuffer.Interfaces
|
||||
{
|
||||
public interface IIndex
|
||||
{
|
||||
void Track(string filePath);
|
||||
List<string> GetFilesToDelete(int allowedAmountGood,int allowedAmountBad);
|
||||
|
||||
}
|
||||
}
|
||||
25
framework/Inspectron.Ringbuffer/RingbufferConfiguration.cs
Normal file
25
framework/Inspectron.Ringbuffer/RingbufferConfiguration.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
namespace Inspectron.Ringbuffer
|
||||
{
|
||||
public class RingbufferConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// How many images should be present in Good folder
|
||||
/// After reaching this amount older images will be deleted
|
||||
/// </summary>
|
||||
public int MaxAmountOfGoodImages { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// How many images should be present in Bad folder
|
||||
/// After reaching this amount older images will be deleted
|
||||
/// </summary>
|
||||
public int MaxAmountOfBadImages { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If enabled folder structure will be like "Good\MyProduct\001.bmp
|
||||
/// If disabled folder structure will be like "MyProduct\Good\001.bmp
|
||||
/// </summary>
|
||||
public bool ProductFirst { get; set; }
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
150
framework/Inspectron.Ringbuffer/RingbufferFolder.cs
Normal file
150
framework/Inspectron.Ringbuffer/RingbufferFolder.cs
Normal file
@@ -0,0 +1,150 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Inspectron.Ringbuffer.Interfaces;
|
||||
|
||||
namespace Inspectron.Ringbuffer
|
||||
{
|
||||
[Obsolete]
|
||||
public class RingbufferFolder:IFolderWritter
|
||||
{
|
||||
|
||||
private string _ringbufferFolder;
|
||||
private RingbufferConfiguration _configuration;
|
||||
private IFileVerifier _fileVerifier;
|
||||
|
||||
private IIndex _index;
|
||||
|
||||
public RingbufferFolder(string ringbufferFolder, RingbufferConfiguration configuration, IFileVerifier fileVerifier)
|
||||
{
|
||||
_ringbufferFolder = ringbufferFolder;
|
||||
_configuration = configuration;
|
||||
_fileVerifier = fileVerifier;
|
||||
EnsureDirectory(_ringbufferFolder);
|
||||
InitIndex();
|
||||
|
||||
|
||||
var th1 = new Thread(RemoveLoop);
|
||||
th1.Priority = ThreadPriority.Normal;
|
||||
th1.Start();
|
||||
|
||||
var th2 = new Thread(SaveLoop);
|
||||
th2.Priority = ThreadPriority.Normal;
|
||||
th2.Start();
|
||||
}
|
||||
|
||||
private void RemoveLoop()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var filesToDelete=_index.GetFilesToDelete(_configuration.MaxAmountOfGoodImages, _configuration.MaxAmountOfBadImages);
|
||||
|
||||
filesToDelete.ForEach(x=>
|
||||
{
|
||||
DeleteFile(x);
|
||||
_fileVerifier.LinkedFiles(x).ForEach(DeleteFile);
|
||||
});
|
||||
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
}
|
||||
|
||||
ConcurrentQueue<SaveCandidate> _saveQueue = new ConcurrentQueue<SaveCandidate>();
|
||||
|
||||
|
||||
object _lock=new object();
|
||||
|
||||
private void EnsureDirectory(string directory)
|
||||
{
|
||||
if (!Directory.Exists(directory))
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
}
|
||||
}
|
||||
private void SaveLoop()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (_saveQueue.TryDequeue(out var candidate))
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
EnsureDirectory(candidate.TargetDirectory);
|
||||
var written=candidate.FileIdentity.WriteTo(candidate.TargetDirectory);
|
||||
_index.Track(written);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void InitIndex()
|
||||
{
|
||||
if (_configuration.ProductFirst)
|
||||
{
|
||||
_index=new ProductIndex();
|
||||
}
|
||||
else
|
||||
{
|
||||
_index=new RootIndex();
|
||||
}
|
||||
IndexFiles();
|
||||
}
|
||||
|
||||
public void DeleteFile(string fileToDelete)
|
||||
{
|
||||
var fi = new System.IO.FileInfo(fileToDelete);
|
||||
if (fi.Exists)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
fi.Delete();
|
||||
}
|
||||
|
||||
fi.Refresh();
|
||||
while (fi.Exists)
|
||||
{
|
||||
System.Threading.Thread.Sleep(10);
|
||||
fi.Refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void IndexFiles()
|
||||
{
|
||||
var allFiles=Directory.GetFiles(_ringbufferFolder, "*.*", SearchOption.AllDirectories);
|
||||
var verifiedFiles = allFiles.Where(x => _fileVerifier.IsMainFile(x)).ToList();
|
||||
var orderedFiles = verifiedFiles.OrderByDescending(x => new FileInfo(x).CreationTimeUtc).ToList();
|
||||
orderedFiles.ForEach(_index.Track);
|
||||
}
|
||||
|
||||
public const string GOOD_DIR = "Good";
|
||||
public const string BAD_DIR = "Bad";
|
||||
public void WriteGood(IFileIdentity fileIdentity,string product)
|
||||
{
|
||||
var path = _configuration.ProductFirst
|
||||
? Path.Combine(_ringbufferFolder, product, GOOD_DIR)
|
||||
: Path.Combine(_ringbufferFolder, GOOD_DIR, product);
|
||||
|
||||
|
||||
_saveQueue.Enqueue(new SaveCandidate(fileIdentity, path));
|
||||
}
|
||||
|
||||
public void WriteBad(IFileIdentity fileIdentity, string product)
|
||||
{
|
||||
var path = _configuration.ProductFirst
|
||||
? Path.Combine(_ringbufferFolder, product, BAD_DIR)
|
||||
: Path.Combine(_ringbufferFolder, BAD_DIR, product);
|
||||
|
||||
_saveQueue.Enqueue(new SaveCandidate(fileIdentity, path));
|
||||
}
|
||||
}
|
||||
}
|
||||
182
framework/Inspectron.Ringbuffer/RingbufferPath.cs
Normal file
182
framework/Inspectron.Ringbuffer/RingbufferPath.cs
Normal file
@@ -0,0 +1,182 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Inspectron.Ringbuffer.Interfaces;
|
||||
|
||||
namespace Inspectron.Ringbuffer
|
||||
{
|
||||
public class RingbufferPath : IFolderWritter,IDisposable
|
||||
{
|
||||
private readonly string _pathFormat;
|
||||
private readonly RingbufferPathConfiguration _configuration;
|
||||
private readonly IFileVerifier _fileVerifier;
|
||||
|
||||
public const string PRODUCT = "$PRODUCT$";
|
||||
public const string GOODBAD = "$GOODBAD$";
|
||||
public const string DATE = "$date$";
|
||||
public const string MONTH = "$month$";
|
||||
public const string YEAR = "$year$";
|
||||
|
||||
private IIndex _index;
|
||||
ConcurrentQueue<SaveCandidate> _saveQueue = new ConcurrentQueue<SaveCandidate>();
|
||||
object _lock = new object();
|
||||
|
||||
private string _ringbufferFolder;
|
||||
private int _productPartId;
|
||||
private int _goodBadPartId;
|
||||
private int _subPartId;
|
||||
|
||||
public RingbufferPath(RingbufferPathConfiguration configuration, IFileVerifier fileVerifier)
|
||||
{
|
||||
_pathFormat = configuration.PathFormat;
|
||||
_configuration = configuration;
|
||||
_fileVerifier = fileVerifier;
|
||||
|
||||
var pathFormat = configuration.PathFormat;
|
||||
|
||||
var pathParts = Path.Combine(configuration.RootDirectory,pathFormat).Split(new char[]{ Path.DirectorySeparatorChar },StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
_ringbufferFolder = configuration.RootDirectory;
|
||||
|
||||
_productPartId = -1;
|
||||
_goodBadPartId = -1;
|
||||
|
||||
for (int i = 1; i < pathParts.Length; i++)
|
||||
{
|
||||
if (pathParts[i] == PRODUCT) _productPartId = i;
|
||||
if (pathParts[i] == GOODBAD) _goodBadPartId = i;
|
||||
|
||||
}
|
||||
|
||||
//if(_productPartId==-1||_goodBadPartId==-1)throw new Exception("one of the markers is missing");
|
||||
|
||||
EnsureDirectory(_ringbufferFolder);
|
||||
InitIndex();
|
||||
|
||||
|
||||
var th1 = new Thread(RemoveLoop);
|
||||
th1.IsBackground = true;
|
||||
th1.Priority = ThreadPriority.Normal;
|
||||
th1.Start();
|
||||
|
||||
var th2 = new Thread(SaveLoop);
|
||||
th2.IsBackground = true;
|
||||
th2.Priority = ThreadPriority.Normal;
|
||||
th2.Start();
|
||||
}
|
||||
|
||||
private void InitIndex()
|
||||
{
|
||||
_index = new GroupIndex(_configuration.GroupPartId,_goodBadPartId);
|
||||
IndexFiles();
|
||||
}
|
||||
private void IndexFiles()
|
||||
{
|
||||
|
||||
|
||||
var allFiles = Directory.GetFiles(_ringbufferFolder, "*.*", SearchOption.AllDirectories);
|
||||
var verifiedFiles = allFiles.Where(x => _fileVerifier.IsMainFile(x)).ToList();
|
||||
var orderedFiles = verifiedFiles.OrderByDescending(x => new FileInfo(x).CreationTimeUtc).ToList();
|
||||
orderedFiles.ForEach(_index.Track);
|
||||
}
|
||||
|
||||
private void RemoveLoop()
|
||||
{
|
||||
while (!_stop)
|
||||
{
|
||||
var filesToDelete = _index.GetFilesToDelete(_configuration.MaxAmountOfGoodImages, _configuration.MaxAmountOfBadImages);
|
||||
|
||||
filesToDelete.ForEach(x =>
|
||||
{
|
||||
DeleteFile(x);
|
||||
_fileVerifier.LinkedFiles(x).ForEach(DeleteFile);
|
||||
});
|
||||
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
}
|
||||
public void DeleteFile(string fileToDelete)
|
||||
{
|
||||
var fi = new System.IO.FileInfo(fileToDelete);
|
||||
if (fi.Exists)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
fi.Delete();
|
||||
}
|
||||
|
||||
fi.Refresh();
|
||||
while (fi.Exists)
|
||||
{
|
||||
System.Threading.Thread.Sleep(10);
|
||||
fi.Refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _stop = false;
|
||||
private void SaveLoop()
|
||||
{
|
||||
while (!_stop)
|
||||
{
|
||||
if (_saveQueue.TryDequeue(out var candidate))
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
EnsureDirectory(candidate.TargetDirectory);
|
||||
var written = candidate.FileIdentity.WriteTo(candidate.TargetDirectory);
|
||||
_index.Track(written);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
private void EnsureDirectory(string directory)
|
||||
{
|
||||
if (!Directory.Exists(directory))
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
}
|
||||
}
|
||||
public static string GOOD_DIR = "Good";
|
||||
public static string BAD_DIR = "Bad";
|
||||
|
||||
string TransformPath(string originalPath,bool isGood,string product)
|
||||
{
|
||||
var time = DateTime.Now;
|
||||
|
||||
var res= originalPath.Replace(PRODUCT, product)
|
||||
.Replace(GOODBAD, isGood ? GOOD_DIR : BAD_DIR)
|
||||
.Replace(MONTH,time.ToString("MMMM"))
|
||||
.Replace(YEAR,time.Year.ToString())
|
||||
.Replace(DATE,time.ToString("dd.MM.yyyy"));
|
||||
return Path.Combine(_configuration.RootDirectory, res);
|
||||
}
|
||||
|
||||
public void WriteGood(IFileIdentity fileIdentity, string product)
|
||||
{
|
||||
var path = TransformPath(_configuration.PathFormat, true, product);
|
||||
|
||||
|
||||
_saveQueue.Enqueue(new SaveCandidate(fileIdentity, path));
|
||||
}
|
||||
|
||||
public void WriteBad(IFileIdentity fileIdentity, string product)
|
||||
{
|
||||
var path = TransformPath(_configuration.PathFormat, false, product);
|
||||
|
||||
_saveQueue.Enqueue(new SaveCandidate(fileIdentity, path));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_stop = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
namespace Inspectron.Ringbuffer
|
||||
{
|
||||
public class RingbufferPathConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// How many images should be present in Good folder
|
||||
/// After reaching this amount older images will be deleted
|
||||
/// </summary>
|
||||
public int MaxAmountOfGoodImages { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// How many images should be present in Bad folder
|
||||
/// After reaching this amount older images will be deleted
|
||||
/// </summary>
|
||||
public int MaxAmountOfBadImages { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Where to store images
|
||||
/// Supports $PRODUCT$ mark to put product name into its place
|
||||
/// Supports $GOODBAD$ mark to put product quality into its place
|
||||
/// </summary>
|
||||
public string PathFormat { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Specifies which part of the path should be checked for maximum files
|
||||
/// </summary>
|
||||
public int GroupPartId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Ringbuffer's root directory
|
||||
/// </summary>
|
||||
public string RootDirectory { get; set; } = "./";
|
||||
|
||||
}
|
||||
}
|
||||
16
framework/Inspectron.Ringbuffer/SaveCandidate.cs
Normal file
16
framework/Inspectron.Ringbuffer/SaveCandidate.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using Inspectron.Ringbuffer.Interfaces;
|
||||
|
||||
namespace Inspectron.Ringbuffer
|
||||
{
|
||||
public class SaveCandidate
|
||||
{
|
||||
public SaveCandidate(IFileIdentity fileIdentity, string targetDirectory)
|
||||
{
|
||||
FileIdentity = fileIdentity;
|
||||
TargetDirectory = targetDirectory;
|
||||
}
|
||||
|
||||
public IFileIdentity FileIdentity { get; set; }
|
||||
public string TargetDirectory { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Windows.Forms;
|
||||
using Inspectron.Settings.Attributes;
|
||||
using Microsoft.WindowsAPICodePack.Dialogs;
|
||||
|
||||
namespace Inspectron.Settings.Windows.Configuration;
|
||||
|
||||
public class DefaultControlFactory : IControlFactory
|
||||
{
|
||||
public virtual Control CreateControl(string name, string description, PropertyInfo propertyInfo,
|
||||
object initialValue, Action<object> valueChangedCallback, OptionsWindow optionsWindow)
|
||||
{
|
||||
var type = propertyInfo.PropertyType;
|
||||
|
||||
// Check for SettingDescriptionAttribute
|
||||
var settingDescriptionAttribute = propertyInfo.GetCustomAttribute<SettingDescriptionAttribute>();
|
||||
string settingDescription = settingDescriptionAttribute?.Description;
|
||||
|
||||
// Check for SettingPreviewAttribute
|
||||
var previewAttribute = propertyInfo.GetCustomAttribute<SettingPreviewAttribute>();
|
||||
Label previewLabel = null;
|
||||
Func<object, string> getPreview = null;
|
||||
|
||||
if (previewAttribute != null)
|
||||
{
|
||||
var method = previewAttribute.PreviewClass.GetMethod(previewAttribute.PreviewFunction, BindingFlags.Public | BindingFlags.Static);
|
||||
getPreview = value => (string)method.Invoke(null, new[] { value });
|
||||
previewLabel = new Label
|
||||
{
|
||||
AutoSize = true,
|
||||
Padding = new Padding(15, 0, 0, 0),
|
||||
Font = new System.Drawing.Font("Segoe UI", 10, System.Drawing.FontStyle.Italic),
|
||||
Text = getPreview(initialValue)
|
||||
};
|
||||
}
|
||||
|
||||
if (type == typeof(string) && name.EndsWith("Path", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return CreatePathControl(name, description, initialValue, valueChangedCallback, optionsWindow, settingDescription, previewLabel, getPreview);
|
||||
}
|
||||
else if (type == typeof(string))
|
||||
{
|
||||
return CreateStringControl(description, initialValue, valueChangedCallback, settingDescription, previewLabel, getPreview);
|
||||
}
|
||||
else if (type == typeof(int))
|
||||
{
|
||||
return CreateIntControl(description, initialValue, valueChangedCallback, settingDescription, previewLabel, getPreview);
|
||||
}
|
||||
else if (type == typeof(bool))
|
||||
{
|
||||
return CreateBoolControl(description, initialValue, valueChangedCallback, settingDescription, previewLabel, getPreview);
|
||||
}
|
||||
else if (type.IsEnum)
|
||||
{
|
||||
return CreateEnumControl(description, type, initialValue, valueChangedCallback, settingDescription, previewLabel, getPreview);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new Label { Text = $"Unsupported type: {type.Name}", AutoSize = true };
|
||||
}
|
||||
}
|
||||
|
||||
private Control CreatePathControl(string name, string description, object initialValue, Action<object> valueChangedCallback,
|
||||
OptionsWindow optionsWindow, string settingDescription, Label previewLabel, Func<object, string> getPreview)
|
||||
{
|
||||
var textBox = new TextBox { Text = initialValue as string, Width = 400 };
|
||||
var label = new Label { Text = description, AutoSize = true, Padding = new Padding(0, 5, 0, 0) };
|
||||
var browseButton = new Button { Text = "Browse", AutoSize = true };
|
||||
|
||||
browseButton.Click += (s, e) =>
|
||||
{
|
||||
using (var dialog = new CommonOpenFileDialog { IsFolderPicker = true })
|
||||
{
|
||||
if (dialog.ShowDialog(optionsWindow.Handle) == CommonFileDialogResult.Ok)
|
||||
{
|
||||
textBox.Text = dialog.FileName;
|
||||
valueChangedCallback(dialog.FileName);
|
||||
if (previewLabel != null && getPreview != null)
|
||||
previewLabel.Text = getPreview(dialog.FileName);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
textBox.TextChanged += (s, e) =>
|
||||
{
|
||||
valueChangedCallback(textBox.Text);
|
||||
if (previewLabel != null && getPreview != null)
|
||||
previewLabel.Text = getPreview(textBox.Text);
|
||||
};
|
||||
|
||||
return CreateOuterPanel(settingDescription, previewLabel, label, textBox, browseButton);
|
||||
}
|
||||
|
||||
private Control CreateStringControl(string description, object initialValue, Action<object> valueChangedCallback,
|
||||
string settingDescription, Label previewLabel, Func<object, string> getPreview)
|
||||
{
|
||||
var textBox = new TextBox { Text = initialValue as string, Width = 400 };
|
||||
var label = new Label { Text = description, AutoSize = true, Padding = new Padding(0, 5, 0, 0) };
|
||||
|
||||
textBox.TextChanged += (s, e) =>
|
||||
{
|
||||
valueChangedCallback(textBox.Text);
|
||||
if (previewLabel != null && getPreview != null)
|
||||
previewLabel.Text = getPreview(textBox.Text);
|
||||
};
|
||||
|
||||
return CreateOuterPanel(settingDescription, previewLabel, label, textBox);
|
||||
}
|
||||
|
||||
private Control CreateIntControl(string description, object initialValue, Action<object> valueChangedCallback,
|
||||
string settingDescription, Label previewLabel, Func<object, string> getPreview)
|
||||
{
|
||||
var label = new Label { Text = description, AutoSize = true, Padding = new Padding(0, 5, 0, 0) };
|
||||
var numericUpDown = new NumericUpDown { Minimum = 0, Maximum = 99999999999, Value = Convert.ToDecimal(initialValue), Width = 200 };
|
||||
|
||||
numericUpDown.ValueChanged += (s, e) =>
|
||||
{
|
||||
valueChangedCallback(Convert.ToInt32(numericUpDown.Value));
|
||||
if (previewLabel != null && getPreview != null)
|
||||
previewLabel.Text = getPreview(Convert.ToInt32(numericUpDown.Value));
|
||||
};
|
||||
|
||||
return CreateOuterPanel(settingDescription, previewLabel, label, numericUpDown);
|
||||
}
|
||||
|
||||
private Control CreateBoolControl(string description, object initialValue, Action<object> valueChangedCallback,
|
||||
string settingDescription, Label previewLabel, Func<object, string> getPreview)
|
||||
{
|
||||
var checkBox = new CheckBox { Text = description, Checked = (bool)initialValue, AutoSize = true };
|
||||
|
||||
checkBox.CheckedChanged += (s, e) =>
|
||||
{
|
||||
valueChangedCallback(checkBox.Checked);
|
||||
if (previewLabel != null && getPreview != null)
|
||||
previewLabel.Text = getPreview(checkBox.Checked);
|
||||
};
|
||||
|
||||
return CreateOuterPanel(settingDescription, previewLabel, checkBox);
|
||||
}
|
||||
|
||||
private Control CreateEnumControl(string description, Type enumType, object initialValue, Action<object> valueChangedCallback,
|
||||
string settingDescription, Label previewLabel, Func<object, string> getPreview)
|
||||
{
|
||||
var label = new Label { Text = description, AutoSize = true, Padding = new Padding(0, 5, 0, 0) };
|
||||
var comboBox = new ComboBox { DropDownStyle = ComboBoxStyle.DropDownList, Width = 200 };
|
||||
|
||||
// Populate ComboBox with enum values
|
||||
foreach (var value in Enum.GetValues(enumType))
|
||||
{
|
||||
comboBox.Items.Add(value);
|
||||
}
|
||||
|
||||
// Set initial value
|
||||
comboBox.SelectedItem = initialValue;
|
||||
|
||||
comboBox.SelectedIndexChanged += (s, e) =>
|
||||
{
|
||||
valueChangedCallback(comboBox.SelectedItem);
|
||||
if (previewLabel != null && getPreview != null)
|
||||
previewLabel.Text = getPreview(comboBox.SelectedItem);
|
||||
};
|
||||
|
||||
return CreateOuterPanel(settingDescription, previewLabel, label, comboBox);
|
||||
}
|
||||
|
||||
private Control CreateOuterPanel(string settingDescription, Label previewLabel, params Control[] controls)
|
||||
{
|
||||
var outerPanel = new FlowLayoutPanel { FlowDirection = FlowDirection.TopDown, AutoSize = true, Margin = new Padding(0, 25, 0, 0) };
|
||||
|
||||
if (!string.IsNullOrEmpty(settingDescription))
|
||||
{
|
||||
var descriptionLabel = new Label { Text = settingDescription, AutoSize = true, Padding = new Padding(0, 5, 0, 0) };
|
||||
outerPanel.Controls.Add(descriptionLabel);
|
||||
}
|
||||
|
||||
foreach (var control in controls)
|
||||
{
|
||||
outerPanel.Controls.Add(control);
|
||||
}
|
||||
|
||||
if (previewLabel != null)
|
||||
outerPanel.Controls.Add(previewLabel);
|
||||
|
||||
return outerPanel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Inspectron.Settings.Windows.Configuration;
|
||||
|
||||
public interface IControlFactory
|
||||
{
|
||||
Control CreateControl(string name, string description, PropertyInfo propertyInfo, object initialValue,
|
||||
Action<object> valueChangedCallback, OptionsWindow optionsWindow);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System.Reflection;
|
||||
|
||||
namespace Inspectron.Settings.Windows.Configuration;
|
||||
|
||||
internal class OptionSetting
|
||||
{
|
||||
public object Owner { get; set; }
|
||||
public PropertyInfo Property { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string TreePath { get; set; }
|
||||
public object Value { get; set; } // Temporary value until OK is pressed
|
||||
}
|
||||
69
framework/Inspectron.Settings.Windows/Configuration/OptionsWindow.Designer.cs
generated
Normal file
69
framework/Inspectron.Settings.Windows/Configuration/OptionsWindow.Designer.cs
generated
Normal file
@@ -0,0 +1,69 @@
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Inspectron.Settings.Windows.Configuration;
|
||||
|
||||
public partial class OptionsWindow
|
||||
{
|
||||
private TreeView treeViewCategories;
|
||||
private FlowLayoutPanel flowLayoutPanelSettings;
|
||||
private Button buttonOK;
|
||||
private Button buttonCancel;
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
treeViewCategories = new TreeView();
|
||||
flowLayoutPanelSettings = new FlowLayoutPanel();
|
||||
buttonOK = new Button();
|
||||
buttonCancel = new Button();
|
||||
SuspendLayout();
|
||||
//
|
||||
// treeViewCategories
|
||||
//
|
||||
treeViewCategories.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left;
|
||||
treeViewCategories.Location = new Point(12, 12);
|
||||
treeViewCategories.Name = "treeViewCategories";
|
||||
treeViewCategories.Size = new Size(200, 582);
|
||||
treeViewCategories.TabIndex = 0;
|
||||
//
|
||||
// flowLayoutPanelSettings
|
||||
//
|
||||
flowLayoutPanelSettings.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
|
||||
flowLayoutPanelSettings.AutoScroll = true;
|
||||
flowLayoutPanelSettings.FlowDirection = FlowDirection.TopDown;
|
||||
flowLayoutPanelSettings.Location = new Point(220, 12);
|
||||
flowLayoutPanelSettings.Name = "flowLayoutPanelSettings";
|
||||
flowLayoutPanelSettings.Size = new Size(852, 582);
|
||||
flowLayoutPanelSettings.TabIndex = 1;
|
||||
flowLayoutPanelSettings.WrapContents = false;
|
||||
//
|
||||
// buttonOK
|
||||
//
|
||||
buttonOK.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
|
||||
buttonOK.Location = new Point(912, 612);
|
||||
buttonOK.Name = "buttonOK";
|
||||
buttonOK.Size = new Size(75, 23);
|
||||
buttonOK.TabIndex = 2;
|
||||
buttonOK.Text = "OK";
|
||||
//
|
||||
// buttonCancel
|
||||
//
|
||||
buttonCancel.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
|
||||
buttonCancel.Location = new Point(1002, 612);
|
||||
buttonCancel.Name = "buttonCancel";
|
||||
buttonCancel.Size = new Size(75, 23);
|
||||
buttonCancel.TabIndex = 3;
|
||||
buttonCancel.Text = "Cancel";
|
||||
//
|
||||
// OptionsWindow
|
||||
//
|
||||
ClientSize = new Size(1089, 649);
|
||||
Controls.Add(treeViewCategories);
|
||||
Controls.Add(flowLayoutPanelSettings);
|
||||
Controls.Add(buttonOK);
|
||||
Controls.Add(buttonCancel);
|
||||
Name = "OptionsWindow";
|
||||
Text = "Options";
|
||||
ResumeLayout(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Inspectron.Settings.Windows.Configuration;
|
||||
|
||||
public partial class OptionsWindow : Form
|
||||
{
|
||||
private Dictionary<string, List<OptionSetting>> settingsByCategory;
|
||||
private IControlFactory controlFactory;
|
||||
|
||||
public OptionsWindow(IControlFactory controlFactory)
|
||||
{
|
||||
this.controlFactory = controlFactory;
|
||||
settingsByCategory = new Dictionary<string, List<OptionSetting>>();
|
||||
|
||||
InitializeComponent();
|
||||
this.treeViewCategories.AfterSelect += new TreeViewEventHandler(this.treeViewCategories_AfterSelect);
|
||||
this.buttonOK.Click += new EventHandler(this.buttonOK_Click);
|
||||
this.buttonCancel.Click += new EventHandler(this.buttonCancel_Click);
|
||||
}
|
||||
|
||||
public void LoadFromSettings(InspectronSettings inspectronSettings)
|
||||
{
|
||||
// UserSettings is a Tree<object> with folders (string) and UserSettingsInfo nodes.
|
||||
// Traverse the tree to find all UserSettingsInfo nodes and their PropertyDescriptors.
|
||||
|
||||
void Traverse(object nodeObj, string path)
|
||||
{
|
||||
var node = nodeObj as dynamic; // Tree<object>
|
||||
object value = node.Value;
|
||||
|
||||
if (value is string folderName)
|
||||
{
|
||||
// Folder node, append to path and traverse children
|
||||
string newPath = string.IsNullOrEmpty(path) ? folderName : $"{path}/{folderName}";
|
||||
foreach (var child in node.Children)
|
||||
Traverse(child, newPath);
|
||||
}
|
||||
else if (value is Inspectron.Settings.InspectronSettings.UserSettingsInfo info)
|
||||
{
|
||||
// Leaf node: user settings group
|
||||
string groupName = info.Name;
|
||||
string groupPath = string.IsNullOrEmpty(path) ? groupName : $"{path}/{groupName}";
|
||||
foreach (var pd in info.Settings)
|
||||
{
|
||||
// Try to get owner from BoundPropertyDescriptor, else skip
|
||||
object owner = null;
|
||||
string description = pd.Description;
|
||||
if (pd is Inspectron.Settings.BoundPropertyDescriptor bpd)
|
||||
{
|
||||
owner = bpd.Owner;
|
||||
if (string.IsNullOrEmpty(description))
|
||||
description = bpd.DisplayName;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If not a BoundPropertyDescriptor, cannot get owner, skip
|
||||
continue;
|
||||
}
|
||||
RegisterUserSetting(owner, bpd.PropertyInfo.Name, groupPath, description);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Traverse(inspectronSettings.UserSettings.Root, "");
|
||||
}
|
||||
|
||||
public void RegisterUserSetting(object owner, string propertyName, string treePath, string description = null)
|
||||
{
|
||||
// Get the PropertyInfo from owner and propertyName
|
||||
var property = owner.GetType().GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance);
|
||||
if (property == null)
|
||||
throw new ArgumentException($"Property '{propertyName}' not found on owner type.");
|
||||
|
||||
if (string.IsNullOrEmpty(description))
|
||||
description = propertyName;
|
||||
|
||||
// Create an OptionSetting instance
|
||||
var setting = new OptionSetting
|
||||
{
|
||||
Owner = owner,
|
||||
Property = property,
|
||||
Description = description,
|
||||
TreePath = treePath,
|
||||
Value = property.GetValue(owner)
|
||||
};
|
||||
|
||||
// Add to settingsByCategory
|
||||
if (!settingsByCategory.TryGetValue(treePath, out var settingsList))
|
||||
{
|
||||
settingsList = new List<OptionSetting>();
|
||||
settingsByCategory[treePath] = settingsList;
|
||||
}
|
||||
settingsList.Add(setting);
|
||||
|
||||
// Add nodes to treeViewCategories
|
||||
AddTreeNodes(treeViewCategories, treePath);
|
||||
}
|
||||
|
||||
private void AddTreeNodes(TreeView treeView, string treePath)
|
||||
{
|
||||
var parts = treePath.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
TreeNodeCollection nodes = treeView.Nodes;
|
||||
foreach (var part in parts)
|
||||
{
|
||||
var node = Enumerable.Cast<TreeNode>(nodes).FirstOrDefault(n => n.Text == part);
|
||||
if (node == null)
|
||||
{
|
||||
node = new TreeNode(part);
|
||||
nodes.Add(node);
|
||||
}
|
||||
nodes = node.Nodes;
|
||||
}
|
||||
}
|
||||
|
||||
private void treeViewCategories_AfterSelect(object sender, TreeViewEventArgs e)
|
||||
{
|
||||
// Display the settings for the selected category
|
||||
string selectedPath = GetFullPath(e.Node);
|
||||
DisplaySettings(selectedPath);
|
||||
}
|
||||
|
||||
private string GetFullPath(TreeNode node)
|
||||
{
|
||||
if (node.Parent == null)
|
||||
return node.Text;
|
||||
else
|
||||
return GetFullPath(node.Parent) + "/" + node.Text;
|
||||
}
|
||||
|
||||
private void DisplaySettings(string treePath)
|
||||
{
|
||||
flowLayoutPanelSettings.Controls.Clear();
|
||||
|
||||
if (settingsByCategory.TryGetValue(treePath, out var settingsList))
|
||||
{
|
||||
foreach (var setting in settingsList)
|
||||
{
|
||||
var control = controlFactory.CreateControl(
|
||||
setting.Property.Name,
|
||||
setting.Description,
|
||||
setting.Property,
|
||||
setting.Value,
|
||||
(newValue) => setting.Value = newValue,
|
||||
this);
|
||||
|
||||
flowLayoutPanelSettings.Controls.Add(control);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void buttonOK_Click(object sender, EventArgs e)
|
||||
{
|
||||
// Update the owner properties with the values
|
||||
foreach (var settingsList in settingsByCategory.Values)
|
||||
{
|
||||
foreach (var setting in settingsList)
|
||||
{
|
||||
setting.Property.SetValue(setting.Owner, setting.Value);
|
||||
}
|
||||
}
|
||||
|
||||
this.DialogResult = DialogResult.OK;
|
||||
this.Close();
|
||||
}
|
||||
|
||||
private void buttonCancel_Click(object sender, EventArgs e)
|
||||
{
|
||||
this.DialogResult = DialogResult.Cancel;
|
||||
this.Close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
<?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.Runtime.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:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<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" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</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" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
@@ -0,0 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft-WindowsAPICodePack-Shell" Version="1.1.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Inspectron.Settings\Inspectron.Settings.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
18
framework/Inspectron.Settings.Windows/PropHelper.cs
Normal file
18
framework/Inspectron.Settings.Windows/PropHelper.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Inspectron.Settings.Windows
|
||||
{
|
||||
public static class PropHelper
|
||||
{
|
||||
public static PropertyInfo GetPrp(object obj,string name)
|
||||
{
|
||||
return obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
||||
.First(x => x.Name == name);
|
||||
}
|
||||
public static string SplitCamelCase(string input)
|
||||
{
|
||||
return System.Text.RegularExpressions.Regex.Replace(input, "([A-Z])", " $1", System.Text.RegularExpressions.RegexOptions.Compiled).Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
61
framework/Inspectron.Settings.WindowsWizard/Controls/CheckEditor.Designer.cs
generated
Normal file
61
framework/Inspectron.Settings.WindowsWizard/Controls/CheckEditor.Designer.cs
generated
Normal file
@@ -0,0 +1,61 @@
|
||||
namespace Inspectron.Settings.WindowsWizard.Controls
|
||||
{
|
||||
partial class CheckEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Component Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.checkBox1 = new System.Windows.Forms.CheckBox();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// checkBox1
|
||||
//
|
||||
this.checkBox1.AutoSize = true;
|
||||
this.checkBox1.Location = new System.Drawing.Point(3, 3);
|
||||
this.checkBox1.Name = "checkBox1";
|
||||
this.checkBox1.Size = new System.Drawing.Size(98, 21);
|
||||
this.checkBox1.TabIndex = 0;
|
||||
this.checkBox1.Text = "checkBox1";
|
||||
this.checkBox1.UseVisualStyleBackColor = true;
|
||||
this.checkBox1.CheckedChanged += new System.EventHandler(this.checkBox1_CheckedChanged);
|
||||
//
|
||||
// CheckEditor
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.Controls.Add(this.checkBox1);
|
||||
this.Name = "CheckEditor";
|
||||
this.Size = new System.Drawing.Size(141, 29);
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.CheckBox checkBox1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.Reflection;
|
||||
using Inspectron.Settings.WindowsWizard.Interfaces;
|
||||
|
||||
namespace Inspectron.Settings.WindowsWizard.Controls
|
||||
{
|
||||
public partial class CheckEditor : UserControl, ISetupItem
|
||||
{
|
||||
private PropertyInfo _prp;
|
||||
private object _obj;
|
||||
|
||||
public CheckEditor()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
public void SetProperty(object obj, string propertyName)
|
||||
{
|
||||
checkBox1.Text = PropHelper.SplitCamelCase(propertyName);
|
||||
_prp = PropHelper.GetPrp(obj, propertyName);
|
||||
_obj = obj;
|
||||
checkBox1.Checked = (bool)_prp.GetValue(obj);
|
||||
}
|
||||
|
||||
private void checkBox1_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
_prp.SetValue(_obj, checkBox1.Checked);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
<?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.Runtime.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:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<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" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</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" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
46
framework/Inspectron.Settings.WindowsWizard/Controls/EnumEditor.Designer.cs
generated
Normal file
46
framework/Inspectron.Settings.WindowsWizard/Controls/EnumEditor.Designer.cs
generated
Normal file
@@ -0,0 +1,46 @@
|
||||
namespace Inspectron.Settings.WindowsWizard.Controls
|
||||
{
|
||||
partial class EnumEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Component Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// EnumEditor
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.Name = "EnumEditor";
|
||||
this.Size = new System.Drawing.Size(112, 51);
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System.Reflection;
|
||||
using Inspectron.Settings.WindowsWizard.Interfaces;
|
||||
|
||||
namespace Inspectron.Settings.WindowsWizard.Controls
|
||||
{
|
||||
public partial class EnumEditor : UserControl,ISetupItem
|
||||
{
|
||||
private PropertyInfo _prp;
|
||||
private object _obj;
|
||||
|
||||
public EnumEditor()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public void SetProperty(object obj, string propertyName)
|
||||
{
|
||||
GroupBox gb = new GroupBox();
|
||||
gb.Text = PropHelper.SplitCamelCase(propertyName);
|
||||
this.Controls.Add(gb);
|
||||
gb.Dock = DockStyle.Fill;
|
||||
FlowLayoutPanel flp = new FlowLayoutPanel();
|
||||
|
||||
gb.Controls.Add(flp);
|
||||
flp.Dock = DockStyle.Fill;
|
||||
_prp = PropHelper.GetPrp(obj, propertyName);
|
||||
_obj = obj;
|
||||
var names = Enum.GetNames(_prp.PropertyType);
|
||||
var values = Enum.GetValues(_prp.PropertyType);
|
||||
var curValue = _prp.GetValue(_obj);
|
||||
int cnt = 0;
|
||||
foreach (string name in names)
|
||||
{
|
||||
var rb = new RadioButton() {Text = name};
|
||||
var cnt1 = cnt;
|
||||
rb.CheckedChanged += (sender, args) =>
|
||||
{
|
||||
if (rb.Checked) _prp.SetValue(_obj, values.GetValue(cnt1));
|
||||
};
|
||||
flp.Controls.Add(rb);
|
||||
if ((int)curValue == (int)values.GetValue(cnt1))
|
||||
{
|
||||
rb.Checked = true;
|
||||
}
|
||||
cnt++;
|
||||
}
|
||||
this.Controls.Add(gb);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
<?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.Runtime.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:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<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" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</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" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
73
framework/Inspectron.Settings.WindowsWizard/Controls/NumberEditor.Designer.cs
generated
Normal file
73
framework/Inspectron.Settings.WindowsWizard/Controls/NumberEditor.Designer.cs
generated
Normal file
@@ -0,0 +1,73 @@
|
||||
namespace Inspectron.Settings.WindowsWizard.Controls
|
||||
{
|
||||
partial class NumberEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Component Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.numericUpDown1 = new System.Windows.Forms.NumericUpDown();
|
||||
((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).BeginInit();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.AutoSize = true;
|
||||
this.label1.Location = new System.Drawing.Point(2, 5);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(35, 13);
|
||||
this.label1.TabIndex = 0;
|
||||
this.label1.Text = "label1";
|
||||
//
|
||||
// numericUpDown1
|
||||
//
|
||||
this.numericUpDown1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.numericUpDown1.Location = new System.Drawing.Point(109, 3);
|
||||
this.numericUpDown1.Name = "numericUpDown1";
|
||||
this.numericUpDown1.Size = new System.Drawing.Size(147, 20);
|
||||
this.numericUpDown1.TabIndex = 1;
|
||||
this.numericUpDown1.ValueChanged += new System.EventHandler(this.numericUpDown1_ValueChanged);
|
||||
//
|
||||
// NumberEditor
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.Controls.Add(this.numericUpDown1);
|
||||
this.Controls.Add(this.label1);
|
||||
this.Name = "NumberEditor";
|
||||
this.Size = new System.Drawing.Size(259, 25);
|
||||
((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).EndInit();
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.NumericUpDown numericUpDown1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using System.Reflection;
|
||||
using Inspectron.Settings.WindowsWizard.Interfaces;
|
||||
|
||||
namespace Inspectron.Settings.WindowsWizard.Controls
|
||||
{
|
||||
public partial class NumberEditor : UserControl, ISetupItem
|
||||
{
|
||||
private PropertyInfo _prp;
|
||||
private object _obj;
|
||||
private int _number;
|
||||
|
||||
public NumberEditor()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public int Number
|
||||
{
|
||||
get { return _number; }
|
||||
set
|
||||
{
|
||||
if (_obj == null) return;
|
||||
_number = value;
|
||||
_prp.SetValue(_obj, value);
|
||||
}
|
||||
}
|
||||
|
||||
public int Min
|
||||
{
|
||||
get => (int)numericUpDown1.Minimum;
|
||||
set => numericUpDown1.Minimum = value;
|
||||
}
|
||||
|
||||
public int Max
|
||||
{
|
||||
get => (int) numericUpDown1.Maximum;
|
||||
set => numericUpDown1.Maximum = value;
|
||||
}
|
||||
|
||||
|
||||
public void SetProperty(object obj, string propertyName)
|
||||
{
|
||||
label1.Text = PropHelper.SplitCamelCase(propertyName);
|
||||
_prp = PropHelper.GetPrp(obj, propertyName);
|
||||
_obj = obj;
|
||||
numericUpDown1.Value =_number = ((int)_prp.GetValue(obj)<Min?Min: (int)_prp.GetValue(obj));
|
||||
}
|
||||
|
||||
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
|
||||
{
|
||||
_number = (int)numericUpDown1.Value;
|
||||
if(_obj==null)return;
|
||||
_prp.SetValue(_obj, _number);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
<?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.Runtime.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:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<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" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</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" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
90
framework/Inspectron.Settings.WindowsWizard/Controls/PathEditor.Designer.cs
generated
Normal file
90
framework/Inspectron.Settings.WindowsWizard/Controls/PathEditor.Designer.cs
generated
Normal file
@@ -0,0 +1,90 @@
|
||||
namespace Inspectron.Settings.WindowsWizard.Controls
|
||||
{
|
||||
partial class PathEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Component Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.textBox1 = new System.Windows.Forms.TextBox();
|
||||
this.button1 = new System.Windows.Forms.Button();
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// textBox1
|
||||
//
|
||||
this.textBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.textBox1.Location = new System.Drawing.Point(150, 2);
|
||||
this.textBox1.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.textBox1.Name = "textBox1";
|
||||
this.textBox1.ReadOnly = true;
|
||||
this.textBox1.Size = new System.Drawing.Size(274, 20);
|
||||
this.textBox1.TabIndex = 1;
|
||||
//
|
||||
// button1
|
||||
//
|
||||
this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.button1.Location = new System.Drawing.Point(428, 2);
|
||||
this.button1.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.button1.Name = "button1";
|
||||
this.button1.Size = new System.Drawing.Size(29, 19);
|
||||
this.button1.TabIndex = 2;
|
||||
this.button1.Text = "...";
|
||||
this.button1.UseVisualStyleBackColor = true;
|
||||
this.button1.Click += new System.EventHandler(this.button1_Click);
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.AutoSize = true;
|
||||
this.label1.Location = new System.Drawing.Point(2, 5);
|
||||
this.label1.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(35, 13);
|
||||
this.label1.TabIndex = 3;
|
||||
this.label1.Text = "label1";
|
||||
//
|
||||
// PathEditor
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.Controls.Add(this.label1);
|
||||
this.Controls.Add(this.button1);
|
||||
this.Controls.Add(this.textBox1);
|
||||
this.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.Name = "PathEditor";
|
||||
this.Size = new System.Drawing.Size(460, 25);
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.TextBox textBox1;
|
||||
private System.Windows.Forms.Button button1;
|
||||
private System.Windows.Forms.Label label1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using System.Reflection;
|
||||
using Inspectron.Settings.WindowsWizard.Interfaces;
|
||||
using Microsoft.WindowsAPICodePack.Dialogs;
|
||||
|
||||
namespace Inspectron.Settings.WindowsWizard.Controls
|
||||
{
|
||||
public partial class PathEditor : UserControl,ISetupItem
|
||||
{
|
||||
|
||||
private PropertyInfo _prp;
|
||||
private object _obj;
|
||||
public PathEditor()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public bool FolderPick { get; set; }
|
||||
|
||||
public string SelectedFolderPath
|
||||
{
|
||||
get => textBox1.Text;
|
||||
set
|
||||
{
|
||||
if (_obj == null) return;
|
||||
textBox1.Text = value;
|
||||
_prp.SetValue(_obj,value);
|
||||
}
|
||||
}
|
||||
|
||||
private void button1_Click(object sender, EventArgs e)
|
||||
{
|
||||
var dialog = new CommonOpenFileDialog();
|
||||
dialog.IsFolderPicker = FolderPick;
|
||||
dialog.InitialDirectory = SelectedFolderPath;
|
||||
if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
|
||||
{
|
||||
SelectedFolderPath = dialog.FileName;
|
||||
FileChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public event Action FileChanged = delegate { };
|
||||
public void SetProperty(object obj, string propertyName)
|
||||
{
|
||||
label1.Text = PropHelper.SplitCamelCase(propertyName);
|
||||
_prp = PropHelper.GetPrp(obj, propertyName);
|
||||
_obj = obj;
|
||||
SelectedFolderPath =(string)_prp.GetValue(obj);
|
||||
if(SelectedFolderPath!=null) FileChanged();
|
||||
}
|
||||
|
||||
public void HidePath()
|
||||
{
|
||||
textBox1.Visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
<?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.Runtime.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:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<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" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</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" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
72
framework/Inspectron.Settings.WindowsWizard/Controls/StringEditor.Designer.cs
generated
Normal file
72
framework/Inspectron.Settings.WindowsWizard/Controls/StringEditor.Designer.cs
generated
Normal file
@@ -0,0 +1,72 @@
|
||||
namespace Inspectron.Settings.WindowsWizard.Controls
|
||||
{
|
||||
partial class StringEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Component Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.textBox1 = new System.Windows.Forms.TextBox();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.AutoSize = true;
|
||||
this.label1.Location = new System.Drawing.Point(3, 6);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(35, 13);
|
||||
this.label1.TabIndex = 0;
|
||||
this.label1.Text = "label1";
|
||||
//
|
||||
// textBox1
|
||||
//
|
||||
this.textBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.textBox1.Location = new System.Drawing.Point(150, 3);
|
||||
this.textBox1.Name = "textBox1";
|
||||
this.textBox1.Size = new System.Drawing.Size(176, 20);
|
||||
this.textBox1.TabIndex = 1;
|
||||
this.textBox1.TextChanged += new System.EventHandler(this.textBox1_TextChanged);
|
||||
//
|
||||
// StringEditor
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.Controls.Add(this.textBox1);
|
||||
this.Controls.Add(this.label1);
|
||||
this.Name = "StringEditor";
|
||||
this.Size = new System.Drawing.Size(329, 28);
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.TextBox textBox1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Reflection;
|
||||
using Inspectron.Settings.WindowsWizard.Interfaces;
|
||||
|
||||
namespace Inspectron.Settings.WindowsWizard.Controls
|
||||
{
|
||||
public partial class StringEditor : UserControl, ISetupItem
|
||||
{
|
||||
private PropertyInfo _prp;
|
||||
private object _obj;
|
||||
private bool _password;
|
||||
|
||||
public StringEditor()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public bool Password
|
||||
{
|
||||
get { return _password; }
|
||||
set
|
||||
{
|
||||
if (value) textBox1.PasswordChar = '*';
|
||||
_password = value;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetProperty(object obj, string propertyName)
|
||||
{
|
||||
_prp = PropHelper.GetPrp(obj, propertyName);
|
||||
_obj = obj;
|
||||
textBox1.Text = (string) _prp.GetValue(obj);
|
||||
label1.Text = PropHelper.SplitCamelCase(propertyName);
|
||||
}
|
||||
|
||||
private void textBox1_TextChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (_obj == null) return;
|
||||
_prp.SetValue(_obj, textBox1.Text);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
<?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.Runtime.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:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<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" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</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" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Inspectron.Settings.WindowsWizard.Enums
|
||||
{
|
||||
public enum ESetupResult
|
||||
{
|
||||
Cancel,
|
||||
Back,
|
||||
Next
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0-windows</TargetFramework>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<Configurations>Debug;Release;CPU</Configurations>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="WindowsAPICodePack-Shell" Version="1.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Resource1.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resource1.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Resource1.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resource1.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Inspectron.Settings.WindowsWizard.Interfaces
|
||||
{
|
||||
public interface ISetupItem
|
||||
{
|
||||
void SetProperty(object obj,string propertyName);
|
||||
}
|
||||
}
|
||||
17
framework/Inspectron.Settings.WindowsWizard/PropHelper.cs
Normal file
17
framework/Inspectron.Settings.WindowsWizard/PropHelper.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System.Reflection;
|
||||
|
||||
namespace Inspectron.Settings.WindowsWizard
|
||||
{
|
||||
public static class PropHelper
|
||||
{
|
||||
public static PropertyInfo GetPrp(object obj,string name)
|
||||
{
|
||||
return obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
||||
.First(x => x.Name == name);
|
||||
}
|
||||
public static string SplitCamelCase(string input)
|
||||
{
|
||||
return System.Text.RegularExpressions.Regex.Replace(input, "([A-Z])", " $1", System.Text.RegularExpressions.RegexOptions.Compiled).Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
73
framework/Inspectron.Settings.WindowsWizard/Resource1.Designer.cs
generated
Normal file
73
framework/Inspectron.Settings.WindowsWizard/Resource1.Designer.cs
generated
Normal file
@@ -0,0 +1,73 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <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 Inspectron.Settings.WindowsWizard {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resource1 {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resource1() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Inspectron.Settings.WindowsWizard.Resource1", typeof(Resource1).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap setup {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("setup", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
124
framework/Inspectron.Settings.WindowsWizard/Resource1.resx
Normal file
124
framework/Inspectron.Settings.WindowsWizard/Resource1.resx
Normal file
@@ -0,0 +1,124 @@
|
||||
<?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.Runtime.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:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<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" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</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" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||
<data name="setup" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>Resources\setup.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
</root>
|
||||
BIN
framework/Inspectron.Settings.WindowsWizard/Resources/setup.png
Normal file
BIN
framework/Inspectron.Settings.WindowsWizard/Resources/setup.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 131 KiB |
139
framework/Inspectron.Settings.WindowsWizard/SetupPage.Designer.cs
generated
Normal file
139
framework/Inspectron.Settings.WindowsWizard/SetupPage.Designer.cs
generated
Normal file
@@ -0,0 +1,139 @@
|
||||
namespace Inspectron.Settings.WindowsWizard
|
||||
{
|
||||
partial class SetupPage
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.panel1 = new System.Windows.Forms.Panel();
|
||||
this.checkBox1 = new System.Windows.Forms.CheckBox();
|
||||
this.button1 = new System.Windows.Forms.Button();
|
||||
this.panel2 = new System.Windows.Forms.Panel();
|
||||
this.pictureBox1 = new System.Windows.Forms.PictureBox();
|
||||
this.tabControl1 = new System.Windows.Forms.TabControl();
|
||||
this.panel1.SuspendLayout();
|
||||
this.panel2.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// panel1
|
||||
//
|
||||
this.panel1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||
this.panel1.Controls.Add(this.checkBox1);
|
||||
this.panel1.Controls.Add(this.button1);
|
||||
this.panel1.Dock = System.Windows.Forms.DockStyle.Bottom;
|
||||
this.panel1.Location = new System.Drawing.Point(0, 433);
|
||||
this.panel1.Margin = new System.Windows.Forms.Padding(2);
|
||||
this.panel1.Name = "panel1";
|
||||
this.panel1.Size = new System.Drawing.Size(814, 34);
|
||||
this.panel1.TabIndex = 0;
|
||||
//
|
||||
// checkBox1
|
||||
//
|
||||
this.checkBox1.AutoSize = true;
|
||||
this.checkBox1.Location = new System.Drawing.Point(4, 5);
|
||||
this.checkBox1.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
this.checkBox1.Name = "checkBox1";
|
||||
this.checkBox1.Size = new System.Drawing.Size(93, 19);
|
||||
this.checkBox1.TabIndex = 1;
|
||||
this.checkBox1.Text = "Expert mode";
|
||||
this.checkBox1.UseVisualStyleBackColor = true;
|
||||
this.checkBox1.CheckedChanged += new System.EventHandler(this.checkBox1_CheckedChanged);
|
||||
//
|
||||
// button1
|
||||
//
|
||||
this.button1.Location = new System.Drawing.Point(737, 2);
|
||||
this.button1.Margin = new System.Windows.Forms.Padding(2);
|
||||
this.button1.Name = "button1";
|
||||
this.button1.Size = new System.Drawing.Size(65, 22);
|
||||
this.button1.TabIndex = 0;
|
||||
this.button1.Text = "Done";
|
||||
this.button1.UseVisualStyleBackColor = true;
|
||||
this.button1.Click += new System.EventHandler(this.button1_Click);
|
||||
//
|
||||
// panel2
|
||||
//
|
||||
this.panel2.Controls.Add(this.pictureBox1);
|
||||
this.panel2.Dock = System.Windows.Forms.DockStyle.Left;
|
||||
this.panel2.Location = new System.Drawing.Point(0, 0);
|
||||
this.panel2.Margin = new System.Windows.Forms.Padding(2);
|
||||
this.panel2.Name = "panel2";
|
||||
this.panel2.Size = new System.Drawing.Size(175, 433);
|
||||
this.panel2.TabIndex = 1;
|
||||
//
|
||||
// pictureBox1
|
||||
//
|
||||
this.pictureBox1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.pictureBox1.Image = global::Inspectron.Settings.WindowsWizard.Resource1.setup;
|
||||
this.pictureBox1.Location = new System.Drawing.Point(0, 0);
|
||||
this.pictureBox1.Margin = new System.Windows.Forms.Padding(2);
|
||||
this.pictureBox1.Name = "pictureBox1";
|
||||
this.pictureBox1.Size = new System.Drawing.Size(175, 433);
|
||||
this.pictureBox1.TabIndex = 0;
|
||||
this.pictureBox1.TabStop = false;
|
||||
//
|
||||
// tabControl1
|
||||
//
|
||||
this.tabControl1.Location = new System.Drawing.Point(183, 14);
|
||||
this.tabControl1.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
this.tabControl1.Name = "tabControl1";
|
||||
this.tabControl1.SelectedIndex = 0;
|
||||
this.tabControl1.Size = new System.Drawing.Size(617, 413);
|
||||
this.tabControl1.TabIndex = 3;
|
||||
//
|
||||
// SetupPage
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(814, 467);
|
||||
this.ControlBox = false;
|
||||
this.Controls.Add(this.tabControl1);
|
||||
this.Controls.Add(this.panel2);
|
||||
this.Controls.Add(this.panel1);
|
||||
this.Margin = new System.Windows.Forms.Padding(2);
|
||||
this.MaximizeBox = false;
|
||||
this.MinimizeBox = false;
|
||||
this.Name = "SetupPage";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.Text = "Setup";
|
||||
this.panel1.ResumeLayout(false);
|
||||
this.panel1.PerformLayout();
|
||||
this.panel2.ResumeLayout(false);
|
||||
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.Panel panel1;
|
||||
private System.Windows.Forms.Button button1;
|
||||
private System.Windows.Forms.Panel panel2;
|
||||
private System.Windows.Forms.PictureBox pictureBox1;
|
||||
private System.Windows.Forms.TabControl tabControl1;
|
||||
private System.Windows.Forms.CheckBox checkBox1;
|
||||
}
|
||||
}
|
||||
156
framework/Inspectron.Settings.WindowsWizard/SetupPage.cs
Normal file
156
framework/Inspectron.Settings.WindowsWizard/SetupPage.cs
Normal file
@@ -0,0 +1,156 @@
|
||||
using Inspectron.Settings.WindowsWizard.Enums;
|
||||
using Inspectron.Settings.WindowsWizard.Interfaces;
|
||||
|
||||
namespace Inspectron.Settings.WindowsWizard
|
||||
{
|
||||
public partial class SetupPage : Form
|
||||
{
|
||||
public SetupPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public SetupPage PreviousPage { get; set; }
|
||||
public SetupPage NextPage { get; set; }
|
||||
public FlowLayoutPanel CurrentLayout { get; set; }
|
||||
|
||||
public static SetupPage Begin(string tabName,string title=null,Func<bool> expertModeCheck=null)
|
||||
{
|
||||
|
||||
var sp = new SetupPage();
|
||||
if (title != null)
|
||||
{
|
||||
sp.Text = title;
|
||||
}
|
||||
sp.ExpertModeCheck = expertModeCheck;
|
||||
if (sp.ExpertModeCheck == null)
|
||||
{
|
||||
sp.checkBox1.Visible = false;
|
||||
}
|
||||
TabPage tp = new TabPage();
|
||||
tp.Text = tabName;
|
||||
FlowLayoutPanel panel = new FlowLayoutPanel();
|
||||
panel.Dock = DockStyle.Fill;
|
||||
panel.FlowDirection = FlowDirection.TopDown;
|
||||
tp.Controls.Add(panel);
|
||||
sp.tabControl1.TabPages.Add(tp);
|
||||
sp.CurrentLayout = panel;
|
||||
return sp;
|
||||
}
|
||||
|
||||
public Func<bool> ExpertModeCheck { get; set; }
|
||||
List<TabPage> _expertPages=new List<TabPage>();
|
||||
public SetupPage Next(string tabName,bool forExpert=false)
|
||||
{
|
||||
TabPage tp = new TabPage();
|
||||
tp.Text = tabName;
|
||||
|
||||
FlowLayoutPanel panel = new FlowLayoutPanel();
|
||||
panel.Dock = DockStyle.Fill;
|
||||
panel.FlowDirection = FlowDirection.TopDown;
|
||||
tp.Controls.Add(panel);
|
||||
|
||||
|
||||
if (forExpert)
|
||||
{
|
||||
_expertPages.Add(tp);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.tabControl1.TabPages.Add(tp);
|
||||
}
|
||||
this.CurrentLayout = panel;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public static string SplitCamelCase(string input)
|
||||
{
|
||||
return System.Text.RegularExpressions.Regex.Replace(input, "([A-Z])", " $1", System.Text.RegularExpressions.RegexOptions.Compiled).Trim();
|
||||
}
|
||||
|
||||
public ESetupResult SetupResult { get; private set; } = ESetupResult.Cancel;
|
||||
|
||||
public void ShowSetup()
|
||||
{
|
||||
SetupPage start = this;
|
||||
while (start.PreviousPage!=null)
|
||||
{
|
||||
start = start.PreviousPage;
|
||||
}
|
||||
|
||||
start.Show();
|
||||
}
|
||||
public void ShowSetupDialog()
|
||||
{
|
||||
SetupPage start = this;
|
||||
while (start.PreviousPage != null)
|
||||
{
|
||||
start = start.PreviousPage;
|
||||
}
|
||||
|
||||
start.ShowDialog();
|
||||
}
|
||||
public SetupPage FinishWith(Action setupFinished)
|
||||
{
|
||||
this.SetupFinished = setupFinished;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Action SetupFinished { get; set; }
|
||||
|
||||
public SetupPage AddItem(ISetupItem item)
|
||||
{
|
||||
CurrentLayout.Controls.Add(item as UserControl);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SetupPage AddItem<T>(object obj, string prp,Action<T> itemAction=null) where T:ISetupItem,new()
|
||||
{
|
||||
T pe = new T();
|
||||
itemAction?.Invoke(pe);
|
||||
pe.SetProperty(obj, prp);
|
||||
(pe as UserControl).Width = 500;
|
||||
|
||||
this.AddItem(pe);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void button1_Click(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
SetupFinished?.Invoke();
|
||||
this.Close();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void checkBox1_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (checkBox1.Checked)
|
||||
{
|
||||
if ((ExpertModeCheck?.Invoke() ?? true))
|
||||
{
|
||||
foreach (TabPage page in _expertPages)
|
||||
{
|
||||
this.tabControl1.TabPages.Add(page);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
checkBox1.Checked = false;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (TabPage page in _expertPages)
|
||||
{
|
||||
this.tabControl1.TabPages.Remove(page);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
60
framework/Inspectron.Settings.WindowsWizard/SetupPage.resx
Normal file
60
framework/Inspectron.Settings.WindowsWizard/SetupPage.resx
Normal file
@@ -0,0 +1,60 @@
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<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" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</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" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
164
framework/Inspectron.Settings/AdaptablePath.cs
Normal file
164
framework/Inspectron.Settings/AdaptablePath.cs
Normal file
@@ -0,0 +1,164 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Inspectron.Settings
|
||||
{
|
||||
public class AdaptablePath<T> : Path<T>, IAdaptable, IDecoratable
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor</summary>
|
||||
/// <param name="last">Single object making up the path</param>
|
||||
public AdaptablePath(T last)
|
||||
: base(last)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor</summary>
|
||||
/// <param name="path">Path as sequence of objects</param>
|
||||
public AdaptablePath(IEnumerable<T> path)
|
||||
: base(path)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor</summary>
|
||||
/// <param name="path">Path as collection of objects</param>
|
||||
public AdaptablePath(ICollection<T> path)
|
||||
: base(path)
|
||||
{
|
||||
}
|
||||
|
||||
#region IAdaptable, IDecoratable, and Related Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets an adapter of the specified type or null</summary>
|
||||
/// <param name="type">Adapter type</param>
|
||||
/// <returns>Adapter of the specified type or null</returns>
|
||||
public object GetAdapter(Type type)
|
||||
{
|
||||
object adapter = Last.As(type);
|
||||
if (adapter != null)
|
||||
return adapter;
|
||||
|
||||
if (type.IsAssignableFrom(GetType()))
|
||||
return this;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all decorators of the specified type</summary>
|
||||
/// <param name="type">Decorator type</param>
|
||||
/// <returns>Enumeration of non-null decorators that are of the specified type. The enumeration may be empty.</returns>
|
||||
public IEnumerable<object> GetDecorators(Type type)
|
||||
{
|
||||
foreach (object obj in Last.AsAll(type))
|
||||
yield return obj;
|
||||
}
|
||||
|
||||
// implement the following members as a convenience when extension methods aren't available
|
||||
|
||||
/// <summary>
|
||||
/// Converts a reference to the given type by first trying a CLR cast, and then
|
||||
/// trying to get an adapter</summary>
|
||||
/// <typeparam name="U">Desired type, must be ref type</typeparam>
|
||||
/// <returns>Converted reference for the given object or null</returns>
|
||||
public U As<U>()
|
||||
where U : class
|
||||
{
|
||||
return Adapters.As<U>(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a reference to the given type by first trying a CLR cast, and then
|
||||
/// trying to get an adapter; if none is available, throws an AdaptationException</summary>
|
||||
/// <typeparam name="U">Desired type, must be ref type</typeparam>
|
||||
/// <returns>Converted reference for the given object</returns>
|
||||
public U Cast<U>() where U : class
|
||||
{
|
||||
return Adapters.Cast<U>(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the given reference can be converted to one of
|
||||
/// the desired type</summary>
|
||||
/// <typeparam name="U">Adapter type, must be ref type</typeparam>
|
||||
/// <returns>True iff the given object can be converted</returns>
|
||||
public bool Is<U>()
|
||||
where U : class
|
||||
{
|
||||
return Adapters.Is<U>(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumeration of all decorators that can convert a reference to the given type</summary>
|
||||
/// <typeparam name="U">Decorator type, must be ref type</typeparam>
|
||||
/// <returns>Enumerable returning all decorators of the given type</returns>
|
||||
public IEnumerable<U> AsAll<U>()
|
||||
where U : class
|
||||
{
|
||||
return Adapters.AsAll<U>(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Concatenates object with path</summary>
|
||||
/// <param name="lhs">Prefix object</param>
|
||||
/// <param name="rhs">Optional path</param>
|
||||
/// <returns>Concatenated path, with lhs as first object</returns>
|
||||
public static AdaptablePath<T> operator +(T lhs, AdaptablePath<T> rhs)
|
||||
{
|
||||
if (rhs == null)
|
||||
return new AdaptablePath<T>(lhs);
|
||||
T[] path = new T[1 + rhs.Count];
|
||||
path[0] = lhs;
|
||||
rhs.CopyTo(path, 1);
|
||||
return new AdaptablePath<T>(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Concatenates path with object</summary>
|
||||
/// <param name="lhs">Optional path</param>
|
||||
/// <param name="rhs">Suffix object</param>
|
||||
/// <returns>Concatenated path, with rhs as last object</returns>
|
||||
public static AdaptablePath<T> operator +(AdaptablePath<T> lhs, T rhs)
|
||||
{
|
||||
if (lhs == null)
|
||||
return new AdaptablePath<T>(rhs);
|
||||
T[] path = new T[lhs.Count + 1];
|
||||
lhs.CopyTo(path, 0);
|
||||
path[lhs.Count] = rhs;
|
||||
return new AdaptablePath<T>(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Concatenates 2 paths</summary>
|
||||
/// <param name="lhs">First path. Can be null.</param>
|
||||
/// <param name="rhs">Second path. Can be null.</param>
|
||||
/// <returns>Concatenated path, with rhs as prefix and lhs as suffix. Is null if both lhs and rhs are null.</returns>
|
||||
public static AdaptablePath<T> operator +(AdaptablePath<T> lhs, AdaptablePath<T> rhs)
|
||||
{
|
||||
if (lhs == null)
|
||||
return rhs;
|
||||
if (rhs == null)
|
||||
return lhs;
|
||||
T[] path = new T[lhs.Count + rhs.Count];
|
||||
lhs.CopyTo(path, 0);
|
||||
rhs.CopyTo(path, lhs.Count);
|
||||
return new AdaptablePath<T>(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts from the path type to another type</summary>
|
||||
/// <typeparam name="U">Desired type</typeparam>
|
||||
/// <param name="item">Item to convert</param>
|
||||
/// <returns>Item converted to given type or null</returns>
|
||||
protected override U Convert<U>(T item)
|
||||
{
|
||||
U u = item.As<U>();
|
||||
return u;
|
||||
}
|
||||
}
|
||||
}
|
||||
25
framework/Inspectron.Settings/AdaptationException.cs
Normal file
25
framework/Inspectron.Settings/AdaptationException.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
|
||||
namespace Inspectron.Settings
|
||||
{
|
||||
public class AdaptationException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor</summary>
|
||||
/// <param name="message">Message explaining why this object couldn't be adapted</param>
|
||||
public AdaptationException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor</summary>
|
||||
/// <param name="message">Message explaining why this object couldn't be adapted</param>
|
||||
/// <param name="innerException">The exception that prevented adaptation. Will become the
|
||||
/// InnerException property.</param>
|
||||
public AdaptationException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
336
framework/Inspectron.Settings/Adapters.cs
Normal file
336
framework/Inspectron.Settings/Adapters.cs
Normal file
@@ -0,0 +1,336 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Inspectron.Settings
|
||||
{
|
||||
public static class Adapters
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a reference to the given type by first trying a CLR cast, and then
|
||||
/// trying to get an adapter</summary>
|
||||
/// <param name="reference">Reference to convert</param>
|
||||
/// <param name="type">Desired type, should be ref type</param>
|
||||
/// <returns>Converted reference for the given object or null</returns>
|
||||
public static object As(this object reference, Type type)
|
||||
{
|
||||
if (reference == null)
|
||||
return null;
|
||||
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
// is the adapted object compatible?
|
||||
if (type.IsAssignableFrom(reference.GetType()))
|
||||
return reference;
|
||||
|
||||
// try to get an adapter
|
||||
var adaptable = reference as IAdaptable;
|
||||
if (adaptable != null)
|
||||
{
|
||||
object adapter = adaptable.GetAdapter(type);
|
||||
if (adapter != null)
|
||||
return adapter;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a reference to the given type by first trying a CLR cast, and then
|
||||
/// trying to get an adapter</summary>
|
||||
/// <typeparam name="T">Desired type, must be ref type</typeparam>
|
||||
/// <param name="reference">Reference to convert</param>
|
||||
/// <returns>Converted reference for the given object or null</returns>
|
||||
public static T As<T>(this object reference)
|
||||
where T : class
|
||||
{
|
||||
if (reference == null)
|
||||
return null;
|
||||
|
||||
// try a normal cast
|
||||
var converted = reference as T;
|
||||
|
||||
// if that fails, try to get an adapter
|
||||
if (converted == null)
|
||||
{
|
||||
var adaptable = reference as IAdaptable;
|
||||
if (adaptable != null)
|
||||
converted = adaptable.GetAdapter(typeof(T)) as T;
|
||||
}
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a reference to the given type by first trying a CLR cast, and then
|
||||
/// trying to get an adapter</summary>
|
||||
/// <typeparam name="T">Desired type, must be ref type</typeparam>
|
||||
/// <param name="adaptable">Adaptable object</param>
|
||||
/// <returns>Converted reference for the given object or null</returns>
|
||||
public static T As<T>(this IAdaptable adaptable)
|
||||
where T : class
|
||||
{
|
||||
if (adaptable == null)
|
||||
return null;
|
||||
|
||||
// try a normal cast
|
||||
var converted = adaptable as T;
|
||||
|
||||
// if that fails, try to get an adapter
|
||||
if (converted == null)
|
||||
converted = adaptable.GetAdapter(typeof(T)) as T;
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a reference to the given type by first trying a CLR cast, and then
|
||||
/// trying to get an adapter; if none is available, throws an AdaptationException</summary>
|
||||
/// <param name="reference">Reference to convert</param>
|
||||
/// <param name="type">Desired type, should be ref type</param>
|
||||
/// <returns>Converted reference for the given object</returns>
|
||||
public static object Cast(this object reference, Type type)
|
||||
{
|
||||
object converted = As(reference, type);
|
||||
if (converted == null)
|
||||
throw new AdaptationException(type.Name + " adapter required");
|
||||
return converted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a reference to the given type by first trying a CLR cast, and then
|
||||
/// trying to get an adapter; if none is available, throws an AdaptationException</summary>
|
||||
/// <typeparam name="T">Desired type, must be ref type</typeparam>
|
||||
/// <param name="reference">Reference to convert</param>
|
||||
/// <returns>Converted reference for the given object</returns>
|
||||
public static T Cast<T>(this object reference)
|
||||
where T : class
|
||||
{
|
||||
T converted = As<T>(reference);
|
||||
if (converted == null)
|
||||
throw new AdaptationException(typeof(T).Name + " adapter required");
|
||||
return converted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a reference to the given type by first trying a CLR cast, and then
|
||||
/// trying to get an adapter; if none is available, throws an AdaptationException</summary>
|
||||
/// <typeparam name="T">Desired type, must be ref type</typeparam>
|
||||
/// <param name="adaptable">Adaptable object</param>
|
||||
/// <returns>Converted reference for the given object</returns>
|
||||
public static T Cast<T>(this IAdaptable adaptable)
|
||||
where T : class
|
||||
{
|
||||
T converted = As<T>(adaptable);
|
||||
if (converted == null)
|
||||
throw new AdaptationException(typeof(T).Name + " adapter required");
|
||||
return converted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating if the given reference can be converted to one of
|
||||
/// the desired type</summary>
|
||||
/// <param name="reference">Reference to test</param>
|
||||
/// <param name="type">Desired type, should be ref type</param>
|
||||
/// <returns>True iff the given object can be converted</returns>
|
||||
public static bool Is(this object reference, Type type)
|
||||
{
|
||||
return As(reference, type) != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating if the given reference can be converted to one of
|
||||
/// the desired type</summary>
|
||||
/// <typeparam name="T">Adapter type, must be ref type</typeparam>
|
||||
/// <param name="reference">Reference to test</param>
|
||||
/// <returns>True iff the given object can be converted</returns>
|
||||
public static bool Is<T>(this object reference)
|
||||
where T : class
|
||||
{
|
||||
return As<T>(reference) != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating if the given reference can be converted to one of
|
||||
/// the desired type</summary>
|
||||
/// <typeparam name="T">Adapter type, must be ref type</typeparam>
|
||||
/// <param name="adaptable">Adaptable object</param>
|
||||
/// <returns>True iff the given object can be converted</returns>
|
||||
public static bool Is<T>(this IAdaptable adaptable)
|
||||
where T : class
|
||||
{
|
||||
return As<T>(adaptable) != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all decorators that can convert a reference to the given type</summary>
|
||||
/// <param name="reference">Reference to convert</param>
|
||||
/// <param name="type">Decorator type, should be ref type</param>
|
||||
/// <returns>Enumerable returning all decorators of the given type.
|
||||
/// Decorators are never null. The enumeration may be empty.</returns>
|
||||
public static IEnumerable<object> AsAll(this object reference, Type type)
|
||||
{
|
||||
if (reference != null)
|
||||
{
|
||||
// if IDecoratable, use that to get decorators
|
||||
var decoratable = reference as IDecoratable;
|
||||
if (decoratable != null)
|
||||
return decoratable.GetDecorators(type);
|
||||
|
||||
// is the decorated object compatible?
|
||||
if (type.IsAssignableFrom(reference.GetType()))
|
||||
return new object[] { reference };
|
||||
}
|
||||
|
||||
return new List<object>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all decorators that can convert a reference to the given type</summary>
|
||||
/// <typeparam name="T">Decorator type, must be ref type</typeparam>
|
||||
/// <param name="reference">Reference to convert</param>
|
||||
/// <returns>Enumerable returning all decorators of the given type.
|
||||
/// Decorators are never null. The enumeration may be empty.</returns>
|
||||
public static IEnumerable<T> AsAll<T>(this object reference)
|
||||
where T : class
|
||||
{
|
||||
if (reference != null)
|
||||
{
|
||||
// if IDecoratable, use that to get decorators
|
||||
var decoratable = reference as IDecoratable;
|
||||
if (decoratable != null)
|
||||
return AsAll<T>(decoratable);
|
||||
|
||||
// otherwise, cast
|
||||
var t = reference as T;
|
||||
if (t != null)
|
||||
return new T[] { t };
|
||||
}
|
||||
|
||||
return new List<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enumeration of all decorators that can convert a reference to the given type</summary>
|
||||
/// <typeparam name="T">Decorator type, must be ref type</typeparam>
|
||||
/// <param name="decoratable">Decoratable object</param>
|
||||
/// <returns>Enumerable returning all decorators of the given type</returns>
|
||||
public static IEnumerable<T> AsAll<T>(this IDecoratable decoratable)
|
||||
where T : class
|
||||
{
|
||||
if (decoratable != null)
|
||||
{
|
||||
foreach (object decorator in decoratable.GetDecorators(typeof(T)))
|
||||
yield return decorator as T;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an adapter that converts an enumerable to an enumerable of another type</summary>
|
||||
/// <param name="enumerable">Enumerable to adapt</param>
|
||||
/// <param name="type">Adapter type, should be ref type</param>
|
||||
/// <returns>Enumerable returning adapted items</returns>
|
||||
public static IEnumerable<object> AsIEnumerable(this IEnumerable enumerable, Type type)
|
||||
{
|
||||
if (enumerable != null)
|
||||
{
|
||||
foreach (object item in enumerable)
|
||||
{
|
||||
object adapter = As(item, type);
|
||||
if (adapter != null)
|
||||
yield return adapter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumeration for an adapter that converts an enumerable to an enumerable of another type</summary>
|
||||
/// <typeparam name="T">Adapter type, must be ref type</typeparam>
|
||||
/// <param name="enumerable">Enumerable to adapt</param>
|
||||
/// <returns>Enumerable returning adapted items</returns>
|
||||
public static IEnumerable<T> AsIEnumerable<T>(this IEnumerable enumerable)
|
||||
where T : class
|
||||
{
|
||||
if (enumerable != null)
|
||||
{
|
||||
foreach (object item in enumerable)
|
||||
{
|
||||
T adapter = As<T>(item);
|
||||
if (adapter != null)
|
||||
yield return adapter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating if any of the items in the enumerable are
|
||||
/// adaptable to the given type</summary>
|
||||
/// <param name="enumerable">Enumerable to adapt</param>
|
||||
/// <param name="type">Adapter type, should be ref type</param>
|
||||
/// <returns>True iff any of the items in the enumerable are adaptable to
|
||||
/// the given type</returns>
|
||||
public static bool Any(this IEnumerable enumerable, Type type)
|
||||
{
|
||||
if (enumerable != null)
|
||||
{
|
||||
foreach (object item in enumerable)
|
||||
{
|
||||
object adapter = As(item, type);
|
||||
if (adapter != null)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating if any of the items in the enumerable are
|
||||
/// adaptable to the given type</summary>
|
||||
/// <typeparam name="T">Adapter type, must be ref type</typeparam>
|
||||
/// <param name="enumerable">Enumerable to adapt</param>
|
||||
/// <returns>True iff any of the items in the enumerable are adaptable to
|
||||
/// the given type</returns>
|
||||
public static bool Any<T>(this IEnumerable enumerable)
|
||||
where T : class
|
||||
{
|
||||
return Any(enumerable, typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating if all of the items in the enumerable are
|
||||
/// adaptable to the given type</summary>
|
||||
/// <param name="enumerable">Enumerable to adapt</param>
|
||||
/// <param name="type">Adapter type, should be ref type</param>
|
||||
/// <returns>True iff all of the items in the enumerable are adaptable to
|
||||
/// the given type</returns>
|
||||
public static bool All(this IEnumerable enumerable, Type type)
|
||||
{
|
||||
if (enumerable != null)
|
||||
{
|
||||
foreach (object item in enumerable)
|
||||
{
|
||||
object adapter = As(item, type);
|
||||
if (adapter == null)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating if all of the items in the enumerable are
|
||||
/// adaptable to the given type</summary>
|
||||
/// <typeparam name="T">Adapter type, must be ref type</typeparam>
|
||||
/// <param name="enumerable">Enumerable to adapt</param>
|
||||
/// <returns>True iff all of the items in the enumerable are adaptable to
|
||||
/// the given type</returns>
|
||||
public static bool All<T>(this IEnumerable enumerable)
|
||||
where T : class
|
||||
{
|
||||
return All(enumerable, typeof(T));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace Inspectron.Settings.Attributes
|
||||
{
|
||||
public class SettingDescriptionAttribute: Attribute
|
||||
{
|
||||
public string Description { get; }
|
||||
|
||||
public SettingDescriptionAttribute(string description)
|
||||
{
|
||||
Description = description;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
|
||||
namespace Inspectron.Settings.Attributes
|
||||
{
|
||||
public class SettingPreviewAttribute: System.Attribute
|
||||
{
|
||||
public Type PreviewClass { get; }
|
||||
public string PreviewFunction { get; }
|
||||
|
||||
|
||||
public SettingPreviewAttribute(Type previewClass, string previewFunction)
|
||||
{
|
||||
PreviewClass = previewClass;
|
||||
PreviewFunction = previewFunction;
|
||||
}
|
||||
}
|
||||
}
|
||||
494
framework/Inspectron.Settings/BoundPropertyDescriptor.cs
Normal file
494
framework/Inspectron.Settings/BoundPropertyDescriptor.cs
Normal file
@@ -0,0 +1,494 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Inspectron.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// A specialization of System.ComponentModel.PropertyDescriptor that is bound
|
||||
/// to a specific property of an object or type. If the property's setter is private,
|
||||
/// this BoundPropertyDescriptor's IsReadOnly property is true.</summary>
|
||||
/// <remarks>Use this class to expose an object or type's property for property editing.</remarks>
|
||||
public class BoundPropertyDescriptor : PropertyDescriptor
|
||||
{
|
||||
public delegate object OnClickDelegate(object sender, EventArgs e);
|
||||
public OnClickDelegate OnClick { get; set; }
|
||||
/// <summary>
|
||||
/// Constructor for instance properties</summary>
|
||||
/// <param name="owner">Property owner</param>
|
||||
/// <param name="expression">Lambda expression that accesses the property;
|
||||
/// e.g., () => myObject.MyProperty</param>
|
||||
/// <param name="displayName">Property display name</param>
|
||||
/// <param name="category">Property category</param>
|
||||
/// <param name="description">Property description</param>
|
||||
public BoundPropertyDescriptor(
|
||||
object owner,
|
||||
Expression<Func<object>> expression,
|
||||
string displayName,
|
||||
string category,
|
||||
string description)
|
||||
|
||||
: this(displayName, category, description)
|
||||
{
|
||||
PropertyInfo propertyInfo = GetPropertyInfo(expression);
|
||||
Init(owner, null, null, propertyInfo, null, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for static properties</summary>
|
||||
/// <param name="ownerType">Type holding static property</param>
|
||||
/// <param name="expression">Lambda expression that accesses the property;
|
||||
/// e.g., () => MyClass.MyProperty</param>
|
||||
/// <param name="displayName">Property display name</param>
|
||||
/// <param name="category">Property category</param>
|
||||
/// <param name="description">Property description</param>
|
||||
public BoundPropertyDescriptor(
|
||||
Type ownerType,
|
||||
Expression<Func<object>> expression,
|
||||
string displayName,
|
||||
string category,
|
||||
string description)
|
||||
|
||||
: this(displayName, category, description)
|
||||
{
|
||||
PropertyInfo propertyInfo = GetPropertyInfo(expression);
|
||||
Init(null, ownerType, null, propertyInfo, null, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for instance properties</summary>
|
||||
/// <param name="owner">Property owner</param>
|
||||
/// <param name="expression">Lambda expression that accesses the property;
|
||||
/// e.g., () => myObject.MyProperty</param>
|
||||
/// <param name="displayName">Property display name</param>
|
||||
/// <param name="category">Property category</param>
|
||||
/// <param name="description">Property description</param>
|
||||
/// <param name="editor">Editor for property</param>
|
||||
/// <param name="converter">TypeConverter for property</param>
|
||||
public BoundPropertyDescriptor(
|
||||
object owner,
|
||||
Expression<Func<object>> expression,
|
||||
string displayName,
|
||||
string category,
|
||||
string description,
|
||||
object editor,
|
||||
TypeConverter converter)
|
||||
|
||||
: this(displayName, category, description)
|
||||
{
|
||||
PropertyInfo propertyInfo = GetPropertyInfo(expression);
|
||||
Init(owner, null, null, propertyInfo, editor, converter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for static properties</summary>
|
||||
/// <param name="ownerType">Type holding static property</param>
|
||||
/// <param name="expression">Lambda expression that accesses the property;
|
||||
/// e.g., () => MyClass.MyProperty</param>
|
||||
/// <param name="displayName">Property display name</param>
|
||||
/// <param name="category">Property category</param>
|
||||
/// <param name="description">Property description</param>
|
||||
/// <param name="editor">Editor for property</param>
|
||||
/// <param name="converter">TypeConverter for property</param>
|
||||
public BoundPropertyDescriptor(
|
||||
Type ownerType,
|
||||
Expression<Func<object>> expression,
|
||||
string displayName,
|
||||
string category,
|
||||
string description,
|
||||
object editor,
|
||||
TypeConverter converter)
|
||||
|
||||
: this(displayName, category, description)
|
||||
{
|
||||
PropertyInfo propertyInfo = GetPropertyInfo(expression);
|
||||
Init(null, ownerType, null, propertyInfo, editor, converter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for instance properties</summary>
|
||||
/// <param name="owner">Property owner</param>
|
||||
/// <param name="name">Property name</param>
|
||||
/// <param name="displayName">Property display name</param>
|
||||
/// <param name="category">Property category</param>
|
||||
/// <param name="description">Property description</param>
|
||||
public BoundPropertyDescriptor(
|
||||
object owner,
|
||||
string name,
|
||||
string displayName,
|
||||
string category,
|
||||
string description)
|
||||
|
||||
: this(displayName, category, description)
|
||||
{
|
||||
Init(owner, null, name, null, null, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for instance properties</summary>
|
||||
/// <param name="owner">Property owner</param>
|
||||
/// <param name="name">Property name</param>
|
||||
/// <param name="displayName">Property display name</param>
|
||||
/// <param name="category">Property category</param>
|
||||
/// <param name="description">Property description</param>
|
||||
/// <param name="editor">Editor for property</param>
|
||||
public BoundPropertyDescriptor(
|
||||
object owner,
|
||||
string name,
|
||||
string displayName,
|
||||
string category,
|
||||
string description,
|
||||
object editor)
|
||||
|
||||
: this(displayName, category, description)
|
||||
{
|
||||
Init(owner, null, name, null, editor, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for instance properties</summary>
|
||||
/// <param name="owner">Property owner</param>
|
||||
/// <param name="name">Property name</param>
|
||||
/// <param name="displayName">Property display name</param>
|
||||
/// <param name="category">Property category</param>
|
||||
/// <param name="description">Property description</param>
|
||||
/// <param name="editor">Editor for property</param>
|
||||
/// <param name="converter">TypeConverter for property</param>
|
||||
public BoundPropertyDescriptor(
|
||||
object owner,
|
||||
string name,
|
||||
string displayName,
|
||||
string category,
|
||||
string description,
|
||||
object editor,
|
||||
TypeConverter converter)
|
||||
|
||||
: this(displayName, category, description)
|
||||
{
|
||||
Init(owner, null, name, null, editor, converter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for static properties</summary>
|
||||
/// <param name="ownerType">Type holding static property</param>
|
||||
/// <param name="name">Property name</param>
|
||||
/// <param name="displayName">Property display name</param>
|
||||
/// <param name="category">Property category</param>
|
||||
/// <param name="description">Property description</param>
|
||||
public BoundPropertyDescriptor(
|
||||
Type ownerType,
|
||||
string name,
|
||||
string displayName,
|
||||
string category,
|
||||
string description)
|
||||
|
||||
: this(displayName, category, description)
|
||||
{
|
||||
Init(null, ownerType, name, null, null, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for static properties</summary>
|
||||
/// <param name="ownerType">Type holding static property</param>
|
||||
/// <param name="name">Property name</param>
|
||||
/// <param name="displayName">Property display name</param>
|
||||
/// <param name="category">Property category</param>
|
||||
/// <param name="description">Property description</param>
|
||||
/// <param name="editor">Editor for property</param>
|
||||
public BoundPropertyDescriptor(
|
||||
Type ownerType,
|
||||
string name,
|
||||
string displayName,
|
||||
string category,
|
||||
string description,
|
||||
object editor)
|
||||
|
||||
: this(displayName, category, description)
|
||||
{
|
||||
Init(null, ownerType, name, null, editor, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for static properties</summary>
|
||||
/// <param name="ownerType">Type holding static property</param>
|
||||
/// <param name="name">Property name</param>
|
||||
/// <param name="displayName">Property display name</param>
|
||||
/// <param name="category">Property category</param>
|
||||
/// <param name="description">Property description</param>
|
||||
/// <param name="editor">Editor for property</param>
|
||||
/// <param name="converter">TypeConverter for property</param>
|
||||
public BoundPropertyDescriptor(
|
||||
Type ownerType,
|
||||
string name,
|
||||
string displayName,
|
||||
string category,
|
||||
string description,
|
||||
object editor,
|
||||
TypeConverter converter)
|
||||
|
||||
: this(displayName, category, description)
|
||||
{
|
||||
Init(null, ownerType, name, null, editor, converter);
|
||||
}
|
||||
|
||||
private void Init(
|
||||
object owner,
|
||||
Type ownerType,
|
||||
string name,
|
||||
PropertyInfo propertyInfo,
|
||||
object editor,
|
||||
TypeConverter converter)
|
||||
{
|
||||
m_owner = owner;
|
||||
|
||||
// if given the property owner, ignore the ownerType parameter
|
||||
if (owner != null)
|
||||
ownerType = owner.GetType();
|
||||
m_ownerType = ownerType;
|
||||
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
if (propertyInfo == null)
|
||||
throw new ArgumentException("either 'name' or 'propertyInfo' must be non-null");
|
||||
name = propertyInfo.Name;
|
||||
}
|
||||
|
||||
// if given the property info, don't use reflection to find it
|
||||
_propertyInfo = propertyInfo;
|
||||
if (_propertyInfo == null)
|
||||
{
|
||||
_propertyInfo = m_ownerType.GetProperty(
|
||||
name,
|
||||
BindingFlags.Public |
|
||||
BindingFlags.Instance |
|
||||
BindingFlags.Static);
|
||||
|
||||
if (_propertyInfo == null)
|
||||
throw new ArgumentException(name + ": Property doesn't exist");
|
||||
}
|
||||
|
||||
// look at "set_" method to determine if property is read-only.
|
||||
// (PropertyInfo.CanWrite will return true if there is a set
|
||||
// accessor, even if that accessor is made inaccessible using
|
||||
// asymmetric accessor accessibility.)
|
||||
MethodInfo setInfo = m_ownerType.GetMethod("set_" + name,
|
||||
BindingFlags.Public |
|
||||
BindingFlags.Instance |
|
||||
BindingFlags.Static);
|
||||
|
||||
m_readOnly = (setInfo == null);
|
||||
|
||||
m_editor = editor;
|
||||
m_typeConverter = converter;
|
||||
}
|
||||
|
||||
private BoundPropertyDescriptor(
|
||||
string displayName,
|
||||
string category,
|
||||
string description)
|
||||
|
||||
: base(displayName,
|
||||
new Attribute[] { new CategoryAttribute(category),
|
||||
new DescriptionAttribute(description), })
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When overridden in a derived class, returns whether resetting an object changes its value</summary>
|
||||
/// <param name="component">The component to test for reset capability</param>
|
||||
/// <returns>True iff resetting the component changes its value</returns>
|
||||
public override bool CanResetValue(object component)
|
||||
{
|
||||
object defaultValue;
|
||||
return GetDefaultValue(out defaultValue) && !Object.Equals(GetValue(null), defaultValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the component this property is bound to. Is null, if bound to a static class's property.</summary>
|
||||
public object Owner
|
||||
{
|
||||
get { return m_owner; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the component this property is bound to</summary>
|
||||
public override Type ComponentType
|
||||
{
|
||||
get { return m_ownerType; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the property</summary>
|
||||
public override Type PropertyType
|
||||
{
|
||||
get { return _propertyInfo.PropertyType; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether this property is read-only</summary>
|
||||
public override bool IsReadOnly
|
||||
{
|
||||
get { return m_readOnly; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the value for this property of the component to the default value</summary>
|
||||
/// <param name="component">The component with the property value that is to be reset to the default value</param>
|
||||
public override void ResetValue(object component)
|
||||
{
|
||||
object defaultValue;
|
||||
GetDefaultValue(out defaultValue);
|
||||
SetValue(component, defaultValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the value of this property needs to be persisted</summary>
|
||||
/// <param name="component">The component with the property to be examined for persistence</param>
|
||||
/// <returns>True iff the property should be persisted</returns>
|
||||
public override bool ShouldSerializeValue(object component)
|
||||
{
|
||||
object val = GetValue(component);
|
||||
|
||||
object defaultValue;
|
||||
if (!GetDefaultValue(out defaultValue) && val == null)
|
||||
return false;
|
||||
else
|
||||
return !val.Equals(defaultValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the Owner's value (if the Owner is not null) or the component's value of the property</summary>
|
||||
/// <param name="component">Component to examine</param>
|
||||
/// <returns>The value of a property</returns>
|
||||
public override object GetValue(object component)
|
||||
{
|
||||
if (m_owner != null)
|
||||
component = m_owner;
|
||||
return _propertyInfo.GetValue(component, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the value of the Owner (if not null) or component</summary>
|
||||
/// <param name="component">Component</param>
|
||||
/// <param name="value">The new value</param>
|
||||
public override void SetValue(object component, object value)
|
||||
{
|
||||
if (m_owner != null)
|
||||
component = m_owner;
|
||||
_propertyInfo.SetValue(component, value, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default value</summary>
|
||||
/// <param name="result">Is set to the default value or null, if it couldn't be determined</param>
|
||||
/// <returns>Whether or not the default value was determined</returns>
|
||||
/// <remarks>Uses reflection to look for a DefaultValueAttribute</remarks>
|
||||
public virtual bool GetDefaultValue(out object result)
|
||||
{
|
||||
bool foundDefault = false;
|
||||
result = null;
|
||||
object[] attributes = _propertyInfo.GetCustomAttributes(typeof(DefaultValueAttribute), false);
|
||||
if (attributes.Length > 0)
|
||||
{
|
||||
foundDefault = true;
|
||||
result = (attributes[0] as DefaultValueAttribute).Value;
|
||||
if (result != null && result.GetType() != _propertyInfo.PropertyType)
|
||||
{
|
||||
// Default value type is not the same as the property type; convert it.
|
||||
// This can happen if the property's type is not CLS-compliant (e.g. UInt32).
|
||||
TypeConverter converter = TypeDescriptor.GetConverter(result);
|
||||
if (converter.CanConvertTo(_propertyInfo.PropertyType))
|
||||
{
|
||||
result = converter.ConvertTo(result, _propertyInfo.PropertyType);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try using the converter associated with the source instead of the target
|
||||
// (Not sure if this is useful for not, but can it hurt?)
|
||||
converter = TypeDescriptor.GetConverter(_propertyInfo.PropertyType);
|
||||
if (converter.CanConvertFrom(result.GetType()))
|
||||
{
|
||||
result = converter.ConvertFrom(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return foundDefault;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an editor of the specified type</summary>
|
||||
/// <param name="editorBaseType">Base type of editor, which is used to differentiate between multiple
|
||||
/// editors that a property supports</param>
|
||||
/// <returns>An instance of the requested editor type, or null if an editor cannot be found</returns>
|
||||
public override object GetEditor(Type editorBaseType)
|
||||
{
|
||||
if (m_editor != null &&
|
||||
editorBaseType.IsInstanceOfType(m_editor))
|
||||
{
|
||||
return m_editor;
|
||||
}
|
||||
|
||||
if (editorBaseType.IsEnum)
|
||||
{
|
||||
|
||||
}
|
||||
return base.GetEditor(editorBaseType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type converter for this property</summary>
|
||||
public override TypeConverter Converter
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_typeConverter != null)
|
||||
return m_typeConverter;
|
||||
|
||||
return base.Converter;
|
||||
}
|
||||
}
|
||||
|
||||
public PropertyInfo PropertyInfo => _propertyInfo;
|
||||
|
||||
// Does not return null. Will throw an exception if 'expression' is poorly formed.
|
||||
private static PropertyInfo GetPropertyInfo(Expression<Func<object>> expression)
|
||||
{
|
||||
PropertyInfo propertyInfo = null;
|
||||
MemberExpression memberExpression = expression.Body as MemberExpression;
|
||||
if (memberExpression != null)
|
||||
{
|
||||
// this is the usual case for when a property has a public getter and setter
|
||||
propertyInfo = memberExpression.Member as PropertyInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if the setter is private, the expression is a UnaryExpression type for some reason.
|
||||
UnaryExpression unaryExpression = expression.Body as UnaryExpression;
|
||||
if (unaryExpression != null)
|
||||
{
|
||||
memberExpression = unaryExpression.Operand as MemberExpression;
|
||||
if (memberExpression != null)
|
||||
propertyInfo = memberExpression.Member as PropertyInfo;
|
||||
}
|
||||
}
|
||||
|
||||
if (propertyInfo == null)
|
||||
throw new ArgumentException(
|
||||
"lambda expression was not properly formed." +
|
||||
" Should be \" => myObject.MyProperty\" or" +
|
||||
" \" => MyClass.MyProperty\"");
|
||||
|
||||
return propertyInfo;
|
||||
}
|
||||
|
||||
object m_owner;
|
||||
Type m_ownerType;
|
||||
PropertyInfo _propertyInfo;
|
||||
bool m_readOnly;
|
||||
private object m_editor;
|
||||
private TypeConverter m_typeConverter;
|
||||
}
|
||||
}
|
||||
43
framework/Inspectron.Settings/Event.cs
Normal file
43
framework/Inspectron.Settings/Event.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Inspectron.Settings
|
||||
{
|
||||
public static class Event
|
||||
{
|
||||
public static void Raise(this EventHandler handler, object sender, EventArgs e)
|
||||
{
|
||||
if (handler != null) handler(sender, e);
|
||||
}
|
||||
|
||||
public static void Raise<T>(this EventHandler<T> handler, object sender, T e) where T : EventArgs
|
||||
{
|
||||
if (handler != null) handler(sender, e);
|
||||
}
|
||||
|
||||
public static bool RaiseCancellable(this CancelEventHandler handler, object sender, CancelEventArgs e)
|
||||
{
|
||||
if (handler != null)
|
||||
foreach (CancelEventHandler h in handler.GetInvocationList())
|
||||
{
|
||||
h(sender, e);
|
||||
if (e.Cancel) break;
|
||||
}
|
||||
|
||||
return e.Cancel;
|
||||
}
|
||||
|
||||
public static bool RaiseCancellable<T>(this EventHandler<T> handler, object sender, T e)
|
||||
where T : CancelEventArgs
|
||||
{
|
||||
if (handler != null)
|
||||
foreach (EventHandler<T> h in handler.GetInvocationList())
|
||||
{
|
||||
h(sender, e);
|
||||
if (e.Cancel) break;
|
||||
}
|
||||
|
||||
return e.Cancel;
|
||||
}
|
||||
}
|
||||
}
|
||||
13
framework/Inspectron.Settings/IAdaptable.cs
Normal file
13
framework/Inspectron.Settings/IAdaptable.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
|
||||
namespace Inspectron.Settings
|
||||
{
|
||||
public interface IAdaptable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets an adapter of the specified type or null</summary>
|
||||
/// <param name="type">Adapter type</param>
|
||||
/// <returns>Adapter of the specified type or null if no adapter available</returns>
|
||||
object GetAdapter(Type type);
|
||||
}
|
||||
}
|
||||
14
framework/Inspectron.Settings/IDecoratable.cs
Normal file
14
framework/Inspectron.Settings/IDecoratable.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Inspectron.Settings
|
||||
{
|
||||
public interface IDecoratable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets all decorators of the specified type</summary>
|
||||
/// <param name="type">Decorator type</param>
|
||||
/// <returns>Enumeration of non-null decorators that are of the specified type. The enumeration may be empty.</returns>
|
||||
IEnumerable<object> GetDecorators(Type type);
|
||||
}
|
||||
}
|
||||
20
framework/Inspectron.Settings/ITreeView.cs
Normal file
20
framework/Inspectron.Settings/ITreeView.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Inspectron.Settings
|
||||
{
|
||||
public interface ITreeView
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the root object of the tree view</summary>
|
||||
object Root
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Obtains enumeration of the children of the given parent object</summary>
|
||||
/// <param name="parent">Parent object</param>
|
||||
/// <returns>Enumeration of children of the parent object</returns>
|
||||
IEnumerable<object> GetChildren(object parent);
|
||||
}
|
||||
}
|
||||
8
framework/Inspectron.Settings/Inspectron.Settings.csproj
Normal file
8
framework/Inspectron.Settings/Inspectron.Settings.csproj
Normal file
@@ -0,0 +1,8 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<Configurations>Debug;Release;CPU</Configurations>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
845
framework/Inspectron.Settings/InspectronSettings.cs
Normal file
845
framework/Inspectron.Settings/InspectronSettings.cs
Normal file
@@ -0,0 +1,845 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Xml;
|
||||
|
||||
namespace Inspectron.Settings
|
||||
{
|
||||
|
||||
public class InspectronSettings
|
||||
{
|
||||
|
||||
public InspectronSettings(string path=null)
|
||||
{
|
||||
Assembly assembly = Assembly.GetEntryAssembly() ?? Assembly.GetExecutingAssembly();
|
||||
|
||||
AssemblyName assemblyName = assembly.GetName();
|
||||
_applicationName = assemblyName.Name;
|
||||
Version version = assemblyName.Version;
|
||||
_versionString = version.Major + "." + version.Minor;
|
||||
|
||||
string startupPath = Path.GetDirectoryName(new Uri(assemblyName.CodeBase).LocalPath);
|
||||
_defaultSettingsPath = Path.Combine(startupPath, "DefaultSettings.xml");
|
||||
|
||||
|
||||
|
||||
if (path == null)
|
||||
{
|
||||
var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||
_settingsPath = string.Format("{0}\\{1}\\{2}\\AppSettings.xml", appDataPath, _applicationName, _versionString);
|
||||
}
|
||||
else
|
||||
{
|
||||
_settingsPath = Path.Combine(path,"AppSettings.xml");
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
internal Path<object> GetSettingsPath(string pathName)
|
||||
{
|
||||
string[] pathSegments = pathName.Split('/', 16);
|
||||
object[] path = new object[pathSegments.Length + 1];
|
||||
|
||||
// first node is the settings tree root
|
||||
Tree<object> node = (Tree<object>)_userSettings;
|
||||
path[0] = _userSettings;
|
||||
|
||||
// middle nodes are folders
|
||||
for (int i = 1; i < path.Length - 1; i++)
|
||||
{
|
||||
node = GetOrCreateFolder(pathSegments[i - 1], node);
|
||||
path[i] = node;
|
||||
}
|
||||
|
||||
// leaf node is user settings object
|
||||
foreach (Tree<object> leaf in node.Children)
|
||||
{
|
||||
UserSettingsInfo info = leaf.Value as UserSettingsInfo;
|
||||
if (info != null && info.Name == pathSegments[pathSegments.Length - 1])
|
||||
{
|
||||
path[path.Length - 1] = leaf;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new Path<object>(path);
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets or sets the current state of all properties (Memento pattern)</summary>
|
||||
public object State
|
||||
{
|
||||
get
|
||||
{
|
||||
MemoryStream stream = new MemoryStream();
|
||||
Serialize(stream);
|
||||
return stream;
|
||||
}
|
||||
set
|
||||
{
|
||||
MemoryStream stream = value as MemoryStream;
|
||||
if (stream == null)
|
||||
throw new ArgumentException("Not a valid memento");
|
||||
stream.Position = 0;
|
||||
Deserialize(stream);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<PropertyDescriptor> UserPropertyDescriptors
|
||||
{
|
||||
get
|
||||
{
|
||||
var userSettings = UserSettings as Tree<object>;
|
||||
if (userSettings == null)
|
||||
throw new InvalidOperationException("userSettings");
|
||||
var all = userSettings.LevelOrder.Where(x => x.Value is UserSettingsInfo);
|
||||
foreach (Tree<object> node in all)
|
||||
{
|
||||
UserSettingsInfo info = node.Value as UserSettingsInfo;
|
||||
if (info != null)
|
||||
{
|
||||
foreach (PropertyDescriptor property in info.Settings)
|
||||
yield return property;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private static readonly object _unusedComponent = new object();
|
||||
public void SetDefaults()
|
||||
{
|
||||
foreach (SettingsInfo info in _settings.Values)
|
||||
{
|
||||
foreach (SettingsInfo.Setting setting in info.Settings.Values)
|
||||
{
|
||||
if (setting.PropertyDescriptor != null &&
|
||||
setting.PropertyDescriptor.CanResetValue(_unusedComponent))
|
||||
{
|
||||
setting.PropertyDescriptor.ResetValue(_unusedComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Load default settings if exist.
|
||||
if (File.Exists(_defaultSettingsPath))
|
||||
{
|
||||
using (Stream stream = File.OpenRead(_defaultSettingsPath))
|
||||
Deserialize(stream);
|
||||
}
|
||||
}
|
||||
internal List<PropertyDescriptor> GetProperties(Tree<object> tree)
|
||||
{
|
||||
var info = tree.Value as UserSettingsInfo;
|
||||
if (info != null)
|
||||
return info.Settings;
|
||||
|
||||
return null;
|
||||
}
|
||||
protected class SettingsInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor with name</summary>
|
||||
/// <param name="name">Name associated with this group of settings</param>
|
||||
public SettingsInfo(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a setting to group of settings as a name and value, replacing the previous value if present</summary>
|
||||
/// <param name="name">Setting name</param>
|
||||
/// <param name="valueString">Setting value</param>
|
||||
public void Add(string name, string valueString)
|
||||
{
|
||||
Setting setting;
|
||||
if (Settings.TryGetValue(name, out setting))
|
||||
{
|
||||
setting.Set(name, valueString);
|
||||
}
|
||||
else
|
||||
{
|
||||
Settings.Add(name, new Setting(name, valueString));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a setting to group of settings as a PropertyDescriptor, replacing the previous value if present</summary>
|
||||
/// <param name="descriptor">PropertyDescriptor describing setting</param>
|
||||
public void Add(PropertyDescriptor descriptor)
|
||||
{
|
||||
Setting setting;
|
||||
if (Settings.TryGetValue(descriptor.Name, out setting))
|
||||
{
|
||||
setting.Set(descriptor);
|
||||
}
|
||||
else
|
||||
{
|
||||
Settings.Add(descriptor.Name, new Setting(descriptor));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Name associated with this group of settings</summary>
|
||||
public readonly string Name;
|
||||
/// <summary>
|
||||
/// Dictionary for names and values of settings in group</summary>
|
||||
public readonly SortedDictionary<string, Setting> Settings = new SortedDictionary<string, Setting>();
|
||||
|
||||
/// <summary>
|
||||
/// Class for handling individual setting information</summary>
|
||||
public class Setting
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor with PropertyDescriptor</summary>
|
||||
/// <param name="descriptor">PropertyDescriptor describing setting</param>
|
||||
public Setting(PropertyDescriptor descriptor)
|
||||
{
|
||||
PropertyDescriptor = descriptor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with name and value of setting</summary>
|
||||
/// <param name="name">Setting name</param>
|
||||
/// <param name="valueString">Setting value</param>
|
||||
public Setting(string name, string valueString)
|
||||
{
|
||||
Name = name;
|
||||
ValueString = valueString;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a setting's value with name and value of setting</summary>
|
||||
/// <param name="name">Setting name</param>
|
||||
/// <param name="valueString">Setting value</param>
|
||||
public void Set(string name, string valueString)
|
||||
{
|
||||
Name = name;
|
||||
ValueString = valueString;
|
||||
if (PropertyDescriptor != null)
|
||||
{
|
||||
SetValue();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a setting's value with PropertyDescriptor</summary>
|
||||
/// <param name="descriptor">PropertyDescriptor describing setting</param>
|
||||
public void Set(PropertyDescriptor descriptor)
|
||||
{
|
||||
PropertyDescriptor = descriptor;
|
||||
if (Name != null && ValueString != null)
|
||||
{
|
||||
SetValue();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetValue()
|
||||
{
|
||||
if (!CanMakeChanges)
|
||||
return;
|
||||
|
||||
object value = GetValue(PropertyDescriptor.PropertyType, ValueString);
|
||||
PropertyDescriptor.SetValue(null, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setting name</summary>
|
||||
public string Name;
|
||||
/// <summary>
|
||||
/// Setting value</summary>
|
||||
public string ValueString;
|
||||
/// <summary>
|
||||
/// Setting PropertyDescriptor</summary>
|
||||
public PropertyDescriptor PropertyDescriptor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set whether property descriptors are allowed to make changes</summary>
|
||||
/// <remarks>Used when persisted settings are being loaded from disk
|
||||
/// to prevent property descriptors from being set multiple times as
|
||||
/// in the case where DefaultSettings.xml and AppSettings.xml both exist</remarks>
|
||||
public static bool CanMakeChanges { private get; set; }
|
||||
}
|
||||
private static object GetValue(Type type, string valueString)
|
||||
{
|
||||
object value = null;
|
||||
try
|
||||
{
|
||||
TypeConverter converter = TypeDescriptor.GetConverter(type);
|
||||
if (CanConvertToAndFromString(converter))
|
||||
{
|
||||
value = converter.ConvertFromInvariantString(valueString);
|
||||
}
|
||||
else
|
||||
{
|
||||
// deserialize
|
||||
byte[] data = Convert.FromBase64String(valueString);
|
||||
using (MemoryStream stream = new MemoryStream(data))
|
||||
{
|
||||
BinaryFormatter formatter = new BinaryFormatter();
|
||||
value = formatter.Deserialize(stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
value = null;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
private static bool CanConvertToAndFromString(TypeConverter converter)
|
||||
{
|
||||
return converter.CanConvertFrom(typeof(string)) &&
|
||||
converter.CanConvertTo(typeof(string));
|
||||
}
|
||||
/// <summary>
|
||||
/// Class for group of user settings</summary>
|
||||
public class UserSettingsInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor with name and PropertyDescriptors</summary>
|
||||
/// <param name="name">Name of group of user settings</param>
|
||||
/// <param name="settings">PropertyDescriptors with settings</param>
|
||||
public UserSettingsInfo(string name, PropertyDescriptor[] settings)
|
||||
{
|
||||
Name = name;
|
||||
Settings = new List<PropertyDescriptor>(settings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Name of group of user settings</summary>
|
||||
public readonly string Name;
|
||||
/// <summary>
|
||||
/// PropertyDescriptors with settings</summary>
|
||||
public readonly List<PropertyDescriptor> Settings;
|
||||
}
|
||||
private readonly SortedDictionary<string, SettingsInfo> _settings = new SortedDictionary<string, SettingsInfo>();
|
||||
private ITreeView _userSettings = new TreeView(string.Empty);
|
||||
|
||||
public void RegisterSettings(string uid, params PropertyDescriptor[] settings)
|
||||
{
|
||||
SettingsInfo settingsInfo;
|
||||
if (!_settings.TryGetValue(uid, out settingsInfo))
|
||||
{
|
||||
settingsInfo = new SettingsInfo(uid);
|
||||
_settings.Add(uid, settingsInfo);
|
||||
}
|
||||
|
||||
foreach (PropertyDescriptor descriptor in settings)
|
||||
settingsInfo.Add(descriptor);
|
||||
}
|
||||
public ITreeView UserSettings
|
||||
{
|
||||
get { return _userSettings; }
|
||||
}
|
||||
|
||||
public void RegisterSimple(object owner,Expression<Func<object>> expression,string pathName,string name,string category="General",string description="")
|
||||
{
|
||||
var descriptor = new BoundPropertyDescriptor(owner,expression, name, category,
|
||||
description);
|
||||
|
||||
RegisterSettings(pathName,descriptor);
|
||||
|
||||
RegisterUserSettings(pathName,descriptor);
|
||||
}
|
||||
|
||||
public void RegisterUserSettings(string pathName, params PropertyDescriptor[] settings)
|
||||
{
|
||||
if (string.IsNullOrEmpty(pathName))
|
||||
throw new ArgumentException("pathName");
|
||||
|
||||
string[] path = pathName.Split('/', 16);
|
||||
|
||||
// get root folder
|
||||
Tree<object> folder = UserSettings as Tree<object>;
|
||||
if (folder == null)
|
||||
throw new InvalidOperationException("userSettings");
|
||||
|
||||
// for each subsequent segment of the path, get folder
|
||||
for (int i = 0; i < path.Length - 1; i++)
|
||||
folder = GetOrCreateFolder(path[i], folder);
|
||||
|
||||
// get the node that should hold the settings, if it already exists
|
||||
string name = path[path.Length - 1];
|
||||
UserSettingsInfo existing = null;
|
||||
int index = 0;
|
||||
foreach (Tree<object> node in folder.Children)
|
||||
{
|
||||
UserSettingsInfo info = node.Value as UserSettingsInfo;
|
||||
if (info != null)
|
||||
{
|
||||
if (info.Name == name)
|
||||
{
|
||||
existing = info;
|
||||
break;
|
||||
}
|
||||
if (info.Name.CompareTo(name) < 0)
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
// add the settings, either by merging with the existing node or creating a new one
|
||||
if (existing != null)
|
||||
{
|
||||
foreach (PropertyDescriptor pd in settings)
|
||||
existing.Settings.Add(pd);
|
||||
}
|
||||
else
|
||||
{
|
||||
Tree<object> node = new Tree<object>(new UserSettingsInfo(name, settings));
|
||||
folder.Children.Insert(index, node);
|
||||
}
|
||||
}
|
||||
private static string GetMutexName(string pathName)
|
||||
{
|
||||
string safeName = pathName;
|
||||
|
||||
//255 characters will break IpcChannel constructor. 250 works.
|
||||
if (safeName.Length > 250)
|
||||
safeName = safeName.Substring(safeName.Length - 250);
|
||||
|
||||
// The Mutex constructor will crash if given '\', unless it's part of a valid path.
|
||||
// NextInstanceMonitor.ActivateApplication() will crash if there are '/' characters.
|
||||
safeName = safeName.Replace('/', '-');
|
||||
safeName = safeName.Replace('\\', '-');
|
||||
|
||||
return safeName;
|
||||
}
|
||||
protected void Serialize(Stream stream)
|
||||
{
|
||||
Saving.Raise(this, EventArgs.Empty);
|
||||
|
||||
XmlDocument xmlDoc = new XmlDocument();
|
||||
xmlDoc.AppendChild(xmlDoc.CreateXmlDeclaration("1.0", "utf-8", "yes"));
|
||||
XmlElement root = xmlDoc.CreateElement("settings");
|
||||
xmlDoc.AppendChild(root);
|
||||
|
||||
// add application name and version to the root element.
|
||||
root.SetAttribute("appName", _applicationName);
|
||||
root.SetAttribute("appVersion", _versionString);
|
||||
|
||||
foreach (SettingsInfo info in _settings.Values)
|
||||
{
|
||||
XmlElement block = xmlDoc.CreateElement("block");
|
||||
block.SetAttribute("id", info.Name);
|
||||
|
||||
foreach (SettingsInfo.Setting setting in info.Settings.Values)
|
||||
{
|
||||
PropertyDescriptor descriptor = setting.PropertyDescriptor;
|
||||
if (descriptor != null)
|
||||
{
|
||||
object value = descriptor.GetValue(null);
|
||||
if (CanWriteValue(value))
|
||||
WriteValue(descriptor.Name, value, block);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteValue(setting.Name, setting.ValueString, block);
|
||||
}
|
||||
}
|
||||
// skip empty block
|
||||
if (block.ChildNodes.Count > 0)
|
||||
root.AppendChild(block);
|
||||
}
|
||||
|
||||
XmlWriterSettings settings = new XmlWriterSettings();
|
||||
settings.CloseOutput = false;
|
||||
settings.Indent = true;
|
||||
|
||||
using (XmlWriter writer = XmlWriter.Create(stream, settings))
|
||||
{
|
||||
xmlDoc.WriteTo(writer);
|
||||
}
|
||||
}
|
||||
private bool CanWriteValue(object value)
|
||||
{
|
||||
if (value == null)
|
||||
return false;
|
||||
|
||||
TypeConverter converter = TypeDescriptor.GetConverter(value.GetType());
|
||||
return CanConvertToAndFromString(converter) || value.GetType().IsSerializable;
|
||||
}
|
||||
private void WriteValue(string name, object value, XmlElement block)
|
||||
{
|
||||
if (value == null)
|
||||
return;
|
||||
|
||||
// skip persisting if any exception occurs
|
||||
string valueString = null;
|
||||
Type type = value.GetType();
|
||||
TypeConverter converter = TypeDescriptor.GetConverter(type);
|
||||
if (CanConvertToAndFromString(converter))
|
||||
{
|
||||
valueString = converter.ConvertToInvariantString(value);
|
||||
}
|
||||
else if (type.IsSerializable)
|
||||
{
|
||||
// serialize
|
||||
BinaryFormatter formatter = new BinaryFormatter();
|
||||
using (MemoryStream stream = new MemoryStream())
|
||||
{
|
||||
formatter.Serialize(stream, value);
|
||||
valueString = Convert.ToBase64String(stream.GetBuffer());
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(valueString))
|
||||
return;
|
||||
|
||||
XmlDocument xmlDoc = block.OwnerDocument;
|
||||
XmlElement elmValue = xmlDoc.CreateElement("value");
|
||||
elmValue.SetAttribute("name", name);
|
||||
elmValue.SetAttribute("type", type.Name);
|
||||
|
||||
// if the valueString is xmlDoc then
|
||||
XmlDocument temp = StringToXmlDoc(valueString);
|
||||
if (temp != null)
|
||||
{
|
||||
// remove xml declaration if exists
|
||||
XmlDeclaration decl = temp.FirstChild as XmlDeclaration;
|
||||
if (decl != null)
|
||||
temp.RemoveChild(decl);
|
||||
elmValue.InnerXml = temp.DocumentElement.OuterXml;
|
||||
}
|
||||
else
|
||||
{
|
||||
elmValue.InnerText = valueString;
|
||||
}
|
||||
|
||||
block.AppendChild(elmValue);
|
||||
}
|
||||
private XmlDocument StringToXmlDoc(string strXml)
|
||||
{
|
||||
XmlDocument xmlDoc = null;
|
||||
try
|
||||
{
|
||||
int len = (strXml.Length > 20) ? 20 : strXml.Length;
|
||||
string test = RemoveAllWhiteSpace(strXml.Substring(0, len)).ToLower();
|
||||
if (test.Contains("<?xmlversion="))
|
||||
{
|
||||
xmlDoc = new XmlDocument();
|
||||
xmlDoc.LoadXml(strXml);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
xmlDoc = null;
|
||||
}
|
||||
return xmlDoc;
|
||||
|
||||
}
|
||||
public static string RemoveAllWhiteSpace(string s)
|
||||
{
|
||||
if (string.IsNullOrEmpty(s))
|
||||
return s;
|
||||
StringBuilder result = new StringBuilder(s.Length);
|
||||
for (int i = 0; i < s.Length; i++)
|
||||
{
|
||||
char c = s[i];
|
||||
if (!char.IsWhiteSpace(c))
|
||||
result.Append(c);
|
||||
}
|
||||
return result.ToString();
|
||||
}
|
||||
public void SaveSettings()
|
||||
{
|
||||
string tempNew = string.Empty;
|
||||
|
||||
string mutexName = GetMutexName(_settingsPath);
|
||||
using (Mutex saveMutex = new Mutex(false, mutexName))
|
||||
{
|
||||
try
|
||||
{
|
||||
saveMutex.WaitOne();
|
||||
|
||||
// Create zero-size file.
|
||||
tempNew = Path.GetTempFileName();
|
||||
|
||||
using (Stream stream = File.Create(tempNew))
|
||||
Serialize(stream);
|
||||
|
||||
// Make sure the settings directory exists. Do nothing if it already exists.
|
||||
string settingsDir = Path.GetDirectoryName(_settingsPath);
|
||||
Directory.CreateDirectory(settingsDir);
|
||||
|
||||
// Erase old backup (if any) and move current settings file (if any).
|
||||
string tempBackup = Path.Combine(settingsDir, "~Settings.xml");
|
||||
if (File.Exists(tempBackup)) // seems unnecessary, but Dan put this check in the WPF version --Ron
|
||||
File.Delete(tempBackup);
|
||||
if (File.Exists(_settingsPath))
|
||||
File.Move(_settingsPath, tempBackup);
|
||||
|
||||
// Move temporary file to be the new settings file, then delete backup.
|
||||
File.Move(tempNew, _settingsPath);
|
||||
File.Delete(tempBackup);
|
||||
}
|
||||
catch (TargetInvocationException)
|
||||
{
|
||||
// Catch and ignore TargetInvocationException happening if Windows
|
||||
// is shut down with the application still running.
|
||||
// TO DO: Find a way to successfully save settings and exit on shutdown.
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Attempt clean-up. No exception is thrown if file doesn't exist.
|
||||
File.Delete(tempNew);
|
||||
saveMutex.ReleaseMutex();
|
||||
}
|
||||
}
|
||||
}
|
||||
private Tree<object> GetOrCreateFolder(string name, Tree<object> tree)
|
||||
{
|
||||
// search for folder
|
||||
Tree<object> result = null;
|
||||
int index = 0;
|
||||
foreach (Tree<object> child in tree.Children)
|
||||
{
|
||||
string folderName = child.Value as string;
|
||||
if (folderName != null)
|
||||
{
|
||||
if (folderName == name)
|
||||
{
|
||||
result = child;
|
||||
break;
|
||||
}
|
||||
|
||||
if (folderName.CompareTo(name) < 0)
|
||||
index++;
|
||||
}
|
||||
else // child is UserSettingsInfo
|
||||
{
|
||||
index++; // folders should follow settings nodes
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// if not found, create it
|
||||
if (result == null)
|
||||
{
|
||||
result = new Tree<object>(name);
|
||||
tree.Children.Insert(index, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public event EventHandler Saving;
|
||||
public event EventHandler Loading;
|
||||
public event EventHandler Reloaded;
|
||||
|
||||
|
||||
private class TreeView : Tree<object>, ITreeView
|
||||
{
|
||||
public TreeView(object root)
|
||||
: base(root)
|
||||
{
|
||||
}
|
||||
|
||||
#region ITreeView Members
|
||||
|
||||
public object Root
|
||||
{
|
||||
get { return this; }
|
||||
}
|
||||
|
||||
public IEnumerable<object> GetChildren(object parent)
|
||||
{
|
||||
foreach (object child in ((Tree<object>)parent).Children)
|
||||
yield return child;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IItemView Members
|
||||
|
||||
/// <summary>
|
||||
/// Gets item's display information</summary>
|
||||
/// <param name="item">Item being displayed</param>
|
||||
/// <param name="info">Item info, to fill out</param>
|
||||
public void GetInfo(object item, ItemInfo info)
|
||||
{
|
||||
object value = ((Tree<object>)item).Value;
|
||||
if (value is string)
|
||||
{
|
||||
info.Label = (string)value;
|
||||
//info.ImageIndex = info.GetImageList().Images.IndexOfKey(Resources.FolderImage);
|
||||
info.AllowSelect = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
UserSettingsInfo settingsInfo = value as UserSettingsInfo;
|
||||
info.Label = settingsInfo.Name;
|
||||
info.AllowLabelEdit = false;
|
||||
//info.ImageIndex = info.GetImageList().Images.IndexOfKey(Resources.PreferencesImage);
|
||||
info.IsLeaf = true;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
|
||||
private string _settingsPath;
|
||||
private string _defaultSettingsPath;
|
||||
private string _applicationName;
|
||||
private string _versionString;
|
||||
private string _propertyViewState;
|
||||
|
||||
|
||||
public void LoadSettings()
|
||||
{
|
||||
Loading.Raise(this, EventArgs.Empty);
|
||||
|
||||
try
|
||||
{
|
||||
bool defaultSettingsExists = File.Exists(_defaultSettingsPath);
|
||||
bool appSettingsExists = File.Exists(_settingsPath);
|
||||
|
||||
// only update property descriptors during the DefaultSettings.xml pass
|
||||
// if DefaultSettings.xml exists and AppSettings.xml does not exist
|
||||
SettingsInfo.CanMakeChanges = defaultSettingsExists && !appSettingsExists;
|
||||
|
||||
// first, load default settings, if they exist:
|
||||
if (defaultSettingsExists)
|
||||
{
|
||||
using (Stream stream = File.OpenRead(_defaultSettingsPath))
|
||||
Deserialize(stream);
|
||||
}
|
||||
|
||||
// restore for AppSettings.xml pass
|
||||
SettingsInfo.CanMakeChanges = true;
|
||||
|
||||
// now load user settings, overriding defaults:
|
||||
if (appSettingsExists)
|
||||
{
|
||||
using (Stream stream = File.OpenRead(_settingsPath))
|
||||
Deserialize(stream);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
// restore value
|
||||
SettingsInfo.CanMakeChanges = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected void ApplyStoredSettings()
|
||||
{
|
||||
foreach (SettingsInfo info in _settings.Values)
|
||||
{
|
||||
foreach (SettingsInfo.Setting setting in info.Settings.Values)
|
||||
{
|
||||
if (setting.PropertyDescriptor is BoundPropertyDescriptor pd)
|
||||
{
|
||||
if (setting.ValueString == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
object v;
|
||||
if (setting.PropertyDescriptor.PropertyType.IsEnum)
|
||||
{
|
||||
v = Enum.Parse(setting.PropertyDescriptor.PropertyType, setting.ValueString);
|
||||
}
|
||||
else
|
||||
{
|
||||
v = Convert.ChangeType(setting.ValueString, setting.PropertyDescriptor.PropertyType);
|
||||
}
|
||||
pd.SetValue(pd.Owner, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected bool Deserialize(Stream stream)
|
||||
{
|
||||
// create XML DOM from stream
|
||||
// if failed, display message box and return
|
||||
try
|
||||
{
|
||||
XmlDocument xmlDoc = new XmlDocument();
|
||||
xmlDoc.Load(stream);
|
||||
|
||||
XmlElement root = xmlDoc.DocumentElement;
|
||||
|
||||
// get all the blocks
|
||||
XmlNodeList blocks = root.SelectNodes("block");
|
||||
if (blocks == null || blocks.Count == 0)
|
||||
throw new Exception("The setting file is empty");
|
||||
|
||||
foreach (XmlElement block in blocks)
|
||||
{
|
||||
try
|
||||
{
|
||||
string id = block.GetAttribute("id");
|
||||
|
||||
SettingsInfo info;
|
||||
if (!_settings.TryGetValue(id, out info))
|
||||
{
|
||||
info = new SettingsInfo(id);
|
||||
_settings.Add(id, info);
|
||||
}
|
||||
|
||||
// get a list of value element in each block
|
||||
XmlNodeList valueNodes = block.SelectNodes("value");
|
||||
|
||||
// skip over empty block
|
||||
if (valueNodes == null || valueNodes.Count == 0)
|
||||
continue;
|
||||
|
||||
foreach (XmlElement xmlElement in valueNodes)
|
||||
{
|
||||
string name = xmlElement.GetAttribute("name");
|
||||
string valueString = GetElementValueString(xmlElement);
|
||||
|
||||
info.Add(name, valueString);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
ApplyStoredSettings();
|
||||
Reloaded.Raise(this, EventArgs.Empty);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
private static string GetElementValueString(XmlElement element)
|
||||
{
|
||||
string valueString = element.InnerText;
|
||||
if (string.IsNullOrEmpty(valueString))
|
||||
valueString = element.InnerXml;
|
||||
|
||||
return valueString;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
148
framework/Inspectron.Settings/ItemInfo.cs
Normal file
148
framework/Inspectron.Settings/ItemInfo.cs
Normal file
@@ -0,0 +1,148 @@
|
||||
namespace Inspectron.Settings
|
||||
{
|
||||
public abstract class ItemInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor for items with no associated images to be drawn</summary>
|
||||
public ItemInfo()
|
||||
{
|
||||
CheckBoxEnabled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets item's label</summary>
|
||||
/// <remarks>Default is empty string if item has no label</remarks>
|
||||
public string Label
|
||||
{
|
||||
get { return m_label; }
|
||||
set { m_label = value; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets item's description</summary>
|
||||
/// <remarks>Default is empty string if item has no description</remarks>
|
||||
public string Description
|
||||
{
|
||||
get { return m_description; }
|
||||
set { m_description = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether item should have a check box control</summary>
|
||||
/// <remarks>Default is false</remarks>
|
||||
public bool HasCheck
|
||||
{
|
||||
get { return m_hasCheck; }
|
||||
set { m_hasCheck = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether check box is enabled</summary>
|
||||
/// <remarks>Default is true.
|
||||
/// This property makes sense only if HasCheck is true</remarks>
|
||||
public bool CheckBoxEnabled
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether item is checked</summary>
|
||||
/// <remarks>Default is false</remarks>
|
||||
public abstract bool Checked
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether item is a leaf (has no sub-items)</summary>
|
||||
/// <remarks>Used by tree controls to inhibit drawing the node expander; default
|
||||
/// is false</remarks>
|
||||
public bool IsLeaf
|
||||
{
|
||||
get { return m_isLeaf; }
|
||||
set { m_isLeaf = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the label is editable</summary>
|
||||
/// <remarks>Used by tree controls to inhibit editing the node label; default
|
||||
/// is true</remarks>
|
||||
public bool AllowLabelEdit
|
||||
{
|
||||
get { return m_allowLabelEdit; }
|
||||
set { m_allowLabelEdit = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the item is selectable</summary>
|
||||
/// <remarks>Used by tree controls to inhibit selecting the node; default
|
||||
/// is true</remarks>
|
||||
public bool AllowSelect
|
||||
{
|
||||
get { return m_allowSelect; }
|
||||
set { m_allowSelect = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether this item is expanded in the view. Set by
|
||||
/// Control adapters - client code shouldn't set this value.</summary>
|
||||
public bool IsExpandedInView
|
||||
{
|
||||
get { return m_isExpandedInView; }
|
||||
set { m_isExpandedInView = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets index of item's image in image list</summary>
|
||||
/// <remarks>Default is -1 if item has no image.
|
||||
/// DAN: This is not required by WPF so could be moved to WinFormsItemInfo</remarks>
|
||||
public int ImageIndex
|
||||
{
|
||||
get { return m_imageIndex; }
|
||||
set { m_imageIndex = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets index of item's "State" image in image list</summary>
|
||||
/// <remarks>Default is -1 if item has no "State" image.
|
||||
/// DAN: This is not required by WPF so could be moved to WinFormsItemInfo</remarks>
|
||||
public int StateImageIndex
|
||||
{
|
||||
get { return m_stateImageIndex; }
|
||||
set { m_stateImageIndex = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets item's properties for lists and tree lists</summary>
|
||||
/// <remarks>Default is an empty array if item has no properties</remarks>
|
||||
public object[] Properties
|
||||
{
|
||||
get { return m_properties; }
|
||||
set { m_properties = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets item's mouse hover over text</summary>
|
||||
public string HoverText
|
||||
{
|
||||
get { return m_hoverText; }
|
||||
set { m_hoverText = value; }
|
||||
}
|
||||
|
||||
private string m_label = string.Empty;
|
||||
private string m_description = string.Empty;
|
||||
private int m_imageIndex = -1;
|
||||
private int m_stateImageIndex = -1;
|
||||
private object[] m_properties = new object[0];
|
||||
private bool m_hasCheck;
|
||||
private bool m_isLeaf;
|
||||
private bool m_allowLabelEdit = true;
|
||||
private bool m_allowSelect = true;
|
||||
private bool m_isExpandedInView;
|
||||
private string m_hoverText = string.Empty;
|
||||
}
|
||||
}
|
||||
405
framework/Inspectron.Settings/Path.cs
Normal file
405
framework/Inspectron.Settings/Path.cs
Normal file
@@ -0,0 +1,405 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Inspectron.Settings
|
||||
{
|
||||
public class Path<T> : IList<T>, IEquatable<Path<T>>
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor using single object</summary>
|
||||
/// <param name="last">Single object making up the path</param>
|
||||
public Path(T last)
|
||||
{
|
||||
m_path = new T[1];
|
||||
m_path[0] = last;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor using sequence of objects</summary>
|
||||
/// <param name="path">Path, as sequence of objects</param>
|
||||
public Path(IEnumerable<T> path)
|
||||
{
|
||||
m_path = path.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor using collection of objects</summary>
|
||||
/// <param name="path">Path, as collection of objects</param>
|
||||
public Path(ICollection<T> path)
|
||||
{
|
||||
m_path = new T[path.Count];
|
||||
path.CopyTo(m_path, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the first object in the path</summary>
|
||||
public T First
|
||||
{
|
||||
get { return m_path[0]; }
|
||||
set { m_path[0] = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the last object in the path</summary>
|
||||
public T Last
|
||||
{
|
||||
get { return m_path[m_path.Length - 1]; }
|
||||
set { m_path[m_path.Length - 1] = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Obtains a prefix with the specified length</summary>
|
||||
/// <param name="length">Prefix length</param>
|
||||
/// <returns>Prefix with the specified length</returns>
|
||||
public Path<T> Prefix(int length)
|
||||
{
|
||||
CheckSubPathLength(length);
|
||||
|
||||
T[] path = new T[length];
|
||||
Array.Copy(m_path, 0, path, 0, length);
|
||||
|
||||
return new Path<T>(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Obtains a suffix with the specified length</summary>
|
||||
/// <param name="length">Suffix length</param>
|
||||
/// <returns>Suffix with the specified length</returns>
|
||||
public Path<T> Suffix(int length)
|
||||
{
|
||||
CheckSubPathLength(length);
|
||||
|
||||
T[] path = new T[length];
|
||||
int offset = m_path.Length - length;
|
||||
Array.Copy(m_path, offset, path, 0, length);
|
||||
|
||||
return new Path<T>(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts path to a path of another type</summary>
|
||||
/// <typeparam name="U">Path type to convert to</typeparam>
|
||||
/// <returns>Path of new type</returns>
|
||||
public Path<U> Convert<U>()
|
||||
where U : class
|
||||
{
|
||||
U[] converted = new U[m_path.Length];
|
||||
for (int i = 0; i < m_path.Length; i++)
|
||||
converted[i] = Convert<U>(m_path[i]);
|
||||
return new Path<U>(converted);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts from the path type to another type</summary>
|
||||
/// <typeparam name="U">Desired type</typeparam>
|
||||
/// <param name="item">Item to convert</param>
|
||||
/// <returns>Item, converted to given type, or null</returns>
|
||||
protected virtual U Convert<U>(T item)
|
||||
where U : class
|
||||
{
|
||||
U u = item as U;
|
||||
return u;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests path for equality</summary>
|
||||
/// <param name="other">Other path</param>
|
||||
/// <returns>True iff this path is equivalent to other</returns>
|
||||
public bool Equals(Path<T> other)
|
||||
{
|
||||
if (object.Equals(other, null))
|
||||
return false;
|
||||
|
||||
if (m_path.Length != other.m_path.Length)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < m_path.Length; i++)
|
||||
if (!m_path[i].Equals(other.m_path[i]))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests object for equality</summary>
|
||||
/// <param name="obj">Other object</param>
|
||||
/// <returns>True iff this path is equivalent to other object</returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
Path<T> path = obj as Path<T>;
|
||||
if (path != null)
|
||||
return Equals(path);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Obtains hash code</summary>
|
||||
/// <returns>Hash code</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
int hash = 0;
|
||||
foreach (T obj in m_path)
|
||||
hash ^= obj.GetHashCode();
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests paths for equality</summary>
|
||||
/// <param name="o1">First path</param>
|
||||
/// <param name="o2">Second path</param>
|
||||
/// <returns>True iff paths are equivalent</returns>
|
||||
public static bool operator ==(Path<T> o1, Path<T> o2)
|
||||
{
|
||||
if (object.Equals(o1, null))
|
||||
return object.Equals(o2, null);
|
||||
else
|
||||
return o1.Equals(o2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests paths for inequality</summary>
|
||||
/// <param name="o1">First path</param>
|
||||
/// <param name="o2">Second path</param>
|
||||
/// <returns>True iff paths are not equivalent</returns>
|
||||
public static bool operator !=(Path<T> o1, Path<T> o2)
|
||||
{
|
||||
if (object.Equals(o1, null))
|
||||
return !object.Equals(o2, null);
|
||||
else
|
||||
return !o1.Equals(o2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Concatenates object with path</summary>
|
||||
/// <param name="lhs">Prefix object</param>
|
||||
/// <param name="rhs">Optional path, may be null</param>
|
||||
/// <returns>Concatenated path, with lhs as first object</returns>
|
||||
public static Path<T> operator +(T lhs, Path<T> rhs)
|
||||
{
|
||||
if (rhs == null)
|
||||
return new Path<T>(lhs);
|
||||
T[] path = new T[1 + rhs.Count];
|
||||
path[0] = lhs;
|
||||
Array.Copy(rhs.m_path, 0, path, 1, rhs.Count);
|
||||
return new Path<T>(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Concatenates path with object</summary>
|
||||
/// <param name="lhs">Optional path, may be null</param>
|
||||
/// <param name="rhs">Suffix object</param>
|
||||
/// <returns>Concatenated path, with rhs as last object</returns>
|
||||
public static Path<T> operator +(Path<T> lhs, T rhs)
|
||||
{
|
||||
if (lhs == null)
|
||||
return new Path<T>(rhs);
|
||||
T[] path = new T[lhs.Count + 1];
|
||||
Array.Copy(lhs.m_path, 0, path, 0, lhs.Count);
|
||||
path[path.Length - 1] = rhs;
|
||||
return new Path<T>(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Concatenates two paths</summary>
|
||||
/// <param name="lhs">First path. Can be null.</param>
|
||||
/// <param name="rhs">Second path. Can be null.</param>
|
||||
/// <returns>Concatenated path, with rhs as prefix and lhs as suffix. Is null if both lhs and rhs are null.</returns>
|
||||
public static Path<T> operator +(Path<T> lhs, Path<T> rhs)
|
||||
{
|
||||
if (lhs == null)
|
||||
return rhs;
|
||||
if (rhs == null)
|
||||
return lhs;
|
||||
T[] path = new T[lhs.Count + rhs.Count];
|
||||
Array.Copy(lhs.m_path, 0, path, 0, lhs.Count);
|
||||
Array.Copy(rhs.m_path, 0, path, lhs.Count, rhs.Count);
|
||||
return new Path<T>(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the enumeration of each path's last item; i.e., the property 'Last'</summary>
|
||||
/// <param name="paths">Enumeration of Path objects, whose Last property is returned</param>
|
||||
/// <returns>Last property of each Path in 'paths', in the same order as 'paths'</returns>
|
||||
public static IEnumerable<T> GetLastItems(IEnumerable<Path<T>> paths)
|
||||
{
|
||||
foreach (Path<T> path in paths)
|
||||
yield return path.Last;
|
||||
}
|
||||
|
||||
#region IList<T> Members
|
||||
|
||||
/// <summary>
|
||||
/// Determines the index of a specific item in the <see cref="T:System.Collections.Generic.IList`1"></see></summary>
|
||||
/// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.IList`1"></see></param>
|
||||
/// <returns>
|
||||
/// The index of item if found in the list; otherwise -1
|
||||
/// </returns>
|
||||
public int IndexOf(T item)
|
||||
{
|
||||
for (int i = 0; i < m_path.Length; i++)
|
||||
if (m_path[i].Equals(item))
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts an item to the <see cref="T:System.Collections.Generic.IList`1"></see> at the specified index</summary>
|
||||
/// <param name="index">The zero-based index at which item should be inserted</param>
|
||||
/// <param name="item">The object to insert into the <see cref="T:System.Collections.Generic.IList`1"></see></param>
|
||||
/// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.IList`1"></see> is read-only</exception>
|
||||
/// <exception cref="T:System.ArgumentOutOfRangeException">index is not a valid index in the <see cref="T:System.Collections.Generic.IList`1"></see></exception>
|
||||
public void Insert(int index, T item)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the <see cref="T:System.Collections.Generic.IList`1"></see> item at the specified index</summary>
|
||||
/// <param name="index">The zero-based index of the item to remove</param>
|
||||
/// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.IList`1"></see> is read-only</exception>
|
||||
/// <exception cref="T:System.ArgumentOutOfRangeException">index is not a valid index in the <see cref="T:System.Collections.Generic.IList`1"></see></exception>
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the item at the specified index</summary>
|
||||
/// <value>Index at which to set value</value>
|
||||
public T this[int index]
|
||||
{
|
||||
get { return m_path[index]; }
|
||||
set { m_path[index] = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ICollection<T> Members
|
||||
|
||||
/// <summary>
|
||||
/// Adds an item to the <see cref="T:System.Collections.Generic.ICollection`1"></see></summary>
|
||||
/// <param name="item">The object to add to the <see cref="T:System.Collections.Generic.ICollection`1"></see></param>
|
||||
/// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"></see>
|
||||
/// is read-only</exception>
|
||||
public void Add(T item)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all items from the <see cref="T:System.Collections.Generic.ICollection`1"></see></summary>
|
||||
/// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"></see>
|
||||
/// is read-only</exception>
|
||||
public void Clear()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the <see cref="T:System.Collections.Generic.ICollection`1"></see> contains a specific value</summary>
|
||||
/// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.ICollection`1"></see></param>
|
||||
/// <returns>
|
||||
/// True iff item is found in the <see cref="T:System.Collections.Generic.ICollection`1"></see>
|
||||
/// </returns>
|
||||
public bool Contains(T item)
|
||||
{
|
||||
foreach (T obj in m_path)
|
||||
if (obj.Equals(item))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the elements of the <see cref="T:System.Collections.Generic.ICollection`1"></see> to an <see cref="T:System.Array"></see>,
|
||||
/// starting at a particular <see cref="T:System.Array"></see> index</summary>
|
||||
/// <param name="array">The one-dimensional <see cref="T:System.Array"></see> that is the destination of the elements
|
||||
/// copied from <see cref="T:System.Collections.Generic.ICollection`1"></see>.
|
||||
/// The <see cref="T:System.Array"></see> must have zero-based indexing.</param>
|
||||
/// <param name="arrayIndex">The zero-based index in array at which copying begins</param>
|
||||
/// <exception cref="T:System.ArgumentOutOfRangeException">ArrayIndex is less than 0</exception>
|
||||
/// <exception cref="T:System.ArgumentNullException">Array is null</exception>
|
||||
/// <exception cref="T:System.ArgumentException">Array is multidimensional.-or-
|
||||
/// arrayIndex is equal to or greater than the length of array.-or-
|
||||
/// The number of elements in the source <see cref="T:System.Collections.Generic.ICollection`1"></see> is greater than
|
||||
/// the available space from arrayIndex to the end of the destination array.-or-
|
||||
/// Type T cannot be cast automatically to the type of the destination array.</exception>
|
||||
public void CopyTo(T[] array, int arrayIndex)
|
||||
{
|
||||
m_path.CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1"></see></summary>
|
||||
public int Count
|
||||
{
|
||||
get { return m_path.Length; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the <see cref="T:System.Collections.Generic.ICollection`1"></see> is read-only</summary>
|
||||
public bool IsReadOnly
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the first occurrence of a specific object from the <see cref="T:System.Collections.Generic.ICollection`1"></see></summary>
|
||||
/// <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1"></see></param>
|
||||
/// <returns>
|
||||
/// True iff item was successfully removed from the <see cref="T:System.Collections.Generic.ICollection`1"></see>.
|
||||
/// This method also returns false if item is not found in the original <see cref="T:System.Collections.Generic.ICollection`1"></see>.
|
||||
/// </returns>
|
||||
/// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"></see> is read-only</exception>
|
||||
public bool Remove(T item)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable<T> Members
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumerator that iterates through the collection</summary>
|
||||
/// <returns>
|
||||
/// A <see cref="T:System.Collections.Generic.IEnumerator`1"></see> that can be used to iterate through the collection
|
||||
/// </returns>
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable<T>)m_path).GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable Members
|
||||
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
||||
{
|
||||
return m_path.GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor</summary>
|
||||
private Path(T[] path)
|
||||
{
|
||||
m_path = path;
|
||||
}
|
||||
|
||||
private void CheckSubPathLength(int length)
|
||||
{
|
||||
if (length < 1)
|
||||
throw new InvalidOperationException("Length must be > 0");
|
||||
if (length > m_path.Length)
|
||||
throw new InvalidOperationException("Length greater than path length");
|
||||
}
|
||||
|
||||
private readonly T[] m_path;
|
||||
}
|
||||
}
|
||||
336
framework/Inspectron.Settings/Tree.cs
Normal file
336
framework/Inspectron.Settings/Tree.cs
Normal file
@@ -0,0 +1,336 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Text;
|
||||
|
||||
namespace Inspectron.Settings
|
||||
{
|
||||
[Serializable]
|
||||
public class Tree<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor of empty tree</summary>
|
||||
public Tree()
|
||||
: this(default(T))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for tree with one node</summary>
|
||||
/// <param name="value">Value associated with this tree</param>
|
||||
public Tree(T value)
|
||||
{
|
||||
m_value = value;
|
||||
m_children = new ChildCollection(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets tree's parent. Is null if this is a root node.</summary>
|
||||
public Tree<T> Parent
|
||||
{
|
||||
get { return m_parent; }
|
||||
set
|
||||
{
|
||||
if (m_parent != value)
|
||||
{
|
||||
if (m_parent != null)
|
||||
m_parent.Children.Remove(this);
|
||||
|
||||
m_parent = value;
|
||||
|
||||
if (m_parent != null)
|
||||
m_parent.Children.Add(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of children nodes. Is the same as 'this' because this Tree implements IList.</summary>
|
||||
public IList<Tree<T>> Children
|
||||
{
|
||||
get { return m_children; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value associated with the tree</summary>
|
||||
public T Value
|
||||
{
|
||||
get { return m_value; }
|
||||
set { m_value = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests for equality</summary>
|
||||
/// <param name="obj">Other object</param>
|
||||
/// <returns>True iff object is a tree with the same structure and values as this tree</returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
return false;
|
||||
|
||||
Tree<T> other = obj as Tree<T>;
|
||||
if (other == null)
|
||||
return false;
|
||||
|
||||
if (!m_value.Equals(other.m_value))
|
||||
return false;
|
||||
|
||||
if (m_children.Count != other.m_children.Count)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < m_children.Count; i++)
|
||||
if (!(m_children[i]).Equals(other.m_children[i]))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests for similarity</summary>
|
||||
/// <param name="other">Other tree</param>
|
||||
/// <returns>True iff other has the same structure as this tree</returns>
|
||||
/// <remarks>Same structure means same node structure</remarks>
|
||||
public bool Similar(Tree<T> other)
|
||||
{
|
||||
if (this == other)
|
||||
return true;
|
||||
|
||||
if (other == null)
|
||||
return false;
|
||||
|
||||
if (m_children.Count != other.m_children.Count)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < m_children.Count; i++)
|
||||
if (!(m_children[i]).Similar(other.m_children[i]))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns hash code for tree</summary>
|
||||
/// <returns>Hash code</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
int result = 0;
|
||||
foreach (Tree<T> tree in PreOrder)
|
||||
result ^= tree.Value.GetHashCode();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts tree to string of the form "(Value(Child1),...,(ChildN))"</summary>
|
||||
/// <returns>String representation of tree</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.Append('(');
|
||||
Stringify(builder);
|
||||
builder.Append(')');
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private void Stringify(StringBuilder builder)
|
||||
{
|
||||
builder.Append(m_value.ToString());
|
||||
if (!IsLeaf)
|
||||
{
|
||||
builder.Append('(');
|
||||
bool firstTime = true;
|
||||
foreach (Tree<T> t in m_children)
|
||||
{
|
||||
if (firstTime)
|
||||
firstTime = false;
|
||||
else
|
||||
builder.Append(',');
|
||||
t.Stringify(builder);
|
||||
}
|
||||
builder.Append(')');
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests if this tree is a descendant of another</summary>
|
||||
/// <param name="ancestor">Possible ancestor</param>
|
||||
/// <returns>True iff this tree is a descendant of the other</returns>
|
||||
/// <remarks>A tree is considered a descendant of itself</remarks>
|
||||
public bool IsDescendantOf(Tree<T> ancestor)
|
||||
{
|
||||
Tree<T> descendant = this;
|
||||
while (descendant != null)
|
||||
{
|
||||
if (ancestor == descendant)
|
||||
return true;
|
||||
descendant = descendant.Parent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether a tree is a leaf (no children)</summary>
|
||||
public bool IsLeaf
|
||||
{
|
||||
get { return m_children.Count == 0; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets level, or depth, in tree</summary>
|
||||
public int Level
|
||||
{
|
||||
get
|
||||
{
|
||||
int result = 0;
|
||||
Tree<T> ancestor = m_parent;
|
||||
while (ancestor != null)
|
||||
{
|
||||
result++;
|
||||
ancestor = ancestor.Parent;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets number of descendants, including the tree itself</summary>
|
||||
public int DescendantCount
|
||||
{
|
||||
get
|
||||
{
|
||||
int n = 0;
|
||||
foreach (Tree<T> tree in PreOrder)
|
||||
n++;
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enumeration of all the nodes of the tree in pre-order (depth first).
|
||||
/// For example, the root node is first, followed by its first child (and its
|
||||
/// children and so on) and then the second child of the root (and its children
|
||||
/// and so on) etc.</summary>
|
||||
public IEnumerable<Tree<T>> PreOrder
|
||||
{
|
||||
get
|
||||
{
|
||||
Stack<Tree<T>> nodes = new Stack<Tree<T>>();
|
||||
nodes.Push(this);
|
||||
while (nodes.Count > 0)
|
||||
{
|
||||
Tree<T> node = nodes.Pop();
|
||||
yield return node;
|
||||
|
||||
// push children in reverse order
|
||||
for (int i = node.m_children.Count - 1; i >= 0; i--)
|
||||
nodes.Push(node.m_children[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enumeration of all the nodes of the tree in post-order. This means that for
|
||||
/// each node, the children are visited first (starting with the first child) and then
|
||||
/// the parent node is enumerated.</summary>
|
||||
public IEnumerable<Tree<T>> PostOrder
|
||||
{
|
||||
get
|
||||
{
|
||||
// push each non-leaf node twice to represent nodes whose children haven't been visited
|
||||
// rather than storing a bit on each node.
|
||||
Stack<Tree<T>> nodes = new Stack<Tree<T>>();
|
||||
nodes.Push(this);
|
||||
if (!IsLeaf)
|
||||
nodes.Push(this);
|
||||
|
||||
while (nodes.Count > 1)
|
||||
{
|
||||
Tree<T> node = nodes.Pop();
|
||||
if (node != nodes.Peek())
|
||||
{
|
||||
yield return node;
|
||||
}
|
||||
else
|
||||
{
|
||||
// push children in reverse order
|
||||
for (int i = node.m_children.Count - 1; i >= 0; i--)
|
||||
{
|
||||
Tree<T> child = node.m_children[i];
|
||||
nodes.Push(child);
|
||||
if (!child.IsLeaf)
|
||||
nodes.Push(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
yield return nodes.Pop();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enumeration of all the nodes in a breadth-first order. This means that the
|
||||
/// root is enumerated first (level 0), followed by all of its children (level 1),
|
||||
/// followed by all of their children (level 2), and so on.</summary>
|
||||
public IEnumerable<Tree<T>> LevelOrder
|
||||
{
|
||||
get
|
||||
{
|
||||
Queue<Tree<T>> nodes = new Queue<Tree<T>>();
|
||||
nodes.Enqueue(this);
|
||||
while (nodes.Count > 0)
|
||||
{
|
||||
Tree<T> node = nodes.Dequeue();
|
||||
yield return node;
|
||||
|
||||
// queue children
|
||||
foreach (Tree<T> child in node.m_children)
|
||||
nodes.Enqueue(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ChildCollection : Collection<Tree<T>>
|
||||
{
|
||||
public ChildCollection(Tree<T> parent)
|
||||
{
|
||||
m_parent = parent;
|
||||
}
|
||||
|
||||
protected override void InsertItem(int index, Tree<T> item)
|
||||
{
|
||||
item.m_parent = m_parent;
|
||||
|
||||
base.InsertItem(index, item);
|
||||
}
|
||||
|
||||
protected override void RemoveItem(int index)
|
||||
{
|
||||
Items[index].m_parent = null;
|
||||
|
||||
base.RemoveItem(index);
|
||||
}
|
||||
|
||||
protected override void SetItem(int index, Tree<T> item)
|
||||
{
|
||||
Items[index].m_parent = null;
|
||||
item.m_parent = m_parent;
|
||||
|
||||
base.SetItem(index, item);
|
||||
}
|
||||
|
||||
protected override void ClearItems()
|
||||
{
|
||||
foreach (Tree<T> subTree in Items)
|
||||
subTree.m_parent = null;
|
||||
|
||||
base.ClearItems();
|
||||
}
|
||||
|
||||
private readonly Tree<T> m_parent;
|
||||
}
|
||||
|
||||
private T m_value;
|
||||
private Tree<T> m_parent;
|
||||
private readonly ChildCollection m_children;
|
||||
}
|
||||
}
|
||||
48
framework/Inspectron.Statistics/CsvStatistics.cs
Normal file
48
framework/Inspectron.Statistics/CsvStatistics.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using System.Globalization;
|
||||
using CsvHelper;
|
||||
|
||||
namespace Inspectron.Statistics;
|
||||
|
||||
public class CsvStatistics
|
||||
{
|
||||
private readonly string _filePath;
|
||||
private readonly StreamWriter _stream;
|
||||
private readonly FileStream _fileStream;
|
||||
private readonly CsvWriter _csv;
|
||||
|
||||
public CsvStatistics(string filePath, string[] headers)
|
||||
{
|
||||
_filePath = filePath;
|
||||
var newFile = !File.Exists(filePath);
|
||||
_fileStream = File.Open(_filePath, FileMode.Append);
|
||||
_stream = new StreamWriter(_fileStream);
|
||||
_csv = new CsvWriter(_stream,CultureInfo.InvariantCulture);
|
||||
if (newFile)
|
||||
{
|
||||
foreach (var header in headers)
|
||||
{
|
||||
_csv.WriteField(header);
|
||||
}
|
||||
_csv.NextRecord();
|
||||
_csv.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteLine(string[] values)
|
||||
{
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
_csv.WriteField(values[i]);
|
||||
}
|
||||
_csv.NextRecord();
|
||||
_csv.Flush();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_csv.Dispose();
|
||||
_stream.Dispose();
|
||||
_fileStream.Dispose();
|
||||
|
||||
}
|
||||
}
|
||||
6
framework/Inspectron.Statistics/IStatistics.cs
Normal file
6
framework/Inspectron.Statistics/IStatistics.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Inspectron.Statistics;
|
||||
|
||||
public interface IStatistics:IDisposable
|
||||
{
|
||||
void WriteLine(StatisticsRecord record);
|
||||
}
|
||||
13
framework/Inspectron.Statistics/Inspectron.Statistics.csproj
Normal file
13
framework/Inspectron.Statistics/Inspectron.Statistics.csproj
Normal file
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CsvHelper" Version="27.2.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
92
framework/Inspectron.Statistics/LockedCsvStatistics.cs
Normal file
92
framework/Inspectron.Statistics/LockedCsvStatistics.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using System.Globalization;
|
||||
using CsvHelper;
|
||||
|
||||
namespace Inspectron.Statistics;
|
||||
|
||||
public class LockedCsvStatistics : IStatistics
|
||||
{
|
||||
private readonly string _filePath;
|
||||
private readonly string _lockFilePath;
|
||||
private readonly string[] _headers;
|
||||
|
||||
public LockedCsvStatistics(string filePath, string[] headers)
|
||||
{
|
||||
_filePath = filePath;
|
||||
_lockFilePath = filePath + ".lock";
|
||||
_headers = headers;
|
||||
|
||||
var directoryPath = Path.GetDirectoryName(filePath);
|
||||
if (directoryPath != null && !Directory.Exists(directoryPath))
|
||||
{
|
||||
Directory.CreateDirectory(directoryPath);
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteLine(StatisticsRecord record)
|
||||
{
|
||||
AcquireLock();
|
||||
|
||||
try
|
||||
{
|
||||
bool writeHeaders = !File.Exists(_filePath) || new FileInfo(_filePath).Length == 0;
|
||||
|
||||
using var fileStream = new FileStream(_filePath, FileMode.Append, FileAccess.Write, FileShare.None);
|
||||
using var streamWriter = new StreamWriter(fileStream);
|
||||
using var csvWriter = new CsvWriter(streamWriter, CultureInfo.InvariantCulture);
|
||||
|
||||
if (writeHeaders)
|
||||
{
|
||||
foreach (var header in _headers)
|
||||
{
|
||||
csvWriter.WriteField(header);
|
||||
}
|
||||
csvWriter.NextRecord();
|
||||
csvWriter.Flush();
|
||||
}
|
||||
|
||||
foreach (var value in record.Values)
|
||||
{
|
||||
csvWriter.WriteField(value);
|
||||
}
|
||||
csvWriter.NextRecord();
|
||||
csvWriter.Flush();
|
||||
}
|
||||
finally
|
||||
{
|
||||
ReleaseLock();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// No resources to dispose
|
||||
}
|
||||
|
||||
private void AcquireLock()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (File.Open(_lockFilePath, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None))
|
||||
{
|
||||
// Lock acquired
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
// Lock file already exists, wait and retry
|
||||
Thread.Sleep(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ReleaseLock()
|
||||
{
|
||||
if (File.Exists(_lockFilePath))
|
||||
{
|
||||
File.Delete(_lockFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
6
framework/Inspectron.Statistics/StatisticsRecord.cs
Normal file
6
framework/Inspectron.Statistics/StatisticsRecord.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Inspectron.Statistics;
|
||||
|
||||
public class StatisticsRecord
|
||||
{
|
||||
public string[] Values { get; set; }
|
||||
}
|
||||
64
framework/Inspectron.Statistics/StatisticsWritter.cs
Normal file
64
framework/Inspectron.Statistics/StatisticsWritter.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Inspectron.Statistics;
|
||||
|
||||
public class StatisticsWritter:IStatistics
|
||||
{
|
||||
private readonly Func<IStatistics> _statisticsBuilder;
|
||||
|
||||
private bool _running = true;
|
||||
public StatisticsWritter(Func<IStatistics> statisticsBuilder)
|
||||
{
|
||||
_statisticsBuilder = statisticsBuilder;
|
||||
Thread th = new Thread(WritingLoop);
|
||||
th.IsBackground = false;
|
||||
th.Start();
|
||||
}
|
||||
|
||||
private void WritingLoop()
|
||||
{
|
||||
while (_running)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var statistics = _statisticsBuilder())
|
||||
{
|
||||
while (_recordQueue.TryDequeue(out var res))
|
||||
{
|
||||
var error = false;
|
||||
do
|
||||
{
|
||||
try
|
||||
{
|
||||
statistics.WriteLine(res);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
error = true;
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
} while (error);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_running = false;
|
||||
}
|
||||
|
||||
private ConcurrentQueue<StatisticsRecord> _recordQueue = new ConcurrentQueue<StatisticsRecord>();
|
||||
public void WriteLine(StatisticsRecord record)
|
||||
{
|
||||
_recordQueue.Enqueue(record);
|
||||
}
|
||||
}
|
||||
12
framework/MaterialSkin.Core/Animations/AnimationDirection.cs
Normal file
12
framework/MaterialSkin.Core/Animations/AnimationDirection.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace MaterialSkin.Animations
|
||||
{
|
||||
enum AnimationDirection
|
||||
{
|
||||
In, //In. Stops if finished.
|
||||
Out, //Out. Stops if finished.
|
||||
InOutIn, //Same as In, but changes to InOutOut if finished.
|
||||
InOutOut, //Same as Out.
|
||||
InOutRepeatingIn, // Same as In, but changes to InOutRepeatingOut if finished.
|
||||
InOutRepeatingOut // Same as Out, but changes to InOutRepeatingIn if finished.
|
||||
}
|
||||
}
|
||||
363
framework/MaterialSkin.Core/Animations/AnimationManager.cs
Normal file
363
framework/MaterialSkin.Core/Animations/AnimationManager.cs
Normal file
@@ -0,0 +1,363 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace MaterialSkin.Animations
|
||||
{
|
||||
class AnimationManager
|
||||
{
|
||||
public bool InterruptAnimation { get; set; }
|
||||
public double Increment { get; set; }
|
||||
public double SecondaryIncrement { get; set; }
|
||||
public AnimationType AnimationType { get; set; }
|
||||
public bool Singular { get; set; }
|
||||
|
||||
public delegate void AnimationFinished(object sender);
|
||||
public event AnimationFinished OnAnimationFinished;
|
||||
|
||||
public delegate void AnimationProgress(object sender);
|
||||
public event AnimationProgress OnAnimationProgress;
|
||||
|
||||
private readonly List<double> _animationProgresses;
|
||||
private readonly List<Point> _animationSources;
|
||||
private readonly List<AnimationDirection> _animationDirections;
|
||||
private readonly List<object[]> _animationDatas;
|
||||
|
||||
private const double MIN_VALUE = 0.00;
|
||||
private const double MAX_VALUE = 1.00;
|
||||
|
||||
private readonly Timer _animationTimer = new Timer { Interval = 5, Enabled = false };
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="singular">If true, only one animation is supported. The current animation will be replaced with the new one. If false, a new animation is added to the list.</param>
|
||||
public AnimationManager(bool singular = true)
|
||||
{
|
||||
_animationProgresses = new List<double>();
|
||||
_animationSources = new List<Point>();
|
||||
_animationDirections = new List<AnimationDirection>();
|
||||
_animationDatas = new List<object[]>();
|
||||
|
||||
Increment = 0.03;
|
||||
SecondaryIncrement = 0.03;
|
||||
AnimationType = AnimationType.Linear;
|
||||
InterruptAnimation = true;
|
||||
Singular = singular;
|
||||
|
||||
if (Singular)
|
||||
{
|
||||
_animationProgresses.Add(0);
|
||||
_animationSources.Add(new Point(0, 0));
|
||||
_animationDirections.Add(AnimationDirection.In);
|
||||
}
|
||||
|
||||
_animationTimer.Tick += AnimationTimerOnTick;
|
||||
}
|
||||
|
||||
private void AnimationTimerOnTick(object sender, EventArgs eventArgs)
|
||||
{
|
||||
for (var i = 0; i < _animationProgresses.Count; i++)
|
||||
{
|
||||
UpdateProgress(i);
|
||||
|
||||
if (!Singular)
|
||||
{
|
||||
if ((_animationDirections[i] == AnimationDirection.InOutIn && _animationProgresses[i] == MAX_VALUE))
|
||||
{
|
||||
_animationDirections[i] = AnimationDirection.InOutOut;
|
||||
}
|
||||
else if ((_animationDirections[i] == AnimationDirection.InOutRepeatingIn && _animationProgresses[i] == MIN_VALUE))
|
||||
{
|
||||
_animationDirections[i] = AnimationDirection.InOutRepeatingOut;
|
||||
}
|
||||
else if ((_animationDirections[i] == AnimationDirection.InOutRepeatingOut && _animationProgresses[i] == MIN_VALUE))
|
||||
{
|
||||
_animationDirections[i] = AnimationDirection.InOutRepeatingIn;
|
||||
}
|
||||
else if (
|
||||
(_animationDirections[i] == AnimationDirection.In && _animationProgresses[i] == MAX_VALUE) ||
|
||||
(_animationDirections[i] == AnimationDirection.Out && _animationProgresses[i] == MIN_VALUE) ||
|
||||
(_animationDirections[i] == AnimationDirection.InOutOut && _animationProgresses[i] == MIN_VALUE))
|
||||
{
|
||||
_animationProgresses.RemoveAt(i);
|
||||
_animationSources.RemoveAt(i);
|
||||
_animationDirections.RemoveAt(i);
|
||||
_animationDatas.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((_animationDirections[i] == AnimationDirection.InOutIn && _animationProgresses[i] == MAX_VALUE))
|
||||
{
|
||||
_animationDirections[i] = AnimationDirection.InOutOut;
|
||||
}
|
||||
else if ((_animationDirections[i] == AnimationDirection.InOutRepeatingIn && _animationProgresses[i] == MAX_VALUE))
|
||||
{
|
||||
_animationDirections[i] = AnimationDirection.InOutRepeatingOut;
|
||||
}
|
||||
else if ((_animationDirections[i] == AnimationDirection.InOutRepeatingOut && _animationProgresses[i] == MIN_VALUE))
|
||||
{
|
||||
_animationDirections[i] = AnimationDirection.InOutRepeatingIn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OnAnimationProgress?.Invoke(this);
|
||||
}
|
||||
|
||||
public bool IsAnimating()
|
||||
{
|
||||
return _animationTimer.Enabled;
|
||||
}
|
||||
|
||||
public void StartNewAnimation(AnimationDirection animationDirection, object[] data = null)
|
||||
{
|
||||
StartNewAnimation(animationDirection, new Point(0, 0), data);
|
||||
}
|
||||
|
||||
public void StartNewAnimation(AnimationDirection animationDirection, Point animationSource, object[] data = null)
|
||||
{
|
||||
if (!IsAnimating() || InterruptAnimation)
|
||||
{
|
||||
if (Singular && _animationDirections.Count > 0)
|
||||
{
|
||||
_animationDirections[0] = animationDirection;
|
||||
}
|
||||
else
|
||||
{
|
||||
_animationDirections.Add(animationDirection);
|
||||
}
|
||||
|
||||
if (Singular && _animationSources.Count > 0)
|
||||
{
|
||||
_animationSources[0] = animationSource;
|
||||
}
|
||||
else
|
||||
{
|
||||
_animationSources.Add(animationSource);
|
||||
}
|
||||
|
||||
if (!(Singular && _animationProgresses.Count > 0))
|
||||
{
|
||||
switch (_animationDirections[_animationDirections.Count - 1])
|
||||
{
|
||||
case AnimationDirection.InOutRepeatingIn:
|
||||
case AnimationDirection.InOutIn:
|
||||
case AnimationDirection.In:
|
||||
_animationProgresses.Add(MIN_VALUE);
|
||||
break;
|
||||
case AnimationDirection.InOutRepeatingOut:
|
||||
case AnimationDirection.InOutOut:
|
||||
case AnimationDirection.Out:
|
||||
_animationProgresses.Add(MAX_VALUE);
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Invalid AnimationDirection");
|
||||
}
|
||||
}
|
||||
|
||||
if (Singular && _animationDatas.Count > 0)
|
||||
{
|
||||
_animationDatas[0] = data ?? new object[] { };
|
||||
}
|
||||
else
|
||||
{
|
||||
_animationDatas.Add(data ?? new object[] { });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_animationTimer.Start();
|
||||
}
|
||||
|
||||
public void UpdateProgress(int index)
|
||||
{
|
||||
switch (_animationDirections[index])
|
||||
{
|
||||
case AnimationDirection.InOutRepeatingIn:
|
||||
case AnimationDirection.InOutIn:
|
||||
case AnimationDirection.In:
|
||||
IncrementProgress(index);
|
||||
break;
|
||||
case AnimationDirection.InOutRepeatingOut:
|
||||
case AnimationDirection.InOutOut:
|
||||
case AnimationDirection.Out:
|
||||
DecrementProgress(index);
|
||||
break;
|
||||
default:
|
||||
throw new Exception("No AnimationDirection has been set");
|
||||
}
|
||||
}
|
||||
|
||||
private void IncrementProgress(int index)
|
||||
{
|
||||
_animationProgresses[index] += Increment;
|
||||
if (_animationProgresses[index] > MAX_VALUE)
|
||||
{
|
||||
_animationProgresses[index] = MAX_VALUE;
|
||||
|
||||
for (int i = 0; i < GetAnimationCount(); i++)
|
||||
{
|
||||
if (_animationDirections[i] == AnimationDirection.InOutIn) return;
|
||||
if (_animationDirections[i] == AnimationDirection.InOutRepeatingIn) return;
|
||||
if (_animationDirections[i] == AnimationDirection.InOutRepeatingOut) return;
|
||||
if (_animationDirections[i] == AnimationDirection.InOutOut && _animationProgresses[i] != MAX_VALUE) return;
|
||||
if (_animationDirections[i] == AnimationDirection.In && _animationProgresses[i] != MAX_VALUE) return;
|
||||
}
|
||||
|
||||
_animationTimer.Stop();
|
||||
OnAnimationFinished?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void DecrementProgress(int index)
|
||||
{
|
||||
_animationProgresses[index] -= (_animationDirections[index] == AnimationDirection.InOutOut || _animationDirections[index] == AnimationDirection.InOutRepeatingOut) ? SecondaryIncrement : Increment;
|
||||
if (_animationProgresses[index] < MIN_VALUE)
|
||||
{
|
||||
_animationProgresses[index] = MIN_VALUE;
|
||||
|
||||
for (var i = 0; i < GetAnimationCount(); i++)
|
||||
{
|
||||
if (_animationDirections[i] == AnimationDirection.InOutIn) return;
|
||||
if (_animationDirections[i] == AnimationDirection.InOutRepeatingIn) return;
|
||||
if (_animationDirections[i] == AnimationDirection.InOutRepeatingOut) return;
|
||||
if (_animationDirections[i] == AnimationDirection.InOutOut && _animationProgresses[i] != MIN_VALUE) return;
|
||||
if (_animationDirections[i] == AnimationDirection.Out && _animationProgresses[i] != MIN_VALUE) return;
|
||||
}
|
||||
|
||||
_animationTimer.Stop();
|
||||
OnAnimationFinished?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
public double GetProgress()
|
||||
{
|
||||
if (!Singular)
|
||||
throw new Exception("Animation is not set to Singular.");
|
||||
|
||||
if (_animationProgresses.Count == 0)
|
||||
throw new Exception("Invalid animation");
|
||||
|
||||
return GetProgress(0);
|
||||
}
|
||||
|
||||
public double GetProgress(int index)
|
||||
{
|
||||
if (!(index < GetAnimationCount()))
|
||||
throw new IndexOutOfRangeException("Invalid animation index");
|
||||
|
||||
switch (AnimationType)
|
||||
{
|
||||
case AnimationType.Linear:
|
||||
return AnimationLinear.CalculateProgress(_animationProgresses[index]);
|
||||
case AnimationType.EaseInOut:
|
||||
return AnimationEaseInOut.CalculateProgress(_animationProgresses[index]);
|
||||
case AnimationType.EaseOut:
|
||||
return AnimationEaseOut.CalculateProgress(_animationProgresses[index]);
|
||||
case AnimationType.CustomQuadratic:
|
||||
return AnimationCustomQuadratic.CalculateProgress(_animationProgresses[index]);
|
||||
default:
|
||||
throw new NotImplementedException("The given AnimationType is not implemented");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Point GetSource(int index)
|
||||
{
|
||||
if (!(index < GetAnimationCount()))
|
||||
throw new IndexOutOfRangeException("Invalid animation index");
|
||||
|
||||
return _animationSources[index];
|
||||
}
|
||||
|
||||
public Point GetSource()
|
||||
{
|
||||
if (!Singular)
|
||||
throw new Exception("Animation is not set to Singular.");
|
||||
|
||||
if (_animationSources.Count == 0)
|
||||
throw new Exception("Invalid animation");
|
||||
|
||||
return _animationSources[0];
|
||||
}
|
||||
|
||||
public AnimationDirection GetDirection()
|
||||
{
|
||||
if (!Singular)
|
||||
throw new Exception("Animation is not set to Singular.");
|
||||
|
||||
if (_animationDirections.Count == 0)
|
||||
throw new Exception("Invalid animation");
|
||||
|
||||
return _animationDirections[0];
|
||||
}
|
||||
|
||||
public AnimationDirection GetDirection(int index)
|
||||
{
|
||||
if (!(index < _animationDirections.Count))
|
||||
throw new IndexOutOfRangeException("Invalid animation index");
|
||||
|
||||
return _animationDirections[index];
|
||||
}
|
||||
|
||||
public object[] GetData()
|
||||
{
|
||||
if (!Singular)
|
||||
throw new Exception("Animation is not set to Singular.");
|
||||
|
||||
if (_animationDatas.Count == 0)
|
||||
throw new Exception("Invalid animation");
|
||||
|
||||
return _animationDatas[0];
|
||||
}
|
||||
|
||||
public object[] GetData(int index)
|
||||
{
|
||||
if (!(index < _animationDatas.Count))
|
||||
throw new IndexOutOfRangeException("Invalid animation index");
|
||||
|
||||
return _animationDatas[index];
|
||||
}
|
||||
|
||||
public int GetAnimationCount()
|
||||
{
|
||||
return _animationProgresses.Count;
|
||||
}
|
||||
|
||||
public void SetProgress(double progress)
|
||||
{
|
||||
if (!Singular)
|
||||
throw new Exception("Animation is not set to Singular.");
|
||||
|
||||
if (_animationProgresses.Count == 0)
|
||||
throw new Exception("Invalid animation");
|
||||
|
||||
_animationProgresses[0] = progress;
|
||||
}
|
||||
|
||||
public void SetDirection(AnimationDirection direction)
|
||||
{
|
||||
if (!Singular)
|
||||
throw new Exception("Animation is not set to Singular.");
|
||||
|
||||
if (_animationProgresses.Count == 0)
|
||||
throw new Exception("Invalid animation");
|
||||
|
||||
_animationDirections[0] = direction;
|
||||
}
|
||||
|
||||
public void SetData(object[] data)
|
||||
{
|
||||
if (!Singular)
|
||||
throw new Exception("Animation is not set to Singular.");
|
||||
|
||||
if (_animationDatas.Count == 0)
|
||||
throw new Exception("Invalid animation");
|
||||
|
||||
_animationDatas[0] = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
53
framework/MaterialSkin.Core/Animations/Animations.cs
Normal file
53
framework/MaterialSkin.Core/Animations/Animations.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
|
||||
namespace MaterialSkin.Animations
|
||||
{
|
||||
enum AnimationType
|
||||
{
|
||||
Linear,
|
||||
EaseInOut,
|
||||
EaseOut,
|
||||
CustomQuadratic
|
||||
}
|
||||
|
||||
static class AnimationLinear
|
||||
{
|
||||
public static double CalculateProgress(double progress)
|
||||
{
|
||||
return progress;
|
||||
}
|
||||
}
|
||||
|
||||
static class AnimationEaseInOut
|
||||
{
|
||||
public static double PI = Math.PI;
|
||||
public static double PI_HALF = Math.PI / 2;
|
||||
|
||||
public static double CalculateProgress(double progress)
|
||||
{
|
||||
return EaseInOut(progress);
|
||||
}
|
||||
|
||||
private static double EaseInOut(double s)
|
||||
{
|
||||
return s - Math.Sin(s * 2 * PI) / (2 * PI);
|
||||
}
|
||||
}
|
||||
|
||||
public static class AnimationEaseOut
|
||||
{
|
||||
public static double CalculateProgress(double progress)
|
||||
{
|
||||
return -1 * progress * (progress - 2);
|
||||
}
|
||||
}
|
||||
|
||||
public static class AnimationCustomQuadratic
|
||||
{
|
||||
public static double CalculateProgress(double progress)
|
||||
{
|
||||
var kickoff = 0.6;
|
||||
return 1 - Math.Cos((Math.Max(progress, kickoff) - kickoff) * Math.PI / (2 - (2 * kickoff)));
|
||||
}
|
||||
}
|
||||
}
|
||||
371
framework/MaterialSkin.Core/ColorScheme.cs
Normal file
371
framework/MaterialSkin.Core/ColorScheme.cs
Normal file
@@ -0,0 +1,371 @@
|
||||
using System.Drawing;
|
||||
|
||||
namespace MaterialSkin
|
||||
{
|
||||
public class ColorScheme
|
||||
{
|
||||
public readonly Color PrimaryColor, DarkPrimaryColor, LightPrimaryColor, AccentColor, TextColor;
|
||||
public readonly Pen PrimaryPen, DarkPrimaryPen, LightPrimaryPen, AccentPen, TextPen;
|
||||
public readonly Brush PrimaryBrush, DarkPrimaryBrush, LightPrimaryBrush, AccentBrush, TextBrush;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the Color Scheme to be used for all forms.
|
||||
/// </summary>
|
||||
/// <param name="primary">The primary color, a -500 color is suggested here.</param>
|
||||
/// <param name="darkPrimary">A darker version of the primary color, a -700 color is suggested here.</param>
|
||||
/// <param name="lightPrimary">A lighter version of the primary color, a -100 color is suggested here.</param>
|
||||
/// <param name="accent">The accent color, a -200 color is suggested here.</param>
|
||||
/// <param name="textShade">The text color, the one with the highest contrast is suggested.</param>
|
||||
public ColorScheme(Primary primary, Primary darkPrimary, Primary lightPrimary, Accent accent, TextShade textShade)
|
||||
{
|
||||
//Color
|
||||
PrimaryColor = ((int)primary).ToColor();
|
||||
DarkPrimaryColor = ((int)darkPrimary).ToColor();
|
||||
LightPrimaryColor = ((int)lightPrimary).ToColor();
|
||||
AccentColor = ((int)accent).ToColor();
|
||||
TextColor = ((int)textShade).ToColor();
|
||||
|
||||
//Pen
|
||||
PrimaryPen = new Pen(PrimaryColor);
|
||||
DarkPrimaryPen = new Pen(DarkPrimaryColor);
|
||||
LightPrimaryPen = new Pen(LightPrimaryColor);
|
||||
AccentPen = new Pen(AccentColor);
|
||||
TextPen = new Pen(TextColor);
|
||||
|
||||
//Brush
|
||||
PrimaryBrush = new SolidBrush(PrimaryColor);
|
||||
DarkPrimaryBrush = new SolidBrush(DarkPrimaryColor);
|
||||
LightPrimaryBrush = new SolidBrush(LightPrimaryColor);
|
||||
AccentBrush = new SolidBrush(AccentColor);
|
||||
TextBrush = new SolidBrush(TextColor);
|
||||
}
|
||||
|
||||
public ColorScheme(int primary, int darkPrimary, int lightPrimary, Accent accent, TextShade textShade)
|
||||
{
|
||||
//Color
|
||||
PrimaryColor = ((int)primary).ToColor();
|
||||
DarkPrimaryColor = ((int)darkPrimary).ToColor();
|
||||
LightPrimaryColor = ((int)lightPrimary).ToColor();
|
||||
AccentColor = ((int)accent).ToColor();
|
||||
TextColor = ((int)textShade).ToColor();
|
||||
|
||||
//Pen
|
||||
PrimaryPen = new Pen(PrimaryColor);
|
||||
DarkPrimaryPen = new Pen(DarkPrimaryColor);
|
||||
LightPrimaryPen = new Pen(LightPrimaryColor);
|
||||
AccentPen = new Pen(AccentColor);
|
||||
TextPen = new Pen(TextColor);
|
||||
|
||||
//Brush
|
||||
PrimaryBrush = new SolidBrush(PrimaryColor);
|
||||
DarkPrimaryBrush = new SolidBrush(DarkPrimaryColor);
|
||||
LightPrimaryBrush = new SolidBrush(LightPrimaryColor);
|
||||
AccentBrush = new SolidBrush(AccentColor);
|
||||
TextBrush = new SolidBrush(TextColor);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ColorExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert an integer number to a Color.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static Color ToColor(this int argb)
|
||||
{
|
||||
return Color.FromArgb(
|
||||
(argb & 0xff0000) >> 16,
|
||||
(argb & 0xff00) >> 8,
|
||||
argb & 0xff);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the alpha component of a color.
|
||||
/// </summary>
|
||||
/// <param name="color"></param>
|
||||
/// <returns></returns>
|
||||
public static Color RemoveAlpha(this Color color)
|
||||
{
|
||||
return Color.FromArgb(color.R, color.G, color.B);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a 0-100 integer to a 0-255 color component.
|
||||
/// </summary>
|
||||
/// <param name="percentage"></param>
|
||||
/// <returns></returns>
|
||||
public static int PercentageToColorComponent(this int percentage)
|
||||
{
|
||||
return (int)((percentage / 100d) * 255d);
|
||||
}
|
||||
}
|
||||
|
||||
//Color constantes
|
||||
public enum TextShade
|
||||
{
|
||||
WHITE = 0xFFFFFF,
|
||||
BLACK = 0x212121
|
||||
}
|
||||
|
||||
public enum Primary
|
||||
{
|
||||
Red50 = 0xFFEBEE,
|
||||
Red100 = 0xFFCDD2,
|
||||
Red200 = 0xEF9A9A,
|
||||
Red300 = 0xE57373,
|
||||
Red400 = 0xEF5350,
|
||||
Red500 = 0xF44336,
|
||||
Red600 = 0xE53935,
|
||||
Red700 = 0xD32F2F,
|
||||
Red800 = 0xC62828,
|
||||
Red900 = 0xB71C1C,
|
||||
Pink50 = 0xFCE4EC,
|
||||
Pink100 = 0xF8BBD0,
|
||||
Pink200 = 0xF48FB1,
|
||||
Pink300 = 0xF06292,
|
||||
Pink400 = 0xEC407A,
|
||||
Pink500 = 0xE91E63,
|
||||
Pink600 = 0xD81B60,
|
||||
Pink700 = 0xC2185B,
|
||||
Pink800 = 0xAD1457,
|
||||
Pink900 = 0x880E4F,
|
||||
Purple50 = 0xF3E5F5,
|
||||
Purple100 = 0xE1BEE7,
|
||||
Purple200 = 0xCE93D8,
|
||||
Purple300 = 0xBA68C8,
|
||||
Purple400 = 0xAB47BC,
|
||||
Purple500 = 0x9C27B0,
|
||||
Purple600 = 0x8E24AA,
|
||||
Purple700 = 0x7B1FA2,
|
||||
Purple800 = 0x6A1B9A,
|
||||
Purple900 = 0x4A148C,
|
||||
DeepPurple50 = 0xEDE7F6,
|
||||
DeepPurple100 = 0xD1C4E9,
|
||||
DeepPurple200 = 0xB39DDB,
|
||||
DeepPurple300 = 0x9575CD,
|
||||
DeepPurple400 = 0x7E57C2,
|
||||
DeepPurple500 = 0x673AB7,
|
||||
DeepPurple600 = 0x5E35B1,
|
||||
DeepPurple700 = 0x512DA8,
|
||||
DeepPurple800 = 0x4527A0,
|
||||
DeepPurple900 = 0x311B92,
|
||||
Indigo50 = 0xE8EAF6,
|
||||
Indigo100 = 0xC5CAE9,
|
||||
Indigo200 = 0x9FA8DA,
|
||||
Indigo300 = 0x7986CB,
|
||||
Indigo400 = 0x5C6BC0,
|
||||
Indigo500 = 0x3F51B5,
|
||||
Indigo600 = 0x3949AB,
|
||||
Indigo700 = 0x303F9F,
|
||||
Indigo800 = 0x283593,
|
||||
Indigo900 = 0x1A237E,
|
||||
Blue50 = 0xE3F2FD,
|
||||
Blue100 = 0xBBDEFB,
|
||||
Blue200 = 0x90CAF9,
|
||||
Blue300 = 0x64B5F6,
|
||||
Blue400 = 0x42A5F5,
|
||||
Blue500 = 0x2196F3,
|
||||
Blue600 = 0x1E88E5,
|
||||
Blue700 = 0x1976D2,
|
||||
Blue800 = 0x1565C0,
|
||||
Blue900 = 0x0D47A1,
|
||||
LightBlue50 = 0xE1F5FE,
|
||||
LightBlue100 = 0xB3E5FC,
|
||||
LightBlue200 = 0x81D4FA,
|
||||
LightBlue300 = 0x4FC3F7,
|
||||
LightBlue400 = 0x29B6F6,
|
||||
LightBlue500 = 0x03A9F4,
|
||||
LightBlue600 = 0x039BE5,
|
||||
LightBlue700 = 0x0288D1,
|
||||
LightBlue800 = 0x0277BD,
|
||||
LightBlue900 = 0x01579B,
|
||||
Cyan50 = 0xE0F7FA,
|
||||
Cyan100 = 0xB2EBF2,
|
||||
Cyan200 = 0x80DEEA,
|
||||
Cyan300 = 0x4DD0E1,
|
||||
Cyan400 = 0x26C6DA,
|
||||
Cyan500 = 0x00BCD4,
|
||||
Cyan600 = 0x00ACC1,
|
||||
Cyan700 = 0x0097A7,
|
||||
Cyan800 = 0x00838F,
|
||||
Cyan900 = 0x006064,
|
||||
Teal50 = 0xE0F2F1,
|
||||
Teal100 = 0xB2DFDB,
|
||||
Teal200 = 0x80CBC4,
|
||||
Teal300 = 0x4DB6AC,
|
||||
Teal400 = 0x26A69A,
|
||||
Teal500 = 0x009688,
|
||||
Teal600 = 0x00897B,
|
||||
Teal700 = 0x00796B,
|
||||
Teal800 = 0x00695C,
|
||||
Teal900 = 0x004D40,
|
||||
Green50 = 0xE8F5E9,
|
||||
Green100 = 0xC8E6C9,
|
||||
Green200 = 0xA5D6A7,
|
||||
Green300 = 0x81C784,
|
||||
Green400 = 0x66BB6A,
|
||||
Green500 = 0x4CAF50,
|
||||
Green600 = 0x43A047,
|
||||
Green700 = 0x388E3C,
|
||||
Green800 = 0x2E7D32,
|
||||
Green900 = 0x1B5E20,
|
||||
LightGreen50 = 0xF1F8E9,
|
||||
LightGreen100 = 0xDCEDC8,
|
||||
LightGreen200 = 0xC5E1A5,
|
||||
LightGreen300 = 0xAED581,
|
||||
LightGreen400 = 0x9CCC65,
|
||||
LightGreen500 = 0x8BC34A,
|
||||
LightGreen600 = 0x7CB342,
|
||||
LightGreen700 = 0x689F38,
|
||||
LightGreen800 = 0x558B2F,
|
||||
LightGreen900 = 0x33691E,
|
||||
Lime50 = 0xF9FBE7,
|
||||
Lime100 = 0xF0F4C3,
|
||||
Lime200 = 0xE6EE9C,
|
||||
Lime300 = 0xDCE775,
|
||||
Lime400 = 0xD4E157,
|
||||
Lime500 = 0xCDDC39,
|
||||
Lime600 = 0xC0CA33,
|
||||
Lime700 = 0xAFB42B,
|
||||
Lime800 = 0x9E9D24,
|
||||
Lime900 = 0x827717,
|
||||
Yellow50 = 0xFFFDE7,
|
||||
Yellow100 = 0xFFF9C4,
|
||||
Yellow200 = 0xFFF59D,
|
||||
Yellow300 = 0xFFF176,
|
||||
Yellow400 = 0xFFEE58,
|
||||
Yellow500 = 0xFFEB3B,
|
||||
Yellow600 = 0xFDD835,
|
||||
Yellow700 = 0xFBC02D,
|
||||
Yellow800 = 0xF9A825,
|
||||
Yellow900 = 0xF57F17,
|
||||
Amber50 = 0xFFF8E1,
|
||||
Amber100 = 0xFFECB3,
|
||||
Amber200 = 0xFFE082,
|
||||
Amber300 = 0xFFD54F,
|
||||
Amber400 = 0xFFCA28,
|
||||
Amber500 = 0xFFC107,
|
||||
Amber600 = 0xFFB300,
|
||||
Amber700 = 0xFFA000,
|
||||
Amber800 = 0xFF8F00,
|
||||
Amber900 = 0xFF6F00,
|
||||
Orange50 = 0xFFF3E0,
|
||||
Orange100 = 0xFFE0B2,
|
||||
Orange200 = 0xFFCC80,
|
||||
Orange300 = 0xFFB74D,
|
||||
Orange400 = 0xFFA726,
|
||||
Orange500 = 0xFF9800,
|
||||
Orange600 = 0xFB8C00,
|
||||
Orange700 = 0xF57C00,
|
||||
Orange800 = 0xEF6C00,
|
||||
Orange900 = 0xE65100,
|
||||
DeepOrange50 = 0xFBE9E7,
|
||||
DeepOrange100 = 0xFFCCBC,
|
||||
DeepOrange200 = 0xFFAB91,
|
||||
DeepOrange300 = 0xFF8A65,
|
||||
DeepOrange400 = 0xFF7043,
|
||||
DeepOrange500 = 0xFF5722,
|
||||
DeepOrange600 = 0xF4511E,
|
||||
DeepOrange700 = 0xE64A19,
|
||||
DeepOrange800 = 0xD84315,
|
||||
DeepOrange900 = 0xBF360C,
|
||||
Brown50 = 0xEFEBE9,
|
||||
Brown100 = 0xD7CCC8,
|
||||
Brown200 = 0xBCAAA4,
|
||||
Brown300 = 0xA1887F,
|
||||
Brown400 = 0x8D6E63,
|
||||
Brown500 = 0x795548,
|
||||
Brown600 = 0x6D4C41,
|
||||
Brown700 = 0x5D4037,
|
||||
Brown800 = 0x4E342E,
|
||||
Brown900 = 0x3E2723,
|
||||
Grey50 = 0xFAFAFA,
|
||||
Grey100 = 0xF5F5F5,
|
||||
Grey200 = 0xEEEEEE,
|
||||
Grey300 = 0xE0E0E0,
|
||||
Grey400 = 0xBDBDBD,
|
||||
Grey500 = 0x9E9E9E,
|
||||
Grey600 = 0x757575,
|
||||
Grey700 = 0x616161,
|
||||
Grey800 = 0x424242,
|
||||
Grey900 = 0x212121,
|
||||
BlueGrey50 = 0xECEFF1,
|
||||
BlueGrey100 = 0xCFD8DC,
|
||||
BlueGrey200 = 0xB0BEC5,
|
||||
BlueGrey300 = 0x90A4AE,
|
||||
BlueGrey400 = 0x78909C,
|
||||
BlueGrey500 = 0x607D8B,
|
||||
BlueGrey600 = 0x546E7A,
|
||||
BlueGrey700 = 0x455A64,
|
||||
BlueGrey800 = 0x37474F,
|
||||
BlueGrey900 = 0x263238
|
||||
}
|
||||
|
||||
public enum Accent
|
||||
{
|
||||
Red100 = 0xFF8A80,
|
||||
Red200 = 0xFF5252,
|
||||
Red400 = 0xFF1744,
|
||||
Red700 = 0xD50000,
|
||||
Pink100 = 0xFF80AB,
|
||||
Pink200 = 0xFF4081,
|
||||
Pink400 = 0xF50057,
|
||||
Pink700 = 0xC51162,
|
||||
Purple100 = 0xEA80FC,
|
||||
Purple200 = 0xE040FB,
|
||||
Purple400 = 0xD500F9,
|
||||
Purple700 = 0xAA00FF,
|
||||
DeepPurple100 = 0xB388FF,
|
||||
DeepPurple200 = 0x7C4DFF,
|
||||
DeepPurple400 = 0x651FFF,
|
||||
DeepPurple700 = 0x6200EA,
|
||||
Indigo100 = 0x8C9EFF,
|
||||
Indigo200 = 0x536DFE,
|
||||
Indigo400 = 0x3D5AFE,
|
||||
Indigo700 = 0x304FFE,
|
||||
Blue100 = 0x82B1FF,
|
||||
Blue200 = 0x448AFF,
|
||||
Blue400 = 0x2979FF,
|
||||
Blue700 = 0x2962FF,
|
||||
LightBlue100 = 0x80D8FF,
|
||||
LightBlue200 = 0x40C4FF,
|
||||
LightBlue400 = 0x00B0FF,
|
||||
LightBlue700 = 0x0091EA,
|
||||
Cyan100 = 0x84FFFF,
|
||||
Cyan200 = 0x18FFFF,
|
||||
Cyan400 = 0x00E5FF,
|
||||
Cyan700 = 0x00B8D4,
|
||||
Teal100 = 0xA7FFEB,
|
||||
Teal200 = 0x64FFDA,
|
||||
Teal400 = 0x1DE9B6,
|
||||
Teal700 = 0x00BFA5,
|
||||
Green100 = 0xB9F6CA,
|
||||
Green200 = 0x69F0AE,
|
||||
Green400 = 0x00E676,
|
||||
Green700 = 0x00C853,
|
||||
LightGreen100 = 0xCCFF90,
|
||||
LightGreen200 = 0xB2FF59,
|
||||
LightGreen400 = 0x76FF03,
|
||||
LightGreen700 = 0x64DD17,
|
||||
Lime100 = 0xF4FF81,
|
||||
Lime200 = 0xEEFF41,
|
||||
Lime400 = 0xC6FF00,
|
||||
Lime700 = 0xAEEA00,
|
||||
Yellow100 = 0xFFFF8D,
|
||||
Yellow200 = 0xFFFF00,
|
||||
Yellow400 = 0xFFEA00,
|
||||
Yellow700 = 0xFFD600,
|
||||
Amber100 = 0xFFE57F,
|
||||
Amber200 = 0xFFD740,
|
||||
Amber400 = 0xFFC400,
|
||||
Amber700 = 0xFFAB00,
|
||||
Orange100 = 0xFFD180,
|
||||
Orange200 = 0xFFAB40,
|
||||
Orange400 = 0xFF9100,
|
||||
Orange700 = 0xFF6D00,
|
||||
DeepOrange100 = 0xFF9E80,
|
||||
DeepOrange200 = 0xFF6E40,
|
||||
DeepOrange400 = 0xFF3D00,
|
||||
DeepOrange700 = 0xDD2C00
|
||||
}
|
||||
}
|
||||
250
framework/MaterialSkin.Core/Controls/MaterialCheckBox.cs
Normal file
250
framework/MaterialSkin.Core/Controls/MaterialCheckBox.cs
Normal file
@@ -0,0 +1,250 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Drawing.Text;
|
||||
using System.Windows.Forms;
|
||||
using MaterialSkin.Animations;
|
||||
|
||||
namespace MaterialSkin.Controls
|
||||
{
|
||||
public class MaterialCheckBox : CheckBox, IMaterialControl
|
||||
{
|
||||
[Browsable(false)]
|
||||
public int Depth { get; set; }
|
||||
[Browsable(false)]
|
||||
public MaterialSkinManager SkinManager => MaterialSkinManager.Instance;
|
||||
[Browsable(false)]
|
||||
public MouseState MouseState { get; set; }
|
||||
[Browsable(false)]
|
||||
public Point MouseLocation { get; set; }
|
||||
|
||||
private bool _ripple;
|
||||
[Category("Behavior")]
|
||||
public bool Ripple
|
||||
{
|
||||
get { return _ripple; }
|
||||
set
|
||||
{
|
||||
_ripple = value;
|
||||
AutoSize = AutoSize; //Make AutoSize directly set the bounds.
|
||||
|
||||
if (value)
|
||||
{
|
||||
Margin = new Padding(0);
|
||||
}
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
private readonly AnimationManager _animationManager;
|
||||
private readonly AnimationManager _rippleAnimationManager;
|
||||
|
||||
private const int CHECKBOX_SIZE = 18;
|
||||
private const int CHECKBOX_SIZE_HALF = CHECKBOX_SIZE / 2;
|
||||
private const int CHECKBOX_INNER_BOX_SIZE = CHECKBOX_SIZE - 4;
|
||||
|
||||
private int _boxOffset;
|
||||
private Rectangle _boxRectangle;
|
||||
|
||||
public MaterialCheckBox()
|
||||
{
|
||||
_animationManager = new AnimationManager
|
||||
{
|
||||
AnimationType = AnimationType.EaseInOut,
|
||||
Increment = 0.05
|
||||
};
|
||||
_rippleAnimationManager = new AnimationManager(false)
|
||||
{
|
||||
AnimationType = AnimationType.Linear,
|
||||
Increment = 0.10,
|
||||
SecondaryIncrement = 0.08
|
||||
};
|
||||
_animationManager.OnAnimationProgress += sender => Invalidate();
|
||||
_rippleAnimationManager.OnAnimationProgress += sender => Invalidate();
|
||||
|
||||
CheckedChanged += (sender, args) =>
|
||||
{
|
||||
_animationManager.StartNewAnimation(Checked ? AnimationDirection.In : AnimationDirection.Out);
|
||||
};
|
||||
|
||||
Ripple = true;
|
||||
MouseLocation = new Point(-1, -1);
|
||||
}
|
||||
|
||||
protected override void OnSizeChanged(EventArgs e)
|
||||
{
|
||||
base.OnSizeChanged(e);
|
||||
|
||||
_boxOffset = Height / 2 - 9;
|
||||
_boxRectangle = new Rectangle(_boxOffset, _boxOffset, CHECKBOX_SIZE - 1, CHECKBOX_SIZE - 1);
|
||||
}
|
||||
|
||||
public override Size GetPreferredSize(Size proposedSize)
|
||||
{
|
||||
var w = _boxOffset + CHECKBOX_SIZE + 2 + (int)CreateGraphics().MeasureString(Text, SkinManager.ROBOTO_MEDIUM_10).Width;
|
||||
return Ripple ? new Size(w, 30) : new Size(w, 20);
|
||||
}
|
||||
|
||||
private static readonly Point[] CheckmarkLine = { new Point(3, 8), new Point(7, 12), new Point(14, 5) };
|
||||
private const int TEXT_OFFSET = 22;
|
||||
protected override void OnPaint(PaintEventArgs pevent)
|
||||
{
|
||||
var g = pevent.Graphics;
|
||||
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||||
g.TextRenderingHint = TextRenderingHint.AntiAlias;
|
||||
|
||||
// clear the control
|
||||
g.Clear(Parent.BackColor);
|
||||
|
||||
var CHECKBOX_CENTER = _boxOffset + CHECKBOX_SIZE_HALF - 1;
|
||||
|
||||
var animationProgress = _animationManager.GetProgress();
|
||||
|
||||
var colorAlpha = Enabled ? (int)(animationProgress * 255.0) : SkinManager.GetCheckBoxOffDisabledColor().A;
|
||||
var backgroundAlpha = Enabled ? (int)(SkinManager.GetCheckboxOffColor().A * (1.0 - animationProgress)) : SkinManager.GetCheckBoxOffDisabledColor().A;
|
||||
|
||||
var brush = new SolidBrush(Color.FromArgb(colorAlpha, Enabled ? SkinManager.ColorScheme.AccentColor : SkinManager.GetCheckBoxOffDisabledColor()));
|
||||
var brush3 = new SolidBrush(Enabled ? SkinManager.ColorScheme.AccentColor : SkinManager.GetCheckBoxOffDisabledColor());
|
||||
var pen = new Pen(brush.Color);
|
||||
|
||||
// draw ripple animation
|
||||
if (Ripple && _rippleAnimationManager.IsAnimating())
|
||||
{
|
||||
for (var i = 0; i < _rippleAnimationManager.GetAnimationCount(); i++)
|
||||
{
|
||||
var animationValue = _rippleAnimationManager.GetProgress(i);
|
||||
var animationSource = new Point(CHECKBOX_CENTER, CHECKBOX_CENTER);
|
||||
var rippleBrush = new SolidBrush(Color.FromArgb((int)((animationValue * 40)), ((bool)_rippleAnimationManager.GetData(i)[0]) ? Color.Black : brush.Color));
|
||||
var rippleHeight = (Height % 2 == 0) ? Height - 3 : Height - 2;
|
||||
var rippleSize = (_rippleAnimationManager.GetDirection(i) == AnimationDirection.InOutIn) ? (int)(rippleHeight * (0.8d + (0.2d * animationValue))) : rippleHeight;
|
||||
using (var path = DrawHelper.CreateRoundRect(animationSource.X - rippleSize / 2, animationSource.Y - rippleSize / 2, rippleSize, rippleSize, rippleSize / 2))
|
||||
{
|
||||
g.FillPath(rippleBrush, path);
|
||||
}
|
||||
|
||||
rippleBrush.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
brush3.Dispose();
|
||||
|
||||
var checkMarkLineFill = new Rectangle(_boxOffset, _boxOffset, (int)((int)(FontHeight * 1.1) * animationProgress), (int)(FontHeight * 1.1));
|
||||
using (var checkmarkPath = DrawHelper.CreateRoundRect(_boxOffset, _boxOffset, (int)(FontHeight*1.1), (int)(FontHeight * 1.1), 1f))
|
||||
{
|
||||
var brush2 = new SolidBrush(DrawHelper.BlendColor(Parent.BackColor, Enabled ? SkinManager.GetCheckboxOffColor() : SkinManager.GetCheckBoxOffDisabledColor(), backgroundAlpha));
|
||||
var pen2 = new Pen(brush2.Color);
|
||||
g.FillPath(brush2, checkmarkPath);
|
||||
g.DrawPath(pen2, checkmarkPath);
|
||||
|
||||
g.FillRectangle(new SolidBrush(Parent.BackColor), _boxOffset + 2, _boxOffset + 2, (int)(FontHeight * 1.1) -4, (int)(FontHeight * 1.1) -4);
|
||||
g.DrawRectangle(new Pen(Parent.BackColor), _boxOffset + 2, _boxOffset + 2, (int)(FontHeight * 1.1) -4, (int)(FontHeight * 1.1) - 4);
|
||||
|
||||
brush2.Dispose();
|
||||
pen2.Dispose();
|
||||
|
||||
if (Enabled)
|
||||
{
|
||||
g.FillPath(brush, checkmarkPath);
|
||||
g.DrawPath(pen, checkmarkPath);
|
||||
}
|
||||
else if (Checked)
|
||||
{
|
||||
g.SmoothingMode = SmoothingMode.None;
|
||||
g.FillRectangle(brush, _boxOffset + 2, _boxOffset + 2, CHECKBOX_INNER_BOX_SIZE, CHECKBOX_INNER_BOX_SIZE);
|
||||
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||||
}
|
||||
|
||||
g.DrawImageUnscaledAndClipped(DrawCheckMarkBitmap(), checkMarkLineFill);
|
||||
}
|
||||
|
||||
// draw checkbox text
|
||||
SizeF stringSize = g.MeasureString(Text, Font);
|
||||
g.DrawString(
|
||||
Text,
|
||||
Font,
|
||||
Enabled ? SkinManager.GetPrimaryTextBrush() : SkinManager.GetDisabledOrHintBrush(),
|
||||
_boxOffset + TEXT_OFFSET, Height / 2 - stringSize.Height / 2);
|
||||
|
||||
// dispose used paint objects
|
||||
pen.Dispose();
|
||||
brush.Dispose();
|
||||
}
|
||||
|
||||
private Bitmap DrawCheckMarkBitmap()
|
||||
{
|
||||
var checkMark = new Bitmap(CHECKBOX_SIZE, CHECKBOX_SIZE);
|
||||
var g = Graphics.FromImage(checkMark);
|
||||
|
||||
// clear everything, transparent
|
||||
g.Clear(Color.Transparent);
|
||||
|
||||
// draw the checkmark lines
|
||||
using (var pen = new Pen(Parent.BackColor, 2))
|
||||
{
|
||||
g.DrawLines(pen, CheckmarkLine);
|
||||
}
|
||||
|
||||
return checkMark;
|
||||
}
|
||||
|
||||
public override bool AutoSize
|
||||
{
|
||||
get { return base.AutoSize; }
|
||||
set
|
||||
{
|
||||
base.AutoSize = value;
|
||||
if (value)
|
||||
{
|
||||
Size = new Size(10, 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsMouseInCheckArea()
|
||||
{
|
||||
return _boxRectangle.Contains(MouseLocation);
|
||||
}
|
||||
|
||||
protected override void OnCreateControl()
|
||||
{
|
||||
base.OnCreateControl();
|
||||
Font = SkinManager.ROBOTO_MEDIUM_10;
|
||||
|
||||
if (DesignMode) return;
|
||||
|
||||
MouseState = MouseState.OUT;
|
||||
MouseEnter += (sender, args) =>
|
||||
{
|
||||
MouseState = MouseState.HOVER;
|
||||
};
|
||||
MouseLeave += (sender, args) =>
|
||||
{
|
||||
MouseLocation = new Point(-1, -1);
|
||||
MouseState = MouseState.OUT;
|
||||
};
|
||||
MouseDown += (sender, args) =>
|
||||
{
|
||||
MouseState = MouseState.DOWN;
|
||||
|
||||
if (Ripple && args.Button == MouseButtons.Left && IsMouseInCheckArea())
|
||||
{
|
||||
_rippleAnimationManager.SecondaryIncrement = 0;
|
||||
_rippleAnimationManager.StartNewAnimation(AnimationDirection.InOutIn, new object[] { Checked });
|
||||
}
|
||||
};
|
||||
MouseUp += (sender, args) =>
|
||||
{
|
||||
MouseState = MouseState.HOVER;
|
||||
_rippleAnimationManager.SecondaryIncrement = 0.08;
|
||||
};
|
||||
MouseMove += (sender, args) =>
|
||||
{
|
||||
MouseLocation = args.Location;
|
||||
Cursor = IsMouseInCheckArea() ? Cursors.Hand : Cursors.Default;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user