diff --git a/Hawkeye.VisionBuilder.UI.Sources.Emulation/EmulationSettings.cs b/Hawkeye.VisionBuilder.UI.Sources.Emulation/EmulationSettings.cs index 17622e3..f25c55c 100644 --- a/Hawkeye.VisionBuilder.UI.Sources.Emulation/EmulationSettings.cs +++ b/Hawkeye.VisionBuilder.UI.Sources.Emulation/EmulationSettings.cs @@ -19,9 +19,9 @@ public class EmulationSettings : ISettings public void RegisterSettings(InspectronSettings settings) { - settings.RegisterSimple(this, () => CycleDelay, CameraName+"/Emulation", nameof(CycleDelay)); - settings.RegisterSimple(this, () => PerImageDelay, CameraName + "/Emulation", nameof(PerImageDelay)); - settings.RegisterSimple(this, () => SingleRun, CameraName + "/Emulation", nameof(SingleRun)); - settings.RegisterSimple(this, () => EmulationPath, CameraName + "/Emulation", nameof(EmulationPath)); + settings.RegisterSimple(this, () => CycleDelay, CameraName+"/Sources/Emulation", nameof(CycleDelay)); + settings.RegisterSimple(this, () => PerImageDelay, CameraName + "/Sources/Emulation", nameof(PerImageDelay)); + settings.RegisterSimple(this, () => SingleRun, CameraName + "/Sources/Emulation", nameof(SingleRun)); + settings.RegisterSimple(this, () => EmulationPath, CameraName + "/Sources/Emulation", nameof(EmulationPath)); } } \ No newline at end of file diff --git a/Hawkeye.VisionBuilder.UI.Sources.IDS/Hawkeye.VisionBuilder.UI.Sources.IDS.csproj b/Hawkeye.VisionBuilder.UI.Sources.IDS/Hawkeye.VisionBuilder.UI.Sources.IDS.csproj new file mode 100644 index 0000000..5df1e7b --- /dev/null +++ b/Hawkeye.VisionBuilder.UI.Sources.IDS/Hawkeye.VisionBuilder.UI.Sources.IDS.csproj @@ -0,0 +1,19 @@ + + + + net8.0 + enable + enable + + + + + + + + + uEyeDotNet.dll + + + + diff --git a/Hawkeye.VisionBuilder.UI.Sources.IDS/IDSImageSource.cs b/Hawkeye.VisionBuilder.UI.Sources.IDS/IDSImageSource.cs new file mode 100644 index 0000000..a8ccf07 --- /dev/null +++ b/Hawkeye.VisionBuilder.UI.Sources.IDS/IDSImageSource.cs @@ -0,0 +1,228 @@ +using OpenCvSharp; +using Serilog; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Threading; +using System.Threading.Channels; +using System.Threading.Tasks; +using uEye.Defines; +using VisionBuilder.UI.Common; +using VisionBuilder.UI.Common.RecipeProcessing; +using Point = System.Drawing.Point; + +namespace Inspectron.Camera.UEye +{ + + public class IDSImageSource : IImageSource, IVisionBuilderModule + { + private readonly IDSImageSourceSettings _settings; + private uEye.Camera _camera = null; + + + int cameraIdx; + + + + public IDSImageSource(IDSImageSourceSettings settings) + { + _settings = settings; + _camera = new uEye.Camera(); + cameraIdx = _settings.CameraId; + System.AppDomain.CurrentDomain.ProcessExit += AppDomain_ProcessExit; + } + + private void AppDomain_ProcessExit(object sender, EventArgs e) + { + Close(); + } + + private Mat MemoryToImage(int idx) + { + uEye.Defines.ColorMode mode; + _camera.PixelFormat.Get(out mode); + int channels = 0; + + if (mode == uEye.Defines.ColorMode.Mono8) + { + channels = 1; + } + else if (mode == uEye.Defines.ColorMode.BGR8Packed || mode == uEye.Defines.ColorMode.RGB8Packed) + { + channels = 3; + } + else if (mode == uEye.Defines.ColorMode.BGRA8Packed) + { + channels = 3; + } + else + { + throw new Exception("unknown image format"); + } + + var size = new uEye.Types.Size(); + _camera.Memory.GetSize(idx, out size); + int pitch; + _camera.Memory.GetPitch(idx, out pitch); + + Byte[] u8img; + _camera.Memory.CopyToArray(idx, out u8img); + //Console.WriteLine($"Image size: {size.Width}x{size.Height}, channels: {channels}({mode}), data size: {u8img.Length}, pitch: {pitch}"); + //Console.ReadLine(); + Mat mat = new Mat(size.Height,pitch, MatType.CV_8UC(channels)); + mat.SetArray(u8img); + return mat; + } + + public void Open() + { + if (_camera.IsOpened) + return; + + uEye.Defines.Status statusRet = 0; + + // Open _camera + statusRet = _camera.Init(cameraIdx); + if (statusRet != uEye.Defines.Status.Success) + { + throw new Exception("_camera initializing failed: "+statusRet); + } + uEye.Types.SensorInfo info = new uEye.Types.SensorInfo(); + _camera.Information.GetSensorInfo(out info); + Console.WriteLine($"Camera {cameraIdx} opened: {info.SensorName} ({info.SensorID})"); + _camera.Parameter.Load(); + Console.WriteLine($"Camera {cameraIdx} parameters loaded"); + + _camera.Trigger.Set(uEye.Defines.TriggerMode.Continuous); + + statusRet = _camera.Memory.Allocate(); + Console.WriteLine("Memory allocated"); + if (statusRet != uEye.Defines.Status.Success) + { + throw new Exception("Allocate Memory failed"); + + } + _camera.EventFrame += onFrameEvent; + try + { + _camera.Device.Feature.ShutterMode.Set(uEye.Defines.Shuttermode.Global); + } + catch (Exception e) + { + Console.WriteLine(e.Message); + } + } + + public void Close() + { + if (!_camera.IsOpened) + return; + + uEye.Defines.Status statusRet; + statusRet = _camera.Exit(); + + if (statusRet != uEye.Defines.Status.Success) + { + throw new Exception("failed to close camera"); + } + } + + + + public void LoadParameters(string parameters_file) + { + uEye.Defines.Status statusRet = 0; + + Int32[] memList; + statusRet = _camera.Memory.GetList(out memList); + if (statusRet != uEye.Defines.Status.Success) + { + throw new Exception("Get memory list failed: " + statusRet); + } + + statusRet = _camera.Memory.Free(memList); + if (statusRet != uEye.Defines.Status.Success) + { + throw new Exception("Free memory list failed: " + statusRet); + } + + statusRet = _camera.Parameter.Load(parameters_file); + if (statusRet != uEye.Defines.Status.Success) + { + throw new Exception("Loading parameter failed: " + statusRet); + } + + uEye.Types.Size t = new uEye.Types.Size(); + + + statusRet = _camera.Memory.Allocate(); + if (statusRet != uEye.Defines.Status.SUCCESS) + { + throw new Exception("Allocate Memory failed"); + } + } + + public void SaveParameters(string parameters_file) + { + uEye.Defines.Status statusRet = 0; + + statusRet = _camera.Parameter.Save(parameters_file); + if (statusRet != uEye.Defines.Status.SUCCESS) + { + throw new Exception("failed to save Parameters"); + } + } + + + + + private void onFrameEvent(object sender, EventArgs e) + { + try + { + uEye.Camera camera = sender as uEye.Camera; + int s32MemId; + int active = (int)camera.Memory.GetActive(out s32MemId); + int num1 = (int)camera.Memory.Lock(s32MemId); + var image = this.MemoryToImage(s32MemId); + int num2 = (int)camera.Memory.Unlock(s32MemId); + + if (!_channel.Writer.TryWrite(image)) + { + Log.Warning("Image channel is full, dropping image"); + } + } + catch (TaskCanceledException ex) + { + int num = (int)(sender as uEye.Camera).Acquisition.Stop(); + + } + } + + + + + + private Channel _channel = Channel.CreateBounded(new BoundedChannelOptions(1) + { + FullMode = BoundedChannelFullMode.DropOldest, + SingleReader = true, + SingleWriter = true, + AllowSynchronousContinuations = true + }); + public async Task GetImage(CancellationToken token) + { + return await _channel.Reader.ReadAsync(token); + } + + public void InitializeModule() + { + Open(); + if(!(string.IsNullOrEmpty(_settings.CameraFile))) + LoadParameters(_settings.CameraFile); + _camera.Trigger.Set(uEye.Defines.TriggerMode.Continuous); + _camera.Acquisition.Capture(); + } + } +} \ No newline at end of file diff --git a/Hawkeye.VisionBuilder.UI.Sources.IDS/IDSImageSourceSettings.cs b/Hawkeye.VisionBuilder.UI.Sources.IDS/IDSImageSourceSettings.cs new file mode 100644 index 0000000..9626c2a --- /dev/null +++ b/Hawkeye.VisionBuilder.UI.Sources.IDS/IDSImageSourceSettings.cs @@ -0,0 +1,40 @@ +using System.Text; +using Inspectron.Settings; +using Inspectron.Settings.Attributes; +using VisionBuilder.UI.Common; + +namespace Inspectron.Camera.UEye; + +public class IDSImageSourceSettings:ISettings +{ + public string CameraName { get; } + + public IDSImageSourceSettings(string cameraName) + { + CameraName = cameraName; + } + + [SettingPreview(typeof(IDSImageSourceSettings), nameof(CameraIdsPreview))] + public int CameraId { get; set; } = 0; + + [File("*.ini")] public string CameraFile { get; set; } = ""; + + public void RegisterSettings(InspectronSettings settings) + { + settings.RegisterSimple(this, () => CameraId, $"{CameraName}/Sources/IDS", nameof(CameraId)); + settings.RegisterSimple(this, () => CameraFile, $"{CameraName}/Sources/IDS", nameof(CameraFile)); + + } + + public static string CameraIdsPreview(int _) + { + uEye.Info.Camera.GetCameraList(out var cameraList); + StringBuilder sb = new StringBuilder(); + foreach (var camera in cameraList) + { + sb.AppendLine($"Camera ID: {camera.CameraID}, Name: {camera.SerialNumber}"); + } + + return sb.ToString(); + } +} \ No newline at end of file diff --git a/Hawkeye.VisionBuilder.UI.Sources.IDS/uEyeDotNet.dll b/Hawkeye.VisionBuilder.UI.Sources.IDS/uEyeDotNet.dll new file mode 100644 index 0000000..1b14092 Binary files /dev/null and b/Hawkeye.VisionBuilder.UI.Sources.IDS/uEyeDotNet.dll differ diff --git a/IDSTest/IDSTest.csproj b/IDSTest/IDSTest.csproj new file mode 100644 index 0000000..2bbea33 --- /dev/null +++ b/IDSTest/IDSTest.csproj @@ -0,0 +1,19 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + + + + diff --git a/IDSTest/Program.cs b/IDSTest/Program.cs new file mode 100644 index 0000000..57ad58c --- /dev/null +++ b/IDSTest/Program.cs @@ -0,0 +1,13 @@ +using Inspectron.Camera.UEye; + +IDSImageSource imageSource = new IDSImageSource(); + +Console.WriteLine("Opening"); +imageSource.InitializeModule(); + +while (true) +{ + Console.WriteLine("Getting image"); + var image=imageSource.GetImage(CancellationToken.None).Result; + image.SaveImage("test.png"); +} \ No newline at end of file diff --git a/VisionBuilder.UI.Camera/ModuleExtensions.cs b/VisionBuilder.UI.Camera/ModuleExtensions.cs index bbf123a..794996d 100644 --- a/VisionBuilder.UI.Camera/ModuleExtensions.cs +++ b/VisionBuilder.UI.Camera/ModuleExtensions.cs @@ -1,4 +1,5 @@ using Hawkeye.VisionBuilder.UI.Sources.Emulation; +using Inspectron.Camera.UEye; using Ninject; using Ninject.Extensions.ChildKernel; using VisionBuilder.UI.Common; @@ -12,6 +13,7 @@ public static class ModuleExtensions { self.Bind().ToConstant(new CameraSettings(cameraName)); self.Bind().ToConstant(new EmulationSettings(cameraName)); + self.Bind().ToConstant(new IDSImageSourceSettings(cameraName)); self.Bind().ToSelf().InSingletonScope(); return self; } @@ -26,6 +28,7 @@ public static class ModuleExtensions case CameraSettings.EImageSource.Hawkeye: break; case CameraSettings.EImageSource.IDS: + kernel.Bind().To().InSingletonScope(); break; case CameraSettings.EImageSource.Basler: break; diff --git a/VisionBuilder.UI.Camera/VisionBuilder.UI.Camera.csproj b/VisionBuilder.UI.Camera/VisionBuilder.UI.Camera.csproj index d29c8c2..149eabe 100644 --- a/VisionBuilder.UI.Camera/VisionBuilder.UI.Camera.csproj +++ b/VisionBuilder.UI.Camera/VisionBuilder.UI.Camera.csproj @@ -8,6 +8,7 @@ + diff --git a/VisionBuilder.UI.Common/Extensions.cs b/VisionBuilder.UI.Common/Extensions.cs index 312673c..c14a438 100644 --- a/VisionBuilder.UI.Common/Extensions.cs +++ b/VisionBuilder.UI.Common/Extensions.cs @@ -33,12 +33,13 @@ public static class Extensions { if (service != null) { + Log.Information($"Initializing module {service.GetType().Name}"); service.InitializeModule(); } } catch (Exception e) { - Log.Error($"Error initializing module {service.GetType().Name}: {e.Message}", e); + Log.Error($"Error initializing module {service.GetType().Name}: {e}", e); } } diff --git a/VisionBuilder.UI.Common/Processing/IImageSource.cs b/VisionBuilder.UI.Common/Processing/IImageSource.cs index 1a17427..7a34764 100644 --- a/VisionBuilder.UI.Common/Processing/IImageSource.cs +++ b/VisionBuilder.UI.Common/Processing/IImageSource.cs @@ -4,6 +4,5 @@ namespace VisionBuilder.UI.Common.RecipeProcessing; public interface IImageSource { - string GetName(); Task GetImage(CancellationToken token); } \ No newline at end of file diff --git a/VisionBuilder.UI.IOCommander.Windows/ModuleExtensions.cs b/VisionBuilder.UI.IOCommander.Windows/ModuleExtensions.cs index a9e421c..c4e572b 100644 --- a/VisionBuilder.UI.IOCommander.Windows/ModuleExtensions.cs +++ b/VisionBuilder.UI.IOCommander.Windows/ModuleExtensions.cs @@ -13,7 +13,6 @@ public static class ModuleExtensions public static IKernel UseIOCommanderWindowsDebug(this IKernel self) { TypeDescriptor.AddAttributes(typeof(List), new TypeConverterAttribute(typeof(PinLabelListConverter))); - self.Rebind().To().InSingletonScope(); self.Bind().ToConstant(new WindowsIOCommanderDebugViewSettings()); return self; diff --git a/VisionBuilder.UI.IOCommander/IOCommander.cs b/VisionBuilder.UI.IOCommander/IOCommander.cs index 85934ea..7c90ede 100644 --- a/VisionBuilder.UI.IOCommander/IOCommander.cs +++ b/VisionBuilder.UI.IOCommander/IOCommander.cs @@ -10,7 +10,6 @@ public class IOCommander : IIOCommander SerialPort _port; public IOCommander(string COMPort) { - _port = new SerialPort(COMPort); _port.BaudRate = 57600; _port.ReadTimeout = 100; diff --git a/VisionBuilder.UI.IOCommander/Modules/IOCommanderCameraModule.cs b/VisionBuilder.UI.IOCommander/Modules/IOCommanderCameraModule.cs index ae7ee25..a13ce58 100644 --- a/VisionBuilder.UI.IOCommander/Modules/IOCommanderCameraModule.cs +++ b/VisionBuilder.UI.IOCommander/Modules/IOCommanderCameraModule.cs @@ -28,7 +28,7 @@ public class IOCommanderCameraModule: IVisionBuilderModule _recognitionControl.ImageProcessed += _recognitionControl_ImageProcessed; _recognitionControl.SessionStarted += _recognitionControl_SessionStarted; _recognitionControl.SessionEnded += _recognitionControl_SessionEnded; - mainModule.OnPinChangedEvent += MainModule_OnPinChangedEvent; + } @@ -93,7 +93,7 @@ public class IOCommanderCameraModule: IVisionBuilderModule public void InitializeModule() { - + _mainModule.OnPinChangedEvent += MainModule_OnPinChangedEvent; } private void _recognitionControl_SessionEnded(SessionEndedEvent obj) diff --git a/VisionBuilder.UI.IOCommander/Modules/IOCommanderModule.cs b/VisionBuilder.UI.IOCommander/Modules/IOCommanderModule.cs index 2bbb91e..5b091c7 100644 --- a/VisionBuilder.UI.IOCommander/Modules/IOCommanderModule.cs +++ b/VisionBuilder.UI.IOCommander/Modules/IOCommanderModule.cs @@ -33,15 +33,12 @@ public class IOCommanderModule:IVisionBuilderModule } ConcurrentDictionary _pinStates = new ConcurrentDictionary(); - public event Action? OnPinChangedEvent - { - add => _ioCommander!.OnPinChanged += value; - remove => _ioCommander!.OnPinChanged -= value; - } + public event Action? OnPinChangedEvent = delegate { }; private void OnPinChanged(int arg1, bool arg2) { _pinStates.TryAdd(arg1, arg2); _debugViewService.SetInputPinStatus(arg1, arg2); + OnPinChangedEvent?.Invoke(arg1, arg2); } public void SetOutput(int pin, bool value) diff --git a/VisionBuilder.UI.Recipes.HawkeyeRecipe/HawkeyeRecognitionControl.cs b/VisionBuilder.UI.Recipes.HawkeyeRecipe/HawkeyeRecognitionControl.cs index f6f090c..7360b4d 100644 --- a/VisionBuilder.UI.Recipes.HawkeyeRecipe/HawkeyeRecognitionControl.cs +++ b/VisionBuilder.UI.Recipes.HawkeyeRecipe/HawkeyeRecognitionControl.cs @@ -25,11 +25,6 @@ namespace VisionBuilder.UI.Recipes.HawkeyeRecipe _loadingService = loadingService; } - public string GetCameraName() - { - return _imageSource.GetName(); - } - public List GetRecipesData() { _loadingService.StartLoading($"Loading recipes for {_recognitionConfiguration.CameraName}..."); @@ -171,7 +166,7 @@ namespace VisionBuilder.UI.Recipes.HawkeyeRecipe AnalysisTime = sw.Elapsed, ErrorNames = errorNames.ToList(), HasError = errorNames.Length > 0, - ImageSource = _imageSource.GetName(), + ImageSource = _recognitionConfiguration.CameraName, ImageOriginal = _workflow.Context.LastCameraImage.ImageData, ImageAnalysis = annotated }; diff --git a/VisionBuilder.UI.Windows.Test/Program.cs b/VisionBuilder.UI.Windows.Test/Program.cs index 329f0d6..aea810a 100644 --- a/VisionBuilder.UI.Windows.Test/Program.cs +++ b/VisionBuilder.UI.Windows.Test/Program.cs @@ -41,10 +41,10 @@ namespace VisionBuilder.UI.Windows.Test var mainKernel = Common.VisionBuilder.CreateMainKernel(settings); mainKernel + .UseConsole() .UseIOCommander([CAMERA1]) .UseIOCommanderWindowsDebug() .UseIOCommanderVirtualInput() - .UseConsole() .UseWindowsServices(); // CAMERA 1 // diff --git a/VisionBuilder.UI.Windows/Components/PreviewWindow.cs b/VisionBuilder.UI.Windows/Components/PreviewWindow.cs index 61f831e..7d6481d 100644 --- a/VisionBuilder.UI.Windows/Components/PreviewWindow.cs +++ b/VisionBuilder.UI.Windows/Components/PreviewWindow.cs @@ -1,4 +1,5 @@ -using OpenCvSharp.Extensions; +using OpenCvSharp; +using OpenCvSharp.Extensions; using VisionBuilder.UI.Common.ViewModel; namespace VisionBuilder.UI.Windows.Components; @@ -18,7 +19,15 @@ public class PreviewWindow:PictureBox switch (e.PropertyName) { case nameof(PreviewVM.ImagePreview): - var bmp = _previewVm.ImagePreview?.ToBitmap(); + var tmp = _previewVm.ImagePreview?.ToBitmap(); + var bmp = new Bitmap(_previewVm.ImagePreview?.Width ?? 0, _previewVm.ImagePreview?.Height ?? 0); + // hack to avoid IDS image glitch + using (Graphics g = Graphics.FromImage(bmp)) + { + g.Clear(Color.Pink); + g.DrawImage(tmp,0,0); + } + using (Graphics g = Graphics.FromImage(bmp)) { // draw red border around the image if it's an error @@ -31,6 +40,7 @@ public class PreviewWindow:PictureBox } } Image = bmp; + Invalidate(); break; } diff --git a/VisionBuilder.UI.sln b/VisionBuilder.UI.sln index 3c5a3c8..e7b750e 100644 --- a/VisionBuilder.UI.sln +++ b/VisionBuilder.UI.sln @@ -61,6 +61,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Utility", "Utility", "{250F EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VisionBuilder.UI.IOCommander.Windows", "VisionBuilder.UI.IOCommander.Windows\VisionBuilder.UI.IOCommander.Windows.csproj", "{254841AD-0706-4C92-A364-7E5DC17AF4AD}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hawkeye.VisionBuilder.UI.Sources.IDS", "Hawkeye.VisionBuilder.UI.Sources.IDS\Hawkeye.VisionBuilder.UI.Sources.IDS.csproj", "{86511F06-AABC-4F1D-AB4E-9763AE4A0EF2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{F2406FBB-DFD3-4CBE-9644-A9FFC2FCBB71}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IDSTest", "IDSTest\IDSTest.csproj", "{AD1A176C-B19F-4E60-9935-728722BAE3C1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -163,6 +169,14 @@ Global {254841AD-0706-4C92-A364-7E5DC17AF4AD}.Debug|Any CPU.Build.0 = Debug|Any CPU {254841AD-0706-4C92-A364-7E5DC17AF4AD}.Release|Any CPU.ActiveCfg = Release|Any CPU {254841AD-0706-4C92-A364-7E5DC17AF4AD}.Release|Any CPU.Build.0 = Release|Any CPU + {86511F06-AABC-4F1D-AB4E-9763AE4A0EF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {86511F06-AABC-4F1D-AB4E-9763AE4A0EF2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {86511F06-AABC-4F1D-AB4E-9763AE4A0EF2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {86511F06-AABC-4F1D-AB4E-9763AE4A0EF2}.Release|Any CPU.Build.0 = Release|Any CPU + {AD1A176C-B19F-4E60-9935-728722BAE3C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AD1A176C-B19F-4E60-9935-728722BAE3C1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AD1A176C-B19F-4E60-9935-728722BAE3C1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AD1A176C-B19F-4E60-9935-728722BAE3C1}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -185,6 +199,8 @@ Global {FBDCC120-B6B3-4A28-8845-DF1690F619C1} = {C828783C-1CE1-4245-8731-4AE18A28F590} {E0F75E78-F7FA-4B7D-BA5F-A8A91C0A9AD9} = {C828783C-1CE1-4245-8731-4AE18A28F590} {254841AD-0706-4C92-A364-7E5DC17AF4AD} = {250F2B27-FA2B-4CE6-BFDE-54D0B7FC9FAF} + {86511F06-AABC-4F1D-AB4E-9763AE4A0EF2} = {D05689E3-04C6-4E3B-ACA7-3F4507CED4CC} + {AD1A176C-B19F-4E60-9935-728722BAE3C1} = {F2406FBB-DFD3-4CBE-9644-A9FFC2FCBB71} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3CE42AE5-D79F-4E97-A246-AA8FD228B677} diff --git a/framework/Inspectron.Settings.Windows/Configuration/DefaultControlFactory.cs b/framework/Inspectron.Settings.Windows/Configuration/DefaultControlFactory.cs index e26b4ce..cc0da14 100644 --- a/framework/Inspectron.Settings.Windows/Configuration/DefaultControlFactory.cs +++ b/framework/Inspectron.Settings.Windows/Configuration/DefaultControlFactory.cs @@ -43,6 +43,10 @@ public class DefaultControlFactory : IControlFactory { return CreateListControl(name, description, type, initialValue, valueChangedCallback, optionsWindow, settingDescription, previewLabel, getPreview); } + else if (type == typeof(string) && propertyInfo.GetCustomAttribute()!=null) + { + return CreateFileControl(name, description, initialValue, valueChangedCallback, optionsWindow, settingDescription, previewLabel, getPreview, propertyInfo.GetCustomAttribute().Filter); + } else if (type == typeof(string) && name.EndsWith("Path", StringComparison.OrdinalIgnoreCase)) { return CreatePathControl(name, description, initialValue, valueChangedCallback, optionsWindow, settingDescription, previewLabel, getPreview); @@ -156,7 +160,36 @@ public class DefaultControlFactory : IControlFactory return CreateOuterPanel(settingDescription, previewLabel, label, textBox, browseButton); } + private Control CreateFileControl(string name, string description, object initialValue, Action valueChangedCallback, + OptionsWindow optionsWindow, string settingDescription, Label previewLabel, Func getPreview, string filter) + { + 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 {Filters = { new CommonFileDialogFilter(filter, filter) }}) + { + 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 valueChangedCallback, string settingDescription, Label previewLabel, Func getPreview) { diff --git a/framework/Inspectron.Settings/Attributes/FileAttribute.cs b/framework/Inspectron.Settings/Attributes/FileAttribute.cs new file mode 100644 index 0000000..4443fa9 --- /dev/null +++ b/framework/Inspectron.Settings/Attributes/FileAttribute.cs @@ -0,0 +1,15 @@ +using System; + +namespace Inspectron.Settings.Attributes +{ + [AttributeUsage(AttributeTargets.Property)] + public class FileAttribute:Attribute + { + public string Filter { get; } + + public FileAttribute(string filter) + { + Filter = filter; + } + } +} \ No newline at end of file