WPF pass parameter to page with factory delegate - wpf

I dont how to pass parameter to new page, I have factory delegate that help me with dependecy injection but I also want to pass parameter throught Navigate service.
App.xml
private void ConfigureServices(ServiceCollection serviceCollection)
{
serviceCollection.AddTransient<Home>();
serviceCollection.AddSingleton<Func<Home>>(ServiceProvider => ServiceProvider.GetService<Home>);
serviceCollection.AddTransient<PrinterDetails>();
serviceCollection.AddSingleton<Func<PrinterDetails>>(ServiceProvider => ServiceProvider.GetService<PrinterDetails>);
serviceCollection.AddTransient<ProvinceList>();
serviceCollection.AddSingleton<Func<ProvinceList>>(ServiceProvider => ServiceProvider.GetService<ProvinceList>);
serviceCollection.AddSingleton(Configuration);
serviceCollection.AddTransient<ISqlDataAccess, SqlDataAccess>();
serviceCollection.AddTransient<IProvinceListViewModel, ProvinceListViewModel>();
serviceCollection.AddTransient(typeof(MainWindow));
}
I navigate from page PrinterDetails to page ProvinceList using NavigationService(when i click btnProvince_Click())
public partial class PrinterDetails : Page, INotifyPropertyChanged
{
private Func<ProvinceList> ProvinceListFactory { get; }
public PrinterDetails(Func<ProvinceList> provinceListFactory)
{
InitializeComponent();
DataContext = this;
PrinterService printerService = new PrinterService();
cmbPrinterList.ItemsSource = printerService.PrinterList();
ProvinceListFactory = provinceListFactory;
}
private void btnProvince_Click(object sender, RoutedEventArgs e)
{
ProvinceList nextPage = this.ProvinceListFactory.Invoke();
NavigationService.Navigate(nextPage);
}
}
ProvinceList page that i want to pass parameter
public partial class ProvinceList : Page
{
private readonly IProvinceListViewModel _provinceListViewModel;
public ProvinceList(IProvinceListViewModel provinceListViewModel)
{
InitializeComponent();
_provinceListViewModel = provinceListViewModel;
GetProvinceList();
}
private void GetProvinceList()
{
//How to get parameter from previous page
}
}

You can implement a public property on ProvinceList. Then PrinterDetails can set this property after it has created the instance using the factory:
private void btnProvince_Click(object sender, RoutedEventArgs e)
{
double parameter = 4.5;
ProvinceList nextPage = this.ProvinceListFactory.Invoke();
nextPage.Value = parameter;
NavigationService.Navigate(nextPage);
}
Alternatively, you can add this parameter to the constructor of ProvinceList and then configure the IoC container to inject the parameter. If the parameter is a dynamic result you must configure your factory to accept this parameter.
Register the factory delegate that accepts a parameter e.g. Func<IProvinceListViewModel, double, ProvinceList>, which is used to construct the type:
// This example assumes that the constructor of ProvinceList
// requests two parameters of type IProvinceListViewModel and double
serviceCollection.AddSingleton<Func<double, ProvinceList>>(serviceProvider => doubleParameter =>
{
IProvinceListViewModel viewModel = serviceProvider.GetService<IProvinceListViewModel>();
return new ProvinceList(viewModel, doubleParameter);
});
serviceCollection.AddTransient<IProvinceListViewModel, ProvinceListViewModel>();
And use the factory as follows:
public PrinterDetails(Func<double, ProvinceList> provinceListFactory)
{
InitializeComponent();
ProvinceListFactory = provinceListFactory;
}
private void btnProvince_Click(object sender, RoutedEventArgs e)
{
double constructorParameter = 4.5;
ProvinceList nextPage = this.ProvinceListFactory.Invoke(constructorParameter);
NavigationService.Navigate(nextPage);
}
Answer to follow up question:
// This example assumes that the constructor of ProvinceList
// requests two parameters of type IProvinceListViewModel and double
serviceCollection.AddSingleton<Func<double, ProvinceList>>(serviceProvider => doubleParameter =>
{
IProvinceListViewModel viewModel = serviceProvider.GetService<IProvinceListViewModel>();
Func<POPList> popListFactory = serviceProvider.GetService<Func<POPList>>();
return new ProvinceList(viewModel, doubleParameter, popListFactory);
});
serviceCollection.AddTransient<IProvinceListViewModel, ProvinceListViewModel>();
serviceCollection.AddTransient<POPList>();
serviceCollection.AddSingleton<Func<POPList>>(serviceProvider => serviceProvider.GetService<POPList>);
private void btnProvince_Click(object sender, RoutedEventArgs e)
{
double constructorParameter = 4.5;
ProvinceList nextPage = this.ProvinceListFactory.Invoke(constructorParameter);
NavigationService.Navigate(nextPage);
}

Related

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

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();
}
}

How to pass StartupEventArgs to other ViewModels in Prism application

We are using Prism 7. Are there any best practices to pass StartupEventArg parameters obtained
from the Prism OnStartup method of App.xaml.cs to other ViewModels. The Event Aggregator is not available in this method so it looks like we can't use this method of passing data to viewmodels.
Thanks
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
if (e.Args.Length > 0)
{
UriBuilder builder = new UriBuilder(e.Args[0]);
var result = HttpUtility.ParseQueryString(builder.Query);
var username = result["username"];
var password = result["password"];
// how to get these to viewmodels
}
}
how to get these to viewmodels?
You create a service that provides the raw arguments to anyone interested, most likely another service that parses them into user name and password.
Example:
internal class EnvironmentCommandLineArgumentsProvider : ICommandLineArgumentsProvider
{
#region ICommandLineArgumentsProvider
public IReadOnlyList<string> Arguments => _arguments.Value;
#endregion
#region private
private readonly Lazy<IReadOnlyList<string>> _arguments = new Lazy<IReadOnlyList<string>>( () => Environment.GetCommandLineArgs() );
#endregion
}
internal class CommandLineInitialCredentialsProvider : IInitialCredentialsProvider
{
public CommandLineInitialCredentialsProvider( ICommandLineArgumentsProvider commandLineArgumentsProvider )
{
_credentials = new Lazy<(string UserName, string Password)>( () =>
{
if (commandLineArgumentsProvider.Arguments.Count > 0)
{
var builder = new UriBuilder(commandLineArgumentsProvider.Arguments[0]);
var result = HttpUtility.ParseQueryString(builder.Query);
return (result["username"], result["password"]);
}
return (null, null);
});
}
#region IInitialCredentialsProvider
public string UserName => _credentials.Value.UserName;
public string Password => _credentials.Value.Password;
#endregion
#region private
private readonly Lazy<(string UserName, string Password)> _credentials;
#endregion
}

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