Without wanting to bug sacha too much, does anyone know what the Cinch V2 way of closing a View from a ViewModel command?
Previously I have used a RelayCommand in the ViewModel base to accept the Escape keybinding command action and wired up a RequestClose event in the View code behind to do this.
Use CloseActivePopUpCommand.Execute(true) in the execute method to close a view.
I've included a short sample below.
[ExportViewModel("PickOperatorViewModel")]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class PickOperatorViewModel : ViewModelBase
{
[ImportingConstructor]
public PickOperatorViewModel()
{
PickOperaterCommand = new SimpleCommand<Object, Object>(CanExecutePickOperaterCommand, ExecutePickOperaterCommand);
}
public SimpleCommand<Object, Object> PickOperaterCommand { get; private set; }
private void ExecutePickOperaterCommand(Object args)
{
CloseActivePopUpCommand.Execute(true);
}
private bool CanExecutePickOperaterCommand(Object args)
{
return true;
}
}
Related
I'm trying to set up a WPF app to call the new window on a menu click with the data provider interface injected into the new viewmodel.
Followed many tutorials and created the Bootstrapper for Caliburn, a service locator and module for ninject. So far the main view doesn't need the IDataProvider but I'd like to open a new window on click event.
The Bootstrapper:
public class Bootstrapper : BootstrapperBase
{
public Bootstrapper()
{
Initialize();
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
DisplayRootViewFor<MainScreenViewModel>();
}
}
The Service Locator and Module:
public class ServiceLocator
{
private readonly IKernel _kernel;
public ServiceLocator()
{
_kernel = new StandardKernel(new ServiceModule());
}
public MainScreenViewModel MainScreenViewModel => _kernel.Get<MainScreenViewModel>();
public NewLayoutViewModel NewLayoutViewModel => _kernel.Get<NewLayoutViewModel>();
}
public class ServiceModule : NinjectModule
{
public override void Load()
{
Bind<ISqlite>().To<Sqlite>();
Bind<IDataProvider>().To<DataProvider>();
}
}
And this is where I got stuck:
public class MainScreenViewModel : Conductor<object>
{
private IWindowManager _windowManager;
public MainScreenViewModel()
{
_windowManager = new WindowManager();
}
public void NewLayout()
{
_windowManager.ShowWindow(new NewLayoutViewModel());
}
}
since the NewLayoutViewModel requires the IDataProvider.
Not sure, what am I missing, but in my understanding Ninject should take care of this di for NewLayoutViewModel.
Found a good solution from Tim Corey on YouTube.
Basically the answer is, if you not insist Ninjet, use Caliburn.Micro's build-in DI solution "SimpleContainer".
Actually exploring the Command Pattern and finds it pretty interesting. I'm writing a WPF Windows App following the MVVM Architectural Pattern.
I've begun with these post which explain the basics.
Basic MVVM and ICommand usuage example
Simplify Distributed System Design Using the Command Pattern, MSMQ, and .NET
Now that I was able to break user actions into commands, I thought this could be great to inject the commands that I want. I noticed that the commands are found into the ViewModel in the first referenced article, So I thought that would be great if I could use them along Ninject and actually inject my command into my view model using a binding that would look like the following:
kernel
.Bind<ICommand>()
.To<RelayCommand>()
.WithConstructorArgument("execute", new Action<object>(???));
But then, what to put in here ???. The expected answer is a method. Great! I just need a method to be put in there.
Because the first article simply initialize its commands within the ViewModel constructor, it is easy to say what method should be executed on the command execute call.
But from within the CompositionRoot? This is no place to put a method that will do anything else than bind types together through whatever DI container you're using!
So now, I've come across the Interceptor Pattern using Ninject Extensions. This looks like it could suits my requirements, and there is a bit of confusion here, if I may say. Not that the articles are confusing, they're not. I'm confused!
Using Ninject.Extensions.Interception Part 1 : The Basics
Using Ninject.Extensions.Interception Part 2 : Working With Interceptors
Also, there is this answer from BatteryBackupUnit who always sets great answers.
Ninject - How to implement Command Pattern with Ninject?
But now, I can't see how to glue it all up together! Humbly, I'm lost.
So here's my code so far.
RelayCommand
public class RelayCommand : ICommand {
public RelayCommand(Action<object> methodToExecute, Predicate<object> canExecute) {
if(methodToExecute == null)
throw new ArgumentNullException("methodToExecute");
if(canExecute == null)
throw new ArgumentNullException("canExecute");
this.canExecute = canExecute;
this.methodToExecute = methodToExecute;
}
public bool CanExecute(object parameter) {
return canExecute != null && canExecute(parameter);
}
public event EventHandler CanExecuteChanged {
add {
CommandManager.RequerySuggested += value;
canExecuteChanged += value;
}
remove {
CommandManager.RequerySuggested -= value;
canExecuteChanged -= value;
}
}
public static bool DefaultCanExecute(object parameter) { return true; }
public void Execute(object parameter) { methodToExecute(parameter); }
public void OnCanExecuteChanged() {
var handler = canExecuteChanged;
if(handler != null) handler(this, EventArgs.Empty);
}
public void Destroy() {
canExecute = _ => false;
methodToExecute = _ => { return; };
}
private Predicate<object> canExecute;
private Action<object> methodToExecute;
private event EventHandler canExecuteChanged;
}
CategoriesManagementViewModel
public class CategoriesManagementViewModel : ViewModel<IList<Category>> {
public CategoriesManagementViewModel(IList<Category> categories
, ICommand changeCommand
, ICommand createCommand
, ICommand deleteCommand) : base(categories) {
if(changeCommand == null)
throw new ArgumentNullException("changeCommand");
if(createCommand == null)
throw new ArgumentNullException("createCommand");
if(deleteCommand == null)
throw new ArgumentNullException("deleteCommand");
this.changeCommand = changeCommand;
this.createCommand = createCommand;
this.deleteCommand = deleteCommand;
}
public ICommand ChangeCommand { get { return changeCommand; } }
public ICommand CreateCommand { get { return createCommand; } }
public ICommand DeleteCommand { get { return deleteCommand; } }
private readonly ICommand changeCommand;
private readonly ICommand createCommand;
private readonly ICommand deleteCommand;
}
I wonder, would it be better off using Property Injection, though I tend not to use it all?
Let's say I have CategoriesManagementView that calls another window, let's say CreateCategoryView.Show(), and then the CreateCategoryView takes over until the user is back to the management window.
The Create Command then needs to call CreateCategoryView.Show(), and that is what I tried from within the CompositionRoot.
CompositionRoot
public class CompositionRoot {
public CompositionRoot(IKernel kernel) {
if(kernel == null) throw new ArgumentNullException("kernel");
this.kernel = kernel;
}
//
// Unrelated code suppressed for simplicity sake.
//
public IKernel ComposeObjectGraph() {
BindCommandsByConvention();
return kernel;
}
private void BindCommandsByConvention() {
//
// This is where I'm lost. I can't see any way to tell Ninject
// what I want it to inject into my RelayCommand class constructor.
//
kernel
.Bind<ICommand>()
.To<RelayCommand>()
.WithConstructorArgument("methodToExecute", new Action<object>());
//
// I have also tried:
//
kernel
.Bind<ICommand>()
.ToConstructor(ctx =>
new RelayCommand(new Action<object>(
ctx.Context.Kernel
.Get<ICreateCategoryView>().ShowSelf()), true);
//
// And this would complain that there is no implicit conversion
// between void and Action and so forth.
//
}
private readonly IKernel kernel;
}
Perhaps I am overcomplicating things, that is generally what happends when one gets confused. =)
I just wonder whether the Ninject Interception Extension could be the right tool for the job, and how to use it effectively?
I created a simple example of a command interacting with an injected service. might not compile since i'm going from memory. Maybe this can help you.
public class TestViewModel
{
private readonly IAuthenticationService _authenticationService;
public DelegateCommand SignInCommand { get; private set; }
public TestViewModel(IAuthenticationService authenticationService) //Inject auth service
{
_authenticationService = authenticationService
SignInCommand = new DelegateCommand(OnSignInRequest)
}
private void OnSignInRequest(Action<bool> isSuccessCallback)
{
var isSuccess = _authenticationService.SignIn();
isSuccessCallback(isSuccess);
}
}
}
In AttachmentViewModel I have the following code
public ICommand EditAttachmentCommand { get; private set; }
public AttachmentsViewModel()
{
EditAttachmentCommand = new ActionCommand<AvailAttachment>(EditAttachment);
}
private void EditAttachment(AvailAttachment attachment)
{
var attachmentDetailsViewModel = Router.ResolveViewModel<AttachmentDetailsViewModel>(true, ViewModelTags.ATTACHMENT_DETAILS_VIEWMODEL);
attachmentDetailsViewModel.NavigateToAttachment(attachment.ArticleId);
EventAggregator.Publish(ViewTags.ATTACHMENT_DETAILS_VIEW.AsViewNavigationArgs());
EventAggregator.Publish(new CurrentViewMessage(ContentView.Attachment));
}
In my MainViewModel I have the following code:
public ICommand SessionAttachmentCommand { get; private set; }
public MainMenuViewModel()
{
SessionAttachmentCommand = new ActionCommand<AvailAttachment>(EditAttachment);
}
private void EditAttachment(AvailAttachment attachment)
{
var attachmentDetailsViewModel = Router.ResolveViewModel<AttachmentDetailsViewModel>(true, ViewModelTags.ATTACHMENT_DETAILS_VIEWMODEL);
attachmentDetailsViewModel.NavigateToAttachment(attachment.ArticleId);
EventAggregator.Publish(ViewTags.ATTACHMENT_DETAILS_VIEW.AsViewNavigationArgs());
EventAggregator.Publish(new CurrentViewMessage(ContentView.Attachment));
}
I would like to pass the object state of AvailAttachment class from AttachmentsViewModel to MainMenuViewModel. presently null value comes for AvailAttachment's attachment object in MainMenuViewModel. I am debugging a code written by someone. This is a silverlight project using MVVM model. How do I do that?
Any help is appreciated. Thanks
I'm not sure the best way to get this accomplished. Here's my view:
public partial class MyPage : Page
{
[Import]
public MyVM ViewModel
{
get { return DataContext as MyVM ; }
set { DataContext = value; }
}
public String EventName { get; set; }
public MyPage()
{
InitializeComponent();
CompositionInitializer.SatisfyImports(this);
}
// Executes when the user navigates to this page.
protected override void OnNavigatedTo(NavigationEventArgs e)
{ }
}
And my VM:
[Export]
public class MyVM : ViewModelBase
{
public MyVM ()
{
}
}
This works great. However, I need to get data from either the viewmodel that has my string, or the URL. Either way, I'm not sure the best way to get the string to MyVW using MEF.
I thought ok I'll use Messaging from MVVMLight, but the MyVM class isn't instantiated yet to receive the broadcast from the other ViewModel. So then I thought well, I'll try this:
[Export]
public class MyVM : ViewModelBase
{
public MyVM ([Import("hello")]string hello)
{
}
}
and then put this in the view:
[Export("hello")]
public String MyHello { get; set; }
but that gave me an error. Cannot call SatisfyImports on a object of type 'Form A' because it is marked with one or more ExportAttributes.
So what's the best way to accomplish this?
To share data between views I usually inject a SharedData object into my ViewModels.
[Import(RequiredCreationPolicy = CreationPolicy.Shared)]
public ISharedData SharedData { get; set; }
I'm also using the Caliburn Micro framework so I'm not passing data around via the URL querystring. By convention CM will parse out URL parameters and inject them into properties on your VM but I'm not sure if this functionality only applies to Windows Phone development.
from here
Examine the Page’s QueryString. Look
for properties on the VM that match
the QueryString parameters and inject
them, performing the necessary type
coercion.
When you say you want to possibly pass data from the view to the vm, that should happen through databinding.
I am working on a Composite MVVM application and trying to get Global Binding events happening - Except it is NOT!..
the buttons are disabled by default although the CanRun returns true!! !! I have followed the Composite Guide and the OnLoadMenu is not firing!!!
I have been going around in circles (Event Aggregators, DelegateCommands, Composite Commands) It just isnt working. Can any please look at this and tell me what I am Missing??
//xmlns:local="clr-namespace:Commands;assembly=MyApp"
<Button HorizontalAlignment="Center" Margin="1,1,1,1"
Grid.Row="2"
Command="{x:Static local:AdminGlobalCommands.LoadAdminMenu}"/>
public static class AdminGlobalCommands // In Common Code Library
{
//List All Global Commands Here
public static CompositeCommand LoadAdminMenu = new CompositeCommand();
}
public class AdminModuleViewModel : ViewModelBase, IAdminModuleViewModel // In AdminModule
{
protected IRegionManager _regionManager;
private IUnityContainer _container;
public AdminModuleViewModel(
IEventAggregator eventAggregator,
IBusyService busyService,
IUnityContainer container,
IRegionManager regionManager
)
: base(eventAggregator, busyService, container)
{
// show the progress indicator
busyService.ShowBusy();
this._regionManager = regionManager;
this._container = container;
//set up the command receivers
this.AdminShowMenuCommand = new DelegateCommand<object>(this.OnLoadAdminMenu, this.CanShowAdminMenu);
//Listen To Events
AdminGlobalCommands.LoadAdminMenu.RegisterCommand(AdminShowMenuCommand);
busyService.HideBusy();
}
public DelegateCommand<object> AdminShowMenuCommand { get; private set; }
private bool CanShowAdminMenu(object obj)
{ //Rules to Handle the Truth
return true;
}
public void OnLoadAdminMenu(object obj)
{
UIElement viewToOpen = (UIElement)_container.Resolve(typeof(AdminMenuControl)) ;
_regionManager.AddToRegion("MainRegion", viewToOpen);
_regionManager.Regions["MainRegion"].Activate(viewToOpen); ;
}
}
When using PRISM if you are creating a CompositeCommand with monitorCommandActivity set to true, you also need to be aware of and set DelegateCommand.IsActive state.
In that case CompositeCommand will not consider inactive DelegateCommands and as a result your button might stay disabled (for example when no other active and executable DelegateCommand is in the CompositeCommands's command chain).