Mayka
Mayka
CC#
Created by Mayka on 2/21/2024 in #help
✅ Lag after closing folder dialog in console application
I need to raise a folder dialog within a console application. It works if I add the [STAThread attribute to the main method. However, there’s consistently a 3-5 second delay after the folder dialog is closed. This Stack Overflow question bring up a similar issue for OpenFileDialog but without a concrete resolution. https://stackoverflow.com/questions/41894044/closing-a-openfiledialog-causes-application-to-hang-for-3-4-seconds Is there any way to remove or minimize the delay after the folder dialog is closed? Project Settings
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0-windows</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <UseWPF>true</UseWPF>
  </PropertyGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0-windows</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <UseWPF>true</UseWPF>
  </PropertyGroup>
</Project>
Code
using Microsoft.Win32;
 
internal class Program
{
    [STAThread]
    private static void Main()
    {
        OpenFolderDialog folderDialog = new();
 
        if (folderDialog.ShowDialog() is true)
        {
            string mainFolderPath = folderDialog.FolderName;
            Console.WriteLine($"Selected folder: {mainFolderPath}");
        }
 
        Console.WriteLine("\nPress any key to continue...");
        Console.ReadKey();
    }
}
using Microsoft.Win32;
 
internal class Program
{
    [STAThread]
    private static void Main()
    {
        OpenFolderDialog folderDialog = new();
 
        if (folderDialog.ShowDialog() is true)
        {
            string mainFolderPath = folderDialog.FolderName;
            Console.WriteLine($"Selected folder: {mainFolderPath}");
        }
 
        Console.WriteLine("\nPress any key to continue...");
        Console.ReadKey();
    }
}
18 replies
CC#
Created by Mayka on 2/12/2024 in #help
Properly locking the console in async code
I made an async progress bar which sits at the bottom of the console. It still needs tweaking, but I’m running into issues truly locking the console during the specific moment that the progress bar is being written to the bottom of the screen. In order to restore the initial cursor position after updating the progress bar, I’m storing the cursor position before it to the bottom of the console. However, it seems that the console is still able to be written to during the “locked” portion of the task. This causes lines to be overwritten, because the stored cursor position is no longer accurate. How do I force a true lock in this case?
using ProgressBar;

Task task = Task.Run(async () =>
{
   ConsoleProgressBar progressBar = new(100);

   for (int i = 0; i < 100; i++)
   {
       await progressBar.IncrementAsync();
       Thread.Sleep(50);
   }
});

for (int i = 0; i <= 20; i++)
{
   Thread.Sleep(100);
   Console.WriteLine($"Message {i}");
}

await task;
Console.ReadKey();

namespace ProgressBar
{
   internal class ConsoleProgressBar(int total = 100)
   {
       private static readonly SemaphoreSlim SemaphoreSlim = new(1, 1);

       private int _current;
       private int _progressBarCursorRow = Console.WindowHeight - 1;

       public async Task IncrementAsync(int? value = null)
       {
           await SemaphoreSlim.WaitAsync();

           try
           {
               await Task.Run(() => IncrementProgressBar(value));
           }
           finally
           {
               SemaphoreSlim.Release();
           }
       }

       private void IncrementProgressBar(int? value)
       {
           int incrementValue = value ?? 1;
           _current += incrementValue;

           (int cursorLeft, int cursorTop) = Console.GetCursorPosition();
           Console.SetCursorPosition(0, _progressBarCursorRow);
           Console.Write(new string(' ', Console.BufferWidth - 1));
           Console.SetCursorPosition(cursorLeft, cursorTop);

           if (cursorTop < _progressBarCursorRow)
           {
               Console.SetCursorPosition(0, Console.WindowHeight - 1);
           }
           else
           {
               Console.SetCursorPosition(0, cursorTop + 1);
           }

           _progressBarCursorRow = Console.CursorTop;

           Draw();
           Console.SetCursorPosition(cursorLeft, cursorTop);
       }

       private void Draw()
       {
           Console.Write("[");

           int percent = (int)(_current * 100 / total);
           int p = (int)((percent / 10f) + .5f);

           for (int i = 0; i < 10; ++i)
           {
               Console.Write(i >= p ? ' ' : '■');
           }

           Console.Write("] {0,3:##0}%", percent);
       }
   }
}
using ProgressBar;

Task task = Task.Run(async () =>
{
   ConsoleProgressBar progressBar = new(100);

   for (int i = 0; i < 100; i++)
   {
       await progressBar.IncrementAsync();
       Thread.Sleep(50);
   }
});

for (int i = 0; i <= 20; i++)
{
   Thread.Sleep(100);
   Console.WriteLine($"Message {i}");
}

await task;
Console.ReadKey();

namespace ProgressBar
{
   internal class ConsoleProgressBar(int total = 100)
   {
       private static readonly SemaphoreSlim SemaphoreSlim = new(1, 1);

       private int _current;
       private int _progressBarCursorRow = Console.WindowHeight - 1;

       public async Task IncrementAsync(int? value = null)
       {
           await SemaphoreSlim.WaitAsync();

           try
           {
               await Task.Run(() => IncrementProgressBar(value));
           }
           finally
           {
               SemaphoreSlim.Release();
           }
       }

       private void IncrementProgressBar(int? value)
       {
           int incrementValue = value ?? 1;
           _current += incrementValue;

           (int cursorLeft, int cursorTop) = Console.GetCursorPosition();
           Console.SetCursorPosition(0, _progressBarCursorRow);
           Console.Write(new string(' ', Console.BufferWidth - 1));
           Console.SetCursorPosition(cursorLeft, cursorTop);

           if (cursorTop < _progressBarCursorRow)
           {
               Console.SetCursorPosition(0, Console.WindowHeight - 1);
           }
           else
           {
               Console.SetCursorPosition(0, cursorTop + 1);
           }

           _progressBarCursorRow = Console.CursorTop;

           Draw();
           Console.SetCursorPosition(cursorLeft, cursorTop);
       }

       private void Draw()
       {
           Console.Write("[");

           int percent = (int)(_current * 100 / total);
           int p = (int)((percent / 10f) + .5f);

           for (int i = 0; i < 10; ++i)
           {
               Console.Write(i >= p ? ' ' : '■');
           }

           Console.Write("] {0,3:##0}%", percent);
       }
   }
}
16 replies
CC#
Created by Mayka on 2/9/2024 in #help
ILogger Dependency Injection for Libraries
The below is what I came up with to inject a logger into a library, but something feels off. Can anyone advise if something in my approach needs tweaking?
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Serilog;
using ILogger = Microsoft.Extensions.Logging.ILogger;

namespace LogInjection;

class Program
{
static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console(
outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3} {SourceContext}] {Message:lj}{NewLine}{Exception}"
)
.CreateLogger();

using ServiceProvider serviceProvider = new ServiceCollection()
.AddLogging(loggingBuilder => loggingBuilder.AddSerilog(dispose: true))
.AddSingleton<FooFactory>()
.BuildServiceProvider();

var fooFactory = serviceProvider.GetRequiredService<FooFactory>();
fooFactory.CreateFoo("red").DoSomething();
fooFactory.CreateFoo("blue").DoSomething();
}
}

// Library code here
public class FooService { }

public class FooFactory(ILogger<FooService>? logger = null)
{
private readonly ILogger _logger = logger ?? NullLogger<FooService>.Instance;
public IFoo CreateFoo(string type)
{
_logger.LogInformation("Creating a new {Type}", type);
return type switch
{
"red" => new FooRed(_logger),
"blue" => new FooBlue(_logger),
_ => throw new ArgumentException("Unknown type", nameof(type)),
};
}
}

public interface IFoo
{
void DoSomething();
}

internal class FooRed(ILogger logger) : IFoo
{
public void DoSomething()
{
logger.LogInformation("I'm red!");
}
}

internal class FooBlue(ILogger logger) : IFoo
{
public void DoSomething()
{
logger.LogInformation("I'm blue!");
}
}
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Serilog;
using ILogger = Microsoft.Extensions.Logging.ILogger;

namespace LogInjection;

class Program
{
static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console(
outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3} {SourceContext}] {Message:lj}{NewLine}{Exception}"
)
.CreateLogger();

using ServiceProvider serviceProvider = new ServiceCollection()
.AddLogging(loggingBuilder => loggingBuilder.AddSerilog(dispose: true))
.AddSingleton<FooFactory>()
.BuildServiceProvider();

var fooFactory = serviceProvider.GetRequiredService<FooFactory>();
fooFactory.CreateFoo("red").DoSomething();
fooFactory.CreateFoo("blue").DoSomething();
}
}

// Library code here
public class FooService { }

public class FooFactory(ILogger<FooService>? logger = null)
{
private readonly ILogger _logger = logger ?? NullLogger<FooService>.Instance;
public IFoo CreateFoo(string type)
{
_logger.LogInformation("Creating a new {Type}", type);
return type switch
{
"red" => new FooRed(_logger),
"blue" => new FooBlue(_logger),
_ => throw new ArgumentException("Unknown type", nameof(type)),
};
}
}

public interface IFoo
{
void DoSomething();
}

internal class FooRed(ILogger logger) : IFoo
{
public void DoSomething()
{
logger.LogInformation("I'm red!");
}
}

internal class FooBlue(ILogger logger) : IFoo
{
public void DoSomething()
{
logger.LogInformation("I'm blue!");
}
}
75 replies
CC#
Created by Mayka on 1/8/2024 in #help
WPF Data Binding Shows Blank
I would like to have an AppState class within my WPF apps that can hold common properties I may want to access throughout the application, such as the assembly’s version number. When I tried to create an instance of this class within my Main Window and then access one of the instance’s properties via a data binding, it’s just showing blank. I know the property itself is set since I can set the window title via code behind to this property, so I’m a bit stumped why nothing populates when I try to access the instance via a binding. Can anyone shed some light on what I may be missing? See attached for a skeleton example.
9 replies
CC#
Created by Mayka on 12/9/2023 in #help
Dependency Injection - Data Exporter
I’ve been trying to wrap my head around dependency injection, but I’m struggling to bridge the gap between written explanations and putting the paradigm into practice within my own code. I need to export data from an Access database to an Excel spreadsheet using Interop (the OLE DB driver isn't an option here, unfortunately), but the goal is to eventually get the data out of Access and into a better format like perhaps a SQL Server database. To make the code more flexible for these changes in the future, I tried to implement a general IExporter interface that could take any kind of input and export the data to any kind of output. Would anyone be able to offer any feedback on how I could improve the below? It feels like my implementation is a bit off design-wise. IDataExporter.cs:
namespace MyApp
{
   public interface IDataExporter
   {
       void Export();
   }
}
namespace MyApp
{
   public interface IDataExporter
   {
       void Export();
   }
}
DataExporter.cs:
namespace MyApp
{
   public class DataExporter
   {
       private readonly IDataExporter exporter;

       public DataExporter(IDataExporter dataExporter)
       {
           exporter = dataExporter;
       }

       public void Export()
       {
           exporter.Export();
       }
   }
}
namespace MyApp
{
   public class DataExporter
   {
       private readonly IDataExporter exporter;

       public DataExporter(IDataExporter dataExporter)
       {
           exporter = dataExporter;
       }

       public void Export()
       {
           exporter.Export();
       }
   }
}
5 replies