WebView2 and MVVM in WPF - PostWebMessage - c#

I have created a web application using angular and now I want to load it in a WPF application using WebView2 (not electron). That all works well. However, I also want to be able to send messages via the WebView2 using:
webView.CoreWebView2.PostWebMessageAsString(message)
I also want to adhere to MVVM. I've created a View:
{
public AngularView()
{
InitializeComponent();
InitializeAsync();
}
public void PostMessage(string message)
{
webView.CoreWebView2.PostWebMessageAsString(message);
}
private async void InitializeAsync()
{
await webView.EnsureCoreWebView2Async();
}
}
And an AngularViewModel. Now I'd like to call PostMessage(message) from my ViewModel when a ICommand is triggered, like when button is pushed on the UI. I know that the ViewModel shouldn't know about the View, so how is the best way to go about this?
Furthermore, I'd also like to have moved my PostMessage(message) method to a WebViewService.cs class, but again, how does the service know about the CoreWebView2 property of the webView in the AngularView.

You should abstract away the view/control, for example by introducing an interface that the view implements:
public interface IWebComponent
{
void Post(string message);
}
You could then inject the view model with this interface:
public AngularViewModel(IWebComponent webComponent)
{
_webComponent = webComponent;
}
In this case the view model knows only about an interface and doesn't have any dependency upon the actual view implementation. And you can easily mock the interface in your unit tests and benefit from everything else that the MVVM design pattern brings.

Related

How to implement Constructor injection into Xamarin Forms App?

I would like to create a Xamarin app that makes use of the dependency injection pattern. My current problem is that I just cannot get it to work with xamarin in any other way than as a dependency locator anti-pattern, while I want an actual constructor injection. I have tried everything I could find, from Prism templates to tinyIoC, but the ViewModels and Views simply require a parameterless constructor. What am I missing?
The following are some sources that were helpful but did not resolve my issue
Xamarin DependencyService with Parameterized Constructor
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/enterprise-application-patterns/dependency-injection
https://github.com/windows-toolkit/MVVM-Samples
My app is a fresh xamarin forms template generated by VS2019, I only added a few libraries but those should not interfere in any way. Xamarin clearly provides a built-in dependency locator, but that is not what I wanted to use. I also saw a few templates that achieved what I wanted, but they were too big for me to find out how exactly they achieved that.
Ideally, I would like to use Microsoft MVVM Toolkit, but that is not a strict requirement. I would also like the implementation to be as simple as possible, as I am just starting with xamarin development.
An example of what I would like to achieve:
public class AboutViewModel : BaseViewModel
{
public AboutViewModel(ITest testService)
{
Title = "About";
OpenWebCommand = new Command(async () => await Browser.OpenAsync("https://aka.ms/xamarin-quickstart"));
}
public ICommand OpenWebCommand { get; }
}
public interface ITest
{
void Test();
}
class TestService : ITest
{
public void Test()
{
Debug.WriteLine("Test successful");
}
}
Ok, I found out I had this in my AboutPage.xaml
<ContentPage.BindingContext>
<vm:AboutViewModel />
</ContentPage.BindingContext>
Seems like a simple way to initialize viewModel in view except it can only accept parameterless constructors. That is what was causing my problems. Moving the ViewModel initialization into the code behind solved everything, though I would still prefer to just pass it through codebehind constructor.
This is the result now:
public AboutPage()
{
InitializeComponent();
BindingContext = Ioc.Default.GetRequiredService<AboutViewModel>();
}

MVP in windows forms

I want to implement MVPO pattern in my windows forms application. As you know there is 1:1 means there is one presenter for one view.
So basicly we can have:
public class MainPresenter
{
IMainView _mainView;
public MainPresenter(IMainView mainView)
{
_myView = myView;
}
}
My question is can one presenter use other presenter so for instance:
public class MainPresenter
{
IMainView _mainView;
ISomeOtherPresenter _otherPresenter;
public MainPresenter(IMainView mainView, IOtherPresenter otherPresenter)
{
_mainView = myView;
_otherPresenter = otherPresenter;
}
}
As a reference i went through video tutorial by Robert Marquez. At his 10 video serie at 10th one he used one rpesenter inside other one
Reference link
at 35:39 you will see his MainPresenter has dependency to IHelpAboutPresenter.
To me it's logical because if HelpAboutView (form) can be opened by button from MainView it's logical MainPresenter has to have access to HelpAboutPresenter which was presented on the video. If not that way how to do so because all the people saying 1:1 and here we have 1 presenter uses other presenter.
It is a bit of a code small to has a cross dependency like that. If you need functionality from another presenter, it is likely the presenters have something in common. So perhaps they should derive from the same base class. Then they can easily share logic without complicating your dependency graph.
public class BasePresenter
{
protected void SharedMethod()
{
//Code that you need to call from both presenters
}
}
public class MainPresenter : BasePresenter
{
IMyView _myView;
public MainPresenter(IMyView myView)
{
_myView = myView;
}
private Foo()
{
SharedFunction();
}
}
public class OtherPresenter : BasePresenter
{
public OtherPresenter(IMyView myView) : base()
{
_myView = myView;
}
private Bar()
{
SharedMethod();
}
}
I would assert that there isn't anything inherently wrong with the approach although one could probably go about it another way.
The "main" view could be acting more along the lines of a front controller. The pattern is more often associated with web-applications although it may still be applicable to desktop applications. For instance, the main MDI view may be the front controller.
Typically a front controller would have some dispatching mechanism to have individual requests (such as menu clicks in a desktop application, or url-based resources in a web-based application) handled by a specific request handler. There is therefore a slight indirection that decouples the front controller from the specific implementation.
In the case of the linked video the relationship is just a bit more coupled in that the About box request (if I understand correctly) is being handled not by a specific request handler but by the front controller.
What makes this appear odd is the fact that the front controller is implemented using the MVP pattern.

(WPF/MVVM) What's the different between an IService and ViewModel?

I wanted to use SaveFileDialog in my ViewModel, But since it's not correct that bind to a View from ViewModel, I searched for ways to do that. But I found a few answers that doesn't completely separate View form ViewModel, Like this:
public interface IOService
{
void IMessageBox(string Message);
string ISaveFileDialog(string DefaultPath);
}
public class IDialog : IOService
{
public void IMessageBox(string Message)
{
System.Windows.MessageBox.Show(Message);
}
public string ISaveFileDialog(string DefaultPath)
{
System.Windows.Forms.SaveFileDialog dg = new SaveFileDialog
{
InitialDirectory = DefaultPath,
Filter = "PDF files (*.pdf) | *.pdf"
};
dg.ShowDialog();
if (dg.FileName == null)
dg.FileName = string.Empty;
return dg.FileName;
}
}
They said that, this is a Service and using it will separate View from ViewModel. But we have make an Instance from this in ViewModel:
IDialog iDialog = new IDialog();
So I wanna know, What's the diffrence between this method and calling MessageBox or SaveFileDialog from ViewModel directly?
Note: Also I find something that said I could use a Service like the above, But implement it like this:
public class ExportViewModel : BaseViewModel
{
IOService _IOService;
public ExportViewModel(IOService ioservice)
{
_IOService = ioservice;
.
.
}
}
But I don't know how send IOService as a parameter to ExportViewModel (Because we can't create Instances from an Interface!)
You shouldn't pop up dialogs directly from your VM for automated testability.
If you call MessageBox.Show(), your test will get stuck until a person closes the dialog.
If, instead, you use "IMessageBox", for unit tests, you can inject an implementation that doesn't actually show the dialog, but rather returns a specific value (the result).
It's an abstraction used to separate UI concerns from the view model, and to allow you a means to intercept these calls within unit tests. Intercepting these calls allows you to both prevent showing a dialog during a test (which will block execution of your tests, not good) and verify the view model is acting as expected.
There is nothing special about calling it a "Service" or calling the abstraction "IService". It's just an allusion to a common pattern
I need to do something
I can't do it myself
I'll find a service I can hire to do it
I found a service that can do this for me
This service will do it for me
You could just as well call it "IDontTouchUIStuffInMuhViewModel", but that's not as elegant.
Your second problem is solved trivially at runtime by providing an implementation of the interface to your view model. Your view model shouldn't care how it's implemented (polymorphism), so you can pass in a legit implementation at runtime and a fake one during testing.
You can also accomplish this by using Dependency Injection or Inversion of Control. By using a library such as Unity to instantiate your view models, all dependencies are automatically provided, based on how you configured the container.

Opening new window from ViewModel

How should I be opening new windows? I'm currently doing the following.
EventArgs:
public class GenericViewRequestedEventArgs : EventArgs
{
public GenericViewModel ViewModel { get; private set; }
public GenericViewRequestedEventArgs(GenericViewModel viewModel)
{
ViewModel = viewModel;
}
}
ViewModel:
public class MainWindowViewModel : ViewModelBase
{
private RelayCommand _viewSpecificCommand;
public ICommand ViewSpecificCommand
{
get
{
if (_viewSpecificCommand == null)
_viewSpecificCommand = new RelayCommand(x => viewSpecific());
return _viewSpecificCommand;
}
}
public EventHandler<GenericViewRequestedEventArgs> GenericViewRequested;
private void RaiseGenericViewRequested(GenericViewModel viewModel)
{
var handler = GenericViewRequested;
if (handler != null)
handler(this, new GenericViewRequestedEventArgs(viewModel));
}
private void viewSpecific()
{
RaiseGenericViewRequested(_specificViewModel);
}
}
View:
public partial class MainWindow : Window
{
private void OnGenericViewRequested(object sender, GenericViewRequestedEventArgs e)
{
GenericWindow window = new GenericWindow(e.ViewModel);
window.ShowDialog();
}
}
This does work, but it seems like a lot of code and I end up with code behind in my view any ways.
What's the logic behind sending the command to the viewmodel at all?
Is it just to optionally use the predicate(if so why not bind to Enabled) and possibly avoid exposing additional viewmodels as properties?
Should I be attaching simple event handlers in the XAML(e.g. Click="btnViewSpecific_Click")?
It depends on how "strict" you want to follow the MVVM pattern. This is just one of the basic pitfalls of MVVM and you can solve it depending on your preferences. One way is to simply use the code-behind, but then how will you handle application-wide commands, keyboard shortcuts, etc? It is a bit too short-sighted IMHO.
I think you should at least consider using existing frameworks that have solved these issues for you years ago and will provide you with a solid base for your application.
For example, Catel has a IUIVisualizerService that can show windows based on a view model. The major advantage is that you can push data into the view model and respond to the result as a service. Another nice advantage is that you can mock the IUIVisualizerService so you can test the reacting code on different results provided by the dialog.
** Disclaimer **
I am the developer of Catel, so I have explained the Catel way here. If anyone else wants to comment on other frameworks, feel free to comment or create a new answer.
Yes, there are a lot of additional codes for MVVM. Building a command that independent of Views is usually for unit testing, such that the command and ViewModel can be unit tested without involving UI components.
However, if the "command" is just opening a window, it is not worth to create a command, and unit test the command to see if the GenericViewRequested is really fired(you can even check if the correct _specificViewModel is returned). The codes are far more complicated and just little value is added. Just open the window in View's button click event handler and it is fine.
If you want to see good example, see how this works in the ViewModel (EmailClient) sample application of the WPF Application Framework (WAF).

Caliburn ShowDialog and MessageBox

I'm making a small demo application for MVVM with caliburn.
Now I want to show a MessageBox, but the MVVM way.
For dialogs I created an event, that is handled in the ShellView (the root view)
and just calls WindowManager.ShowDialog with a Dialogs ViewModel type.
Seems to stick to MVVM for me.
But what is the way to show a messagebox and get its result (Okay or cancel)?
I already saw this question, but it contains no answer either.
Mr Eisenberg hisself answers with
"Caliburn has services built-in for calling custom message boxes."
Can anyone tell what he means with that? I don't see it in the samples.
As you mentioned, you just prepare the view model (e.g. ConfirmationBoxViewModel) and an appropriate view. You'll have to create two actions (after inheriting the view model from Screen, which is necessary to use TryClose. You can always implement IScreen instead, but that would be more work):
public void OK()
{
TryClose(true);
}
public void Cancel()
{
TryClose(false);
}
and then in your other view model:
var box = new ConfirmationBoxViewModel()
var result = WindowManager.ShowDialog(box);
if(result == true)
{
// OK was clicked
}
Notice that after the dialog closes, you can access the view model properties if you need to pull additional data from the dialog (e.g. Selected item, display name etc).
In the article A Billy Hollis Hybrid Shell (written by the framework coordinator) the author showed a nice way to handle both dialog and message boxes, but he used dependency injection (you can go without DI of course but it makes things simpler). The main idea is that you can let your main window, the one used as the application shell implement an interface that looks something like this:
public interface IDialogManager
{
void ShowDialog(IScreen dialogModel);
void ShowMessageBox(string message, string title = null, MessageBoxOptions options = MessageBoxOptions.Ok, Action<IMessageBox> callback = null);
}
and then he registers this interface with the IoC container, I guess you can use your imagination from there on and if you don't have time then you can look at the source code that accompanies the article.
When the root/main/shell view-model implements a kind of DialogService interface, every other view-model needing to show dialogs will end up with a dependency on the root view-model. Sometimes this might not be desiderable, e.g. if it could cause a dependency loop:
DialogService (aka RootViewModel) -> SomeViewModel -> RootViewModel.
A more involved approach to break this dependency chain (and actually invert it) is the following:
Implement a behavior that detects Window.OnSourceInitialized event and attach it to main view Window component. That is the event fired when the window handle is available. Upon event, behavior will notify some handler passed in via attached property:
<my:WindowSourceBehavior InitListener="{Binding WindowListener}" />
public class WindowSourceBehavior : Behavior<Window>
{
// ...
// boilerplate code for IWindowListener InitListener dependency property
// ...
attachedWindow.SourceInitialized += (sender, evt) =>
{
// ...
InitListener.SourceInitialized(sender as Window);
}
}
DialogService exposes a handler - or interface - as requested by behavior:
public class DialogService : IWindowListener
{
// ...
public void SourceInitialized(Window rootWindow) { /* ... */ }
}
In root view-model, (indirectly) get the DialogService injected as a dependency. During construction, sets view-model bound property, WindowListener, to the DialogService handler/interface:
public MainViewModel(IWindowListener dialogServiceInDisguise)
{
WindowListener = dialogServiceInDisguise;
}
public IWindowListener WindowListener { get; private set; }
Doing so, the DialogService is able to get a hold of root Window, and whichever view-model needs to show a dialog does not create a(n indirect) dependency on main view-model.

Categories

Resources