Getting issue with the dependency injection asp core 6 with winforms creation? - winforms

Github link to sample project
static void Main()
{
ApplicationConfiguration.Initialize();
var builder = new HostBuilder()
.ConfigureServices((hostContext, services) =>
{
services.AddTransient<Form1>();
services.AddTransient<Form2>();
});
var host = builder.Build();
using (var serviceScope = host.Services.CreateScope())
{
IServiceProvider services = serviceScope.ServiceProvider;
Application.Run(services.GetRequiredService<Form1>());
}
}
Form1 is MDI MdiParent where i am injecting Form 2
public partial class Form1 : Form
{
private readonly Form2 form2;
public Form1(Form2 form2)
{
InitializeComponent();
this.form2 = form2;
}
private void form2ToolStripMenuItem_Click(object sender, EventArgs e)
{
this.form2.MdiParent = this;
this.form2.Show();
}
}
When I Open Form2 by clicking from Menu it opens and close it by using [X] button
When i reopen it i am getting error

The form is disposed when closed.
I would suggest using a factory
static void Main() {
ApplicationConfiguration.Initialize();
var builder = new HostBuilder()
.ConfigureServices((hostContext, services) => {
services.AddTransient<Form1>();
services.AddTransient<Form2>();
//Form2 factory delegate
services.AddSingleton<Func<Form2>>(sp => () => sp.GetRequiredService<Form2>());
});
var host = builder.Build();
using (var serviceScope = host.Services.CreateScope()) {
IServiceProvider services = serviceScope.ServiceProvider;
Application.Run(services.GetRequiredService<Form1>());
}
}
to initialize a new form every time the button is clicked.
public partial class Form1 : Form {
private readonly Func<Form2> factory;
public Form1(Func<Form2> factory) {
InitializeComponent();
this.factory = factory;
}
private void form2ToolStripMenuItem_Click(object sender, EventArgs e) {
Form2 form2 = factory();
form2.MdiParent = this;
form2.Show();
}
}

Related

Navigation between Blazor Winforms pages

After a successfull experience with Blazor web server and Maui hybrid with Blazor, I'm now taking my first steps integrating my Blazor components in Winforms.
But I found no tutorials with samples of how to navigate between different pages/windows/forms.
Html links do not seem to work, and neither do NavigationManager:
Default page, app successfully loads it:
#inject NavigationManager NavigationManager
<div>
<button #onclick="OnClick">Go to page 2</button>
<a href="#" #onclick="#OnClick" #onclick:preventDefault>go to page 2</a>
<a href="page2" >go to page 2</a>
</div>
#code {
private void OnClick()
{
NavigationManager.NavigateTo("Page2");
}
}
Second page, no way to open it from previous one:
#page "/Page2"
<div>Wellcome to Page2</div>
Should I better navigate between Forms?
Update 1
I finally got it working having the component trigger an event on a form state class which instantiates a new form and shows it.
I guess I should better declare a singleton service with global appstate class in program.cs and hold all form instantiation from there, but I can't find the right way to declare it on a Blazor Winforms project.
My last version as follows, but appState is null when initializing Form1:
The singleton that communicates between Blazor compolnents and Winforms forms:
public class AppState
{
public event EventHandler<EventArgs>? GotoForm;
public void NavigateNext()
{
GotoForm?.Invoke(this,new EventArgs());
}
}
The component which feeds content to each form:
#inject AppState appState
<h3>Welcome to Form #FormNumber</h3>
<button #onclick="appState.NavigateNext">Goto page #AltFormNumber()</button>
#code {
[Parameter] public int FormNumber { get; set; }
int? AltFormNumber()=>FormNumber == null ? null : 3-FormNumber;
}
The default form:
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.WebView.WindowsForms;
using Microsoft.Extensions.DependencyInjection;
public partial class Form1 : Form
{
[Inject] AppState appState { get; }
public Form1()
{
InitializeComponent();
var services = new ServiceCollection();
services.AddWindowsFormsBlazorWebView();
//services.AddSingleton<FormState>();
blazorWebView1.HostPage = "wwwroot\\index.html";
blazorWebView1.Services = services.BuildServiceProvider();
//var appState = blazorWebView1.Services.GetRequiredService<AppState>();
appState.GotoForm += NavigateNext;
blazorWebView1.RootComponents.Add<Pages.FormComponent>("#app",
new Dictionary<string, object?> { { "FormNumber", 1 } });
}
private void NavigateNext(object? sender, EventArgs args)
{
var frm = new Form2();
frm.Show();
}
}
The alternative form:
public partial class Form2 : Form
{
[Inject] AppState appState { get; }
public Form2()
{
InitializeComponent();
var services = new ServiceCollection();
services.AddWindowsFormsBlazorWebView();
//services.AddSingleton<FormState>();
blazorWebView1.HostPage = "wwwroot\\index.html";
blazorWebView1.Services = services.BuildServiceProvider();
//var appState = blazorWebView1.Services.GetRequiredService<AppState>();
appState.GotoForm += NavigateNext;
blazorWebView1.RootComponents.Add<Pages.FormComponent>("#app",
new Dictionary<string, object?> { { "FormNumber", 2 } });
}
private void NavigateNext(object? sender, EventArgs args)
{
var frm = new Form1();
frm.Show();
}
}
The Program.cs:
internal static class Program
{
[STAThread]
static void Main()
{
ApplicationConfiguration.Initialize();
var host = CreateHostBuilder().Build();
ServiceProvider = host.Services;
Application.Run(new Form1());
}
public static IServiceProvider ServiceProvider { get; private set; }
static IHostBuilder CreateHostBuilder()
{
return Host.CreateDefaultBuilder()
.ConfigureServices((context, services) => {
services.AddSingleton<AppState>();
});
}
}

Windows Service can not open Windows Form

I am trying to create a Windows Service using TopShelf and within this service i want to launch a Windows Form.After i created the service and i debugged it calling ShowDialog the form does not show up:
Service
class SimpleServ {
private Task task;
private const string PATH = #"D:/out.txt";
private Logger logger;
private CancellationTokenSource src = new CancellationTokenSource();
public SimpleServ() {
logger = new Logger();
}
public void Start() {
logger.Log("Started");
this.task = Task.Run(async () => {
var fm = new Fm(logger);
while (true) {
fm.ShowDialog();
logger.Log("Just closed the dialog");
await Task.Delay(3000);
}
});
}
public void Stop() {
logger.Log("Stopped service");
}
}
Form
public partial class Fm : Form {
private Logger log;
public Fm(Logger log) {
this.log = log;
this.log.Log("From Form constructor");
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e) {
this.log.Log("Button clicked");
this.Close();
}
}
Main
class Program {
static void Main(string[] args) {
var exitCode = HostFactory.Run(x => {
x.Service<SimpleServ>(s => {
s.ConstructUsing(h => new SimpleServ());
s.WhenStarted(h => h.Start());
s.WhenStopped(h => h.Stop());
});
x.RunAsLocalSystem();
x.SetServiceName("SimpleService");
x.SetDisplayName("Simple Service");
x.SetDescription("Simple serv description");
});
int exitCodeValue = (int)Convert.ChangeType(exitCode, exitCode.GetTypeCode());
Environment.ExitCode = exitCodeValue;
}
}
I have attached myself to the service and after it reaches the line of ShowDialog nothing happens.
Update
I have also added a Logger to log all important events and so far , it seems the form opens but i can not see it:
Logger
public class Logger {
private string path;
public Logger(string logPath=Constants.PATH) {
this.path = logPath;
}
private object #lock = new object();
public void Log(string message) {
string formattedMessage = "Date:" + DateTime.Now.ToString() + "\tMessage:" + message;
File.AppendAllLines(this.path, new string[] { formattedMessage });
}
}
The output of the file is :
Date:6/12/2019 11:19:13 AM Message:Started
Date:6/12/2019 11:19:13 AM Message:From Form constructor
In a world where Session 0 Isolation -- an important security measure to prevent Shatter attacks -- is the law of the land, you should think very carefully about any design relying on service interaction.
A best practice is to restructure your solution to have:
A service that runs in the background, independently of the
user
A conventional GUI application that interacts with the service and
can be run by any user

How to automatically refresh listbox when add or remove the item in WPF?

I have WPF application that uses web service (asmx). The web service uses EF to get the data from MS SQL Server.
The code looks as following:
1) WPF:
public partial class MainWindow : Window
{
LetterWebServiceSoapClient _client = new LetterWebServiceSoapClient();
private ObservableCollection<Letter> _letters;
public MainWindow()
{
InitializeComponent();
}
private void cmdGetLetters_Click(object sender, RoutedEventArgs e)
{
lstLetters.ItemsSource = null;
_letters = _client.GetAllLetters();
lstLetters.ItemsSource = _letters;
}
private void cmdDeleteLetter_Click(object sender, RoutedEventArgs e)
{
_client.DeleteLetter((Letter)lstLetters.SelectedItem);
}
private void cmdAddLetter_Click(object sender, RoutedEventArgs e)
{
var newLetter = new Letter
{
Name = "Letter3",
Date = DateTime.Now,
Recipient = "John",
Sender = "David",
Content = "cccc"
};
_client.AddNewLetter(newLetter);
}
}
2) The web service:
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
// [System.Web.Script.Services.ScriptService]
public class LetterWebService : System.Web.Services.WebService
{
LetterDbEntities _letterDbEntities = new LetterDbEntities();
[WebMethod]
public ObservableCollection<Letter> GetAllLetters()
{
return new ObservableCollection<Letter>(_letterDbEntities.Letters.ToList());
}
[WebMethod]
public void AddNewLetter(Letter newLetter)
{
_letterDbEntities.Letters.Add(newLetter);
_letterDbEntities.SaveChanges();
}
[WebMethod]
public void DeleteLetter(Letter letter)
{
var letterToBeDeleted = _letterDbEntities.Letters.First(l => l.Id == letter.Id);
_letterDbEntities.Letters.Remove(letterToBeDeleted);
_letterDbEntities.SaveChanges();
}
}
When I add new letter or remove existing one they are added or removed on database level, but it doesn't reflect in UI that is in list box. What I'm missing?
You need to change the _letters collection. Try this:
private void cmdDeleteLetter_Click(object sender, RoutedEventArgs e)
{
var selectedItem = (Letter)lstLetters.SelectedItem;
_client.DeleteLetter(selectedItem);
_letters.Remove(selectedItem);
}
private void cmdAddLetter_Click(object sender, RoutedEventArgs e)
{
var newLetter = new Letter
{
Name = "Letter3",
Date = DateTime.Now,
Recipient = "John",
Sender = "David",
Content = "cccc"
};
_client.AddNewLetter(newLetter);
_letters.Add(newLetter);
}

WebKit.NET C# Custom Context Menu

I am implementing a Webkit Browser control in my windows app.
I need to use a custom context menu (right click) that only has copy/cut/paste as its options regardless of what element is right clicked. I need kind of a step-by-step as to how to implement it
Customizing the context menu for the WebKitBrowser supposes that you get a reference to the WebViewClass and then, setting a IWebUIDelegate for it by calling the setUIDelegate() method.
void MyWebBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
var webView = this.GetWebView() as WebKit.Interop.WebViewClass;
webView.setUIDelegate(new MyWebUIDelegate(this));
}
In the IWebUIDelegate implementation you may intercept the contextMenuItemsForElement method and trigger the display of the context menu of the browser.
Here is a working sample:
public partial class Form1 : Form
{
MyWebBrowser webKitBrowser;
public Form1()
{
InitializeComponent();
webKitBrowser = new MyWebBrowser();
webKitBrowser.Dock = DockStyle.Fill;
this.Controls.Add(webKitBrowser);
webKitBrowser.Navigate("http://www.google.com");
}
}
class MyContextMenu : ContextMenu
{
public MyContextMenu()
{
var cutMenuItem = new MenuItem("Cut");
var copyMenuItem = new MenuItem("Copy");
var pasteMenuItem = new MenuItem("Paste");
cutMenuItem.Click += cutMenuItem_Click;
MenuItems.Add(cutMenuItem);
MenuItems.Add(copyMenuItem);
MenuItems.Add(pasteMenuItem);
}
void cutMenuItem_Click(object sender, EventArgs e)
{
//TODO: implement functionality
MessageBox.Show("Cut was selected");
}
}
class MyWebBrowser : WebKitBrowser
{
public event EventHandler ShowContextMenu = new EventHandler(OnFireShowContextMenu);
public MyWebBrowser()
{
DocumentCompleted += MyWebBrowser_DocumentCompleted;
var myContextMenu = new MyContextMenu();
ContextMenu = myContextMenu;
}
void MyWebBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
var webView = this.GetWebView() as WebKit.Interop.WebViewClass;
webView.setUIDelegate(new MyWebUIDelegate(this));
}
public static void OnFireShowContextMenu(object sender, EventArgs e)
{
var webBrowser = (Control)sender;
var webView = (WebKit.Interop.WebViewClass)((MyWebBrowser)sender).GetWebView();
var originalPoint = webBrowser.PointToScreen(new Point(0, 0));
var currentPoint = new Point(Cursor.Position.X - originalPoint.X, Cursor.Position.Y - originalPoint.Y);
((WebKitBrowser)sender).ContextMenu.Show((Control)sender, currentPoint);
}
public void FireShowContextMenu()
{
this.ShowContextMenu(this, null);
}
}
class MyWebUIDelegate : IWebUIDelegate
{
private MyWebBrowser owner;
public MyWebUIDelegate(MyWebBrowser browser)
{
this.owner = browser;
}
//trigger the browser's FireShowContextMenu() method
public int contextMenuItemsForElement(WebView sender, CFDictionaryPropertyBag element, int defaultItemsHMenu)
{
owner.FireShowContextMenu();
return defaultItemsHMenu;
}
//return 1, true
public int hasCustomMenuImplementation()
{
return 1;
}
//the rest of the IWebUIDelegate interface implementation
}
For more insight, probably you would want to study some other customizations, such as open-webkit-sharp.

Caliburn.Micro and WebServiceResult

I'm looking for the correct version of this class for Caliburn.Micro
public class WebServiceResult : IResult where T : new()
The above signature is from the ContactManager example in the full Caliburn framework.
It does not cut and paste directly into a Micro-based project. There are too many missing classes to use this directly. Thoughts? or anyone know of the replacement?
Event though the underlying infrastructure is very different in Caliburn Micro (which is based on System.Windows.Interactivity), the concepts are pretty much the same.
Here is the CM version:
public class WebServiceResult<T, K> : IResult
where T : new()
where K : EventArgs
{
readonly static Func<bool> ALWAYS_FALSE_GUARD= () => false;
readonly static Func<bool> ALWAYS_TRUE_GUARD = () => true;
private readonly Action<K> _callback;
private readonly Expression<Action<T>> _serviceCall;
private ActionExecutionContext _currentContext;
private Func<bool> _originalGuard;
public WebServiceResult(Expression<Action<T>> serviceCall)
{
_serviceCall = serviceCall;
}
public WebServiceResult(Expression<Action<T>> serviceCall, Action<K> callback)
{
_serviceCall = serviceCall;
_callback = callback;
}
public event EventHandler<ResultCompletionEventArgs> Completed = delegate { };
public void Execute(ActionExecutionContext context)
{
_currentContext = context;
//if you would to disable the control that caused the service to be called, you could do this:
ChangeAvailability(false);
var lambda = (LambdaExpression)_serviceCall;
var methodCall = (MethodCallExpression)lambda.Body;
var eventName = methodCall.Method.Name.Replace("Async", "Completed");
var eventInfo = typeof(T).GetEvent(eventName);
var service = new T();
eventInfo.AddEventHandler(service, new EventHandler<K>(OnEvent));
_serviceCall.Compile()(service);
}
public void OnEvent(object sender, K args)
{
//re-enable the control that caused the service to be called:
ChangeAvailability(true);
if (_callback != null)
_callback(args);
Completed(this, new ResultCompletionEventArgs());
}
private void ChangeAvailability(bool isAvailable)
{
if (_currentContext == null) return;
if (!isAvailable) {
_originalGuard = _currentContext.CanExecute;
_currentContext.CanExecute = ALWAYS_FALSE_GUARD;
}
else if (_currentContext.CanExecute == ALWAYS_FALSE_GUARD) {
_currentContext.CanExecute = _originalGuard ?? ALWAYS_TRUE_GUARD;
}
_currentContext.Message.UpdateAvailability();
}
}

Resources