Caliburn.Micro WindowManager and Window constructor parameter - wpf

It looks like Caliburn.Micro's WindowManager have problem with WPF Windows, that have constructor parameter although that parameter's type is registered in Caliburn.Micro IoC. Is there better way to put that needed parameter into Window except using stinking service locator IoC.Get(Of T) in constructor?
Namespace Views
Class MainWindowView
Private _eventAggregator As IEventAggregator
Public Sub New(eventAggregator As IEventAggregator)
_eventAggregator = eventAggregator
End Sub
End Class
End Namespace
Sub ShowMainWindowView()
'Everything is correctly registered in Ioc...
Dim windowManager As New WindowManager
Dim viewModel As New MainWindowViewModel
windowManager.ShowDialog(viewModel)
'Exception is thrown about absence of parameterless constructor of MainWindowView
End Sub

I tried to reproduce this problem again in new, clear project and it works - it is only needed to have type of view (Window) registered in IoC container and WindowManager automatically injects required dependencies in view's constructor.

Related

Binding UserControl to ViewModel (Caliburn Micro WPF)

I am creating a login form that will be used by many different applications. The login will always have the same logic, so I'd like to bind a viewmodel and do all logic there (Retrieving login info from database, etc). I created a new UserControl, MainView and a ViewModel, MainViewModel both of which are in a Login namespace.
The form continues to run everything in the code-behind, but nothing in the VM.
Is there another way of binding that I am not aware of?
Code-Behind MainView.Xaml.vb
Imports Caliburn.Micro
Namespace Login
Public Class MainView
Public Sub New()
MsgBox("TEST code-behind")
End Sub
End Class
End Namespace
VM MainViewModel.vb
Imports Caliburn.Micro
Namespace Login
Public Class MainViewModel
Inherits PropertyChangedBase
Public Sub New()
MsgBox("TEST ViewModel")
End Sub
End Class
End Namespace
Xaml
<UserControl x:Class="Login.MainView"
xmlns:cal="http://www.caliburnproject.org"
xmlns:local="clr-namespace:cLogin.Login"
cal:Bind.Model="cLogin.Login.MainViewModel" (not sure if needed due to naming)
... >
EDIT
This is how I have added the UserControl as a separate window before the user is logged in, I can see the content, but none of the properties inside the ViewModel bind
Dim login As New Window
With login
.WindowStyle = WindowStyle.None
.ResizeMode = ResizeMode.NoResize
.SizeToContent = SizeToContent.WidthAndHeight
.Content = New MainView()
End With
login.ShowDialog()
Since you are creating the window explicitly, you also need to explicitly set its DataContext:
Dim login As New Window
With login
.WindowStyle = WindowStyle.None
.ResizeMode = ResizeMode.NoResize
.SizeToContent = SizeToContent.WidthAndHeight
.Content = New MainView()
.DataContext = New MainViewModel()
End With
You should also bind the attached Bind.Model property to the DataContext in the view:
cal:Bind.Model="{Binding}"

Caliburn's Screen can not be inherited by Window or UserControl

I discovered that Window or UserControl can not inherit Caliburn's Screen base class. Am I forced to implement IScreen interface to every window/user control in my WPF application?
Imports Caliburn.Micro
Namespace Views
Public Class CustomView
Inherits Screen
End Class
End Namespace
Base class 'Screen' specified for class 'CustomView' cannot be
different from the base class 'UserControl' of one of its other
partial types.
Caliburn's Screen base class is meant to be used on view models.
Imports Caliburn.Micro
Namespace ViewModels
Public Class CustomViewModel
Inherits Screen
'...'
End Class
End Namespace
And then used to bind derived view models to the views
Imports ViewModels
Namespace Views
Public Class CustomView
Inherits Window
Public Sub New()
'...'
Dim viewModel As New CustomViewModel()
DataContext = viewModel
End Sub
End Class
End Namespace
Reference Caliburn.Micro Documentation: Screens, Conductors and Composition

How to use Ribbon Buttons to execute code?

So far I have managed to create a ribbon with several buttons by following this tutorial.
Now I want to make the buttons actually do stuff! I found this example which shows how to execute already existing method. However if I do: Command="myCustomClass.myCustomMethod" and use
Class myCustomClass
Public Sub myCustomMethod()
'do stuff
End Sub
End Class
I get the error myCustomClass is not supported in a Windows Presentation Foundation (WPF) project.
I also noticed this this post from the same post. After converting the code to VB.NET:
Public Class MyCommand
Implements ICommand
Public Sub Execute(parameter As Object) Implements ICommand.Execute
Dim hello As String = TryCast(parameter, String)
MsgBox(hello)
End Sub
Public Function CanExecute(parameter As Object) As Boolean Implements ICommand.CanExecute
Return True
End Function
Public Event CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged
End Class
and adding the references to the <Window x:Class="MainWindow" ... element:
<Window.Resources>
<local:MyCommand x:Key="mycmd"/>
</Window.Resources>
I get the following error: The name 'MyCommand' does not exist in the namespace "clr-namespace:RibbonSample".
I'm at a complete loss as to what I need to do to implement these buttons. Would it be possible to get any full ribbon samples using the same framework, with which I can learn from? I assume the solution to this should be really simple.
Rebuild your project.
The WPF designer loads classes from your project's compiled assembly (so that it can actually execute your code).
If you add a new class, it will give you errors until you rebuild your project so the assembly includes that class.

Singletons and ICommand

Is there some special rules I have to be careful about when binding my view to the commands defined in my singleton ViewModel, opposed to normal (non-singleton) ViewModels?
All my ViewModels except the one in question behave normally. Each of them exposes two public members called HasChanges (bool property) and SaveChanges (method) that I'm calling in the CanExecute and Execute functions of my commands.
While all other Views behave normally, enabling/disabling the buttons when the value of HasChanges changes and saving the contents when those buttons are clicked, the only ViewModel that implements Singleton pattern happens to call CanExecute only upon the first loading of the View.
After that, any number of PropertyChanged events raised from within that ViewModel (all my ViewModels implement INotifyPropertyChanged) do not affect the disabled state of the button.
Wondering what I'm missing here.
Here's the singleton ModelView:
Public NotInheritable Class MyViewModel
Private Shared ReadOnly mInstance As New CommonListsViewModel
Public Shared ReadOnly Property Instance() As CommonListsViewModel
Get
Return mInstance
End Get
End Property
Public Property SaveChangesCommand As ICommand
Private Sub New()
SaveChangesCommand = New Commands.SaveChangesCommand()
End Sub
Public ReadOnly Property HasChanges As Boolean Implements IEditorViewModel.HasChanges
Get
...
End Get
End Property
Public Function SaveChanges() As Boolean Implements IEditorViewModel.SaveChanges
...
End Function
End Class
Here's the command:
Friend Class SaveChangesCommand
Inherits CommandBase
Public Overrides Function CanExecute(parameter As Object) As Boolean
Return MyViewModel.Instance.HasChanges
End Function
Public Overrides Sub Execute(parameter As Object)
MyViewModel.Instance.SaveChanges()
End Sub
End Class
And here's my View:
<Grid DataContext="{x:Static local:CommonListsViewModel.Instance}">
<Button Command="{Binding SaveChangesCommand}">
</Grid>
There shouldn't be any difference in the behavior due to the fact that a class follows the singleton pattern. Anything with a reference to the singleton will not care how the construction of the class is restricted, only that the instance of the class exists.
The problem probably lies in a lack of a proper link between the PropertyChanged event of the ViewModel and the CanExecuteChanged on the Command.

Changing host WinForm properties from ViewModel bound to ElementHost

Can anyone suggest approaches that will allow me to change the properties of a host WinForm from a WPF element in an ElementHost?
I have an MVVM application using Prism that's bound to an ElementHost. I would like to change the WinForm title, resize the WinForm and close the WinForm from within my ViewModel.
I understand receiving data from a WPF composite control described in this article but I can't see how that will work in a ViewModel.
App is the WinForm
ChartWizardViewModel is the ViewModel I want to change the properties of App from.
Everything else is the Prism architecture. Shell, Bootstrapper etc.
Public Class App
Public Sub New(ByVal modulesToLoad As List(Of String))
' This call is required by the designer.
InitializeComponent()
If Application.Current Is Nothing Then
Dim wpfAppAdapter As New WpfApplicationAdapter
End If
' Load the application modules
Dim formBootstrapper As New Bootstrapper(modulesToLoad)
formBootstrapper.Run()
' Get the current instance of shell and bind it to the ElementHost
Dim shellElement = formBootstrapper.Container.Resolve(Of Shell)()
ehMaster.Child = shellElement
End Sub
End Class
Public NotInheritable Class Bootstrapper
Inherits UnityBootstrapper
Private _modulesToLoad As List(Of String) ' The modules that we want to load
Public Sub New(ByVal modulesToLoad As List(Of String))
_modulesToLoad = modulesToLoad
End Sub
Protected Overrides Function CreateShell() As DependencyObject
Dim shell = Container.Resolve(Of Shell)()
Return shell
End Function
Protected Overrides Function GetModuleCatalog() As IModuleCatalog
Dim catalog As ModuleCatalog = New ConfigurationModuleCatalog()
For Each moduleToLoad As String In _modulesToLoad
Select Case StringHelper.CleanString(moduleToLoad)
Case "chartwizardmodule"
catalog.AddModule(GetType(ChartWizardModule))
End Select
Next
Return catalog
End Function
End Class
Public NotInheritable Class ChartWizardModule
Implements IModule
Private ReadOnly regionManager As IRegionManager
Public Sub Initialize() Implements IModule.Initialize
regionManager.RegisterViewWithRegion("MainRegion", GetType(MainWindow))
End Sub
Public Sub New(regionManager As IRegionManager)
Me.regionManager = regionManager
End Sub
End Class
Partial Public Class MainWindow
Private _objChartWizardViewModel As ChartWizardViewModel ' The chart wizard base view model that controls the rest of the views
Private Sub MainWindow_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
Dim objChartWizardViewModel As New ChartWizardViewModel()
_objChartWizardViewModel = objChartWizardViewModel
' Data Context for the Chart Wizard
Me.DataContext = _objChartWizardViewModel
End Sub
End Class
Public Class ChartWizardViewModel
Implements INotifyPropertyChanged
' I need to change the properties of the WinForm (App) from here
End Class
I solved this with an EventAggregator as discussed in this post
Using EventAggregator to communicate between WPF in ElementHost and host WinForm

Resources