Prism. Closing a dialog created with IDialogService - c#

I am trying to use a new IDialogService which was discussed in github issue 1666. A New IDialogService for WPF. I like this new feature but I can't find a solution for one case of using IDialogService in compare with InteractionRequest.
There is a button, pressing on which non-modal dialog is opened. If user press the same button one more time, while dialog still open, dialog close. How this behavior should be implemented in a proper way?
MainWindowViewModel
public class MainWindowViewModel : BindableBase
{
private readonly IDialogService _dialogService;
public DelegateCommand CustomPopupCommand { get; }
public MainWindowViewModel(IDialogService dialogService)
{
_dialogService = dialogService;
CustomPopupCommand = new DelegateCommand(OpenClosePopup);
}
private void OpenClosePopup()
{
// It looks like some additional logic should be implemented here.
// How to save previously opened IDialogAware instance and close it if needed?
_dialogService.Show("CustomPopupView", new DialogParameters("Title=Good Title"), result => { });
}
}
CustomPopupViewModel
public class CustomPopupViewModel : BindableBase, IDialogAware
{
private string _title;
public string Title
{
get => _title;
set => SetProperty(ref _title, value);
}
public DelegateCommand<object> CloseCommand { get; }
public CustomPopupViewModel()
{
CloseCommand = new DelegateCommand<object>(CloseDialog);
}
public event Action<IDialogResult> RequestClose;
public void OnDialogOpened(IDialogParameters parameters)
{
Title = parameters.GetValue<string>(nameof(Title));
}
public void OnDialogClosed()
{
}
public bool CanCloseDialog()
{
return true;
}
public void RaiseRequestClose(IDialogResult dialogResult)
{
RequestClose?.Invoke(dialogResult);
}
private void CloseDialog(object button)
{
RaiseRequestClose(
new DialogResult(button is ButtonResult buttonResult ? buttonResult : ButtonResult.Cancel));
}
}
I have no idea how can it be implemented in proper way because method IDialogService.Show() fully decoupled from knowing about ViewModel and View. Of course except the name of View.

You can always send an event through the event aggregator, probably you have to pass some id in the dialog parameters to close the right dialog if there's more than one open at a time.
But this feels really clunky, I'd prefer to get an IDisposable from Show/ShowDialog that closes the dialog on Dispose.
public CustomPopupViewModel(IEventAggregator eventAggregator)
{
eventAggregator.GetEvent<CloseDialogEvent>().Subscribe( id => { if (id == _id) CloseMe(); } );
}
public void OnDialogOpened(IDialogParameters parameters)
{
_id = parameters.GetValue<string>("id");
}
_dialogService.Show("CustomPopupView", new DialogParameters("id=12345"), result => { });
_eventAggregator.GetEvent<CloseDialogEvent>().Publish("12345");

I find it simplest to use Prism implementation of the subscriber pattern
I use a class that will be used in the pattern and is communicated:
public class DialogStatus
{
public bool DialogResult { get; set; }
}
In my sample, I show you how I do this using a Login Dialog in WPF using Prism 8.0.0.1909
in the App.cs
protected override void OnInitialized()
{
var login = Container.Resolve<LoginDialog>();
var result = login.ShowDialog();
if (result.HasValue && result.Value)
{
base.OnInitialized();
}
else
{
Application.Current.Shutdown();
}
}
in LoginDialog.cs in my Dialogs folder
public partial class LoginDialog : Window
{
public LoginDialog(IEventAggregator eventAggregator)
{
InitializeComponent();
eventAggregator.GetEvent<CloseDialogWindowEvent>().Subscribe(OnCloseWindow);
}
private void OnCloseWindow(DialogStatus obj)
{
base.DialogResult = obj.DialogResult;
}
}
now anywhere in my code, in a ViewModel of view a custom control's view model, the only thing I need to do is pass the IEventAggregator in in the constructor and save it in a field.
private readonly IEventAggregator _eventAggregator;
public LoginControlViewModel(IAuthenticationService authenticationService
, IConnectFileImporterService connectFileImporterService
, IDialogService dialogService
, IEventAggregator eventAggregator)
{
_eventAggregator= eventAggregator;
// the other code
}
I can now close my dialog, and in this sample return true to falls to my OnInitalize in my App.cs from anywhere by calling
_eventAggregator.GetEvent<CloseDialogWindowEvent>().Publish(new CloseDialogWindowEvent() { DialogResult = true });
or
_eventAggregator.GetEvent<CloseDialogWindowEvent>().Publish(new CloseDialogWindowEvent() { DialogResult = false});

If i understand correctly, you want to close the dailog window programmatically instead of clicking the windows's close button, right? If It is true, maybe I can provide you with a solution. Although this method is not very elegant, it is very simple.
My project use mahapps styles, I want use metrowindow as the dailoghost window. Following prism documentation, I register dialoghost window and usercontrol like this:
containerRegistry.RegisterDialogWindow<DialogHost>(nameof(DialogHost));
containerRegistry.RegisterDialog<UserEdit, UserEditViewModel>(nameof(UserEdit));
The UserEidt is a usercontrol, I place a confirm button and a cancel button in UserEidt, and both button binding DelegateCommand in UserEditViewModel. The question is, how can i close dailogwindow by clicking the cancel button?
Here is my solution, firstly define a IDailogViewModel interface:
public interface IDialogViewModel
{
Action CloseDialogWindow { get; set; }
}
Then UserEditViewModel implement this interface:
public class UserEditViewModel : BindableBase, IDialogAware,IDialogViewModel
{
public DelegateCommand CancelCmd { get; private set; }
public Action CloseDialogWindow { get; set; }
public UserEditViewModel()
{
CancelCmd = new DelegateCommand(CloseDialogWindow)
}
private void CloseDialogWindow()
{
CloseDialogWindow.Invoke();
}
}
Infact, when the dialog window popup, the UserEdit will be dialogWindow's content. So in the dialogwindow's loaded event handler, i can get the UserEdit object by using Window.Content, here is the code:
public partial class DialogHost : MetroWindow, IDialogWindow
{
public DialogHost()
{
InitializeComponent();
}
public IDialogResult Result { get; set; }
private void MetroWindow_Loaded(object sender, RoutedEventArgs e)
{
var dialogVM = (IDialogViewModel)((UserControl)Content).DataContext;
dialogVM.CloseDialogWindow += CloseDialogWindow;
}
void CloseDialogWindow()
{
Close();
}
}
Now,after clicking the cancel button, the dialogwindow will be close.

Related

Creating dialog windows with Dependency Injection and Services

I've finally given in and started learning and applying MVVM architecture on my EFCore project. However, I haven't understood how scoped services work. On my previous question I was told I needed to use one scope per viewmodel otherwise, since I'm working with async methods, I would be getting concurrent transactions on the same DbContext (which is not allowed).
I've also read here that one way of using dialog windows while still following MVVM would be to have a service that would create a dialog window container with a dynamically filled viewmodel.
So far, so good. However I've hit a snag:
I'm on my MainView, and I run a command that opens a new dialog window (with a new scope, I think). And on that dialog, I run a command that should open another dialog window. However, when trying to close, somehow I manage to close the parent dialog rather than the focused window. My relevant code is as follows:
What am I doing wrong here?
PS.: I translated the names to english for better readability, but I might've let one or another slip by - let me know if I can fix anything.
public class PurchasesViewModel : ViewModelBase
{
private readonly IDialogGenerator _purchaseDialogGenerator;
private readonly IDialogViewModelFactory _purchaseRecordVMFactory;
public ICommand CreateNewPurchase { get; set; }
public PurchasesViewModel(IServiceProvider services, IDialogGenerator purchaseVMDialogGenerator)
{
_purchaseDialogGenerator = purchaseVMDialogGenerator;
IServiceScope purchaseVMScope = services.CreateScope();
IServiceProvider provider = purchaseVMScope.ServiceProvider;
_purchaseRecordVMFactory = provider.GetRequiredService<IDialogViewModelFactory>();
CreateNewPurchase = new CreatePurchaseCommand(_purchaseDialogGenerator, _purchaseRecordVMFactory);
}
}
internal class CreatePurchaseCommand : ICommand
{
private IDialogGenerator _purchaseDialogGenerator;
private IDialogViewModelFactory _purchaseVMFactory;
public CreatePurchaseCommand(IDialogGenerator PurchaseRecordDialogGenerator, IDialogViewModelFactory PurchaseRecordVMFactory)
{
_purchaseDialogGenerator = PurchaseRecordDialogGenerator;
_purchaseVMFactory = PurchaseRecordVMFactory;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_purchaseDialogGenerator.ShownViewModel = _purchaseVMFactory.CreateDialogContentViewModel(DialogType.RecordPurchases);
_purchaseDialogGenerator.ShowDialog();
}
}
public class PurchaseRecordViewModel : DialogContentViewModelBase
{
private readonly IMessaging<PURCHASE> _purchaseMessaging;
private readonly IDialogGenerator _purchaseDialogGenerator;
private readonly IDialogGenerator _importViaXMLDialogGenerator;
private readonly IDialogViewModelFactory _importViaXMLVMFactory;
public ICommand SavePurchase { get; set; }
public ICommand ImportXML { get; set; }
public PurchaseRecordViewModel(IServiceProvider servicos, IDialogGenerator PurchaseRecordDialogGenerator)
{
_purchaseDialogGenerator = PurchaseRecordDialogGenerator;
IServiceScope scope = servicos.CreateScope();
IServiceProvider provider = scope.ServiceProvider;
_importViaXMLDialogGenerator = provider.GetRequiredService<IDialogGenerator>();
_importViaXMLVMFactory = provider.GetRequiredService<IDialogViewModelFactory>();
_purchaseMessaging = provider.GetRequiredService<IMessaging<PURCHASE>>();
SavePurchase = new SalvaPurchaseCommandAsync(PurchaseRecordDialogGenerator);
ImportXML = new ImportXMLDePurchaseCommand(_importViaXMLDialogGenerator, _importViaXMLVMFactory);
}
}
internal class ImportXMLFromPurchaseCommand : ICommand
{
private readonly IDialogGenerator _importviaXMLDialogGenerator;
private readonly IDialogViewModelFactory _importXMLVMFactory;
public ImportXMLFromPurchaseCommand(
IDialogGenerator importviaXMLDialogGenerator,
IDialogViewModelFactory importXMLVMFactory)
{
_importviaXMLDialogGenerator = importviaXMLDialogGenerator;
_importXMLVMFactory = importXMLVMFactory;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_importviaXMLDialogGenerator.ShownViewModel = _importXMLVMFactory.CreateDialogContentViewModel(DialogType.ImportXML);
_importviaXMLDialogGenerator.ShowDialog();
}
}
internal class SalvaPurchaseCommandAsync : ICommand
{
private readonly IDialogGenerator _dialogGenerator;
public SalvaPurchaseCommandAsync(IDialogGenerator dialogGenerator)
{
_dialogGenerator = dialogGenerator;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_dialogGenerator.Result = DialogResult.OK;
_dialogGenerator.Close();
}
}
public class ImportViaXMLViewModel : DialogContentViewModelBase
{
public ICommand ImportArquivoXML { get; set; }
public ICommand Import { get; set; }
private readonly IDialogGenerator _importViaXMLDialogGenerator;
public ImportViaXMLViewModel(IMessaging<PURCHASE> messaging,
IDialogGenerator importViaXMLDialogGenerator)
{
_importViaXMLDialogGenerator = importViaXMLDialogGenerator;
Import = new ImportCommand(this, messaging, _importViaXMLDialogGenerator);
}
}
internal class ImportCommand : ICommand
{
private readonly IMessaging<PURCHASE> _messaging;
private readonly IDialogGenerator _dialogGenerator;
public ImportCommand(IMessaging<PURCHASE> messaging, IDialogGenerator dialogGenerator)
{
_messaging = messaging;
_dialogGenerator = dialogGenerator;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_messaging.Message = new COMPRA();
_dialogGenerator.Result = DialogResult.OK;
_dialogGenerator.Close();
}
}
public abstract class AsyncCommandBase : ICommand
{
private bool _isExecuting;
private readonly Action<Exception> _onException;
public bool IsExecuting
{
get { return _isExecuting; }
set {
_isExecuting = value;
CanExecuteChanged?.Invoke(this, new EventArgs());
}
}
public AsyncCommandBase(Action<Exception> onException)
{
this._onException = onException;
}
public virtual event EventHandler CanExecuteChanged;
public virtual bool CanExecute(object parameter)
{
return !IsExecuting;
}
public async void Execute(object parameter)
{
IsExecuting = true;
try
{
await ExecuteAsync(parameter);
}
catch (Exception ex)
{
_onException(ex);
}
IsExecuting = false;
}
protected abstract Task ExecuteAsync(object parameter);
}
I found out a way to work it out. I found an answer after reading Good or bad practice for Dialogs in wpf with MVVM?.
I have created a singleton dialogStore in which I keep all the currently opened dialogs. Whenever I need a new dialog window, I "register" a new dialogGenerator with the dialogStore, and it'll open it for me. When I need to close it, the store is also responsible for that. Furthermore, so far, I've been using only one modal dialog active at a time, so whenever I close a dialog, I'll be sure it's the latest added one. But in the future, each dialogGenerator will know its set dialogStore index, and will be able to close themselves (and other dialogs) in any order needed.
My dialogStore is as follows:
public class DialogsStore : IDialogsStore
{
public List<IDialogGenerator> OpenDialogs { get; set; } = new List<IDialogGenerator>();
public void CloseDialog(DialogResult dialogResult)
{
IDialogGenerator lastDialogGenerator = OpenDialogs[OpenDialogs.Count - 1];
lastDialogGenerator.Resultado = dialogResult;
//lastDialogGenerator.DialogClosed = null;
lastDialogGenerator.Close();
}
public int RegisterDialog(IDialogGenerator dialog)
{
OpenDialogs.Add(dialog);
dialog.DialogClosed += Dialog_DialogClosed;
dialog.ShowDialog();
return OpenDialogs.Count - 1;
}
private void Dialog_DialogClosed()
{
OpenDialogs.RemoveAt(OpenDialogs.Count - 1);
}
}
One important point I have to deal with is that closing the dialog and removing it from the store didn't dispose of the scoped dialogGenerator, so I will still need to dispose from anything inside my dialogGenerator before removing it from my dialogStore.
If anyone has a better way to deal with dialog windows in MVVM with Dependency Injection, I'll be glad to hear it.

How to get a number input from the ViewModel in Xamarin forms

I need to request a number from the user after a command has been run. This needs to run in the view model. I've tried ACR.UserDialog, but I can't seem to figure out how to call it from the viewmodel.
Something like this:
void RemoveItem()
{
int intQuantity = (dialog from user to get the quantity);
}
Thanks in advance!
Do you want to achieve the result like following result with Command in viewModel.
First of all, you need to create the AbstractViewModel.
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Acr.UserDialogs;
namespace MyCusListview
{
public abstract class AbstractViewModel : INotifyPropertyChanged
{
protected AbstractViewModel(IUserDialogs dialogs)
{
this.Dialogs = dialogs;
}
protected IUserDialogs Dialogs { get; }
protected virtual void Result(string msg)
{
this.Dialogs.Alert(msg);
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Then implement this abstract class, do not forget to add the IUserDialogs dialogs in your viewmodel constructor. Then push the dialogs.PromptAsync, you can get the result in input.Text.
public class PersonsViewModel: AbstractViewModel
{
public ObservableCollection<Person> persons { get; set; }
public static ObservableCollection<Person> selectItems { get; set; }
public ICommand TextChangeCommand { protected set; get; }
public PersonsViewModel(INavigation navigation, IUserDialogs dialogs, ContentPage page):base(dialogs)
{
TextChangeCommand = new Command<Person>(async (key) =>
{
var input = await dialogs.PromptAsync("What is your email?", "Confirm Email", "Confirm", "Cancel");
if (input.Ok)
{
Console.WriteLine("Your email is" + input.Text);
}
});
}
}
When you binding the viewModel in the background code, you just use following code when init the viewmodel.
public MainPage()
{
InitializeComponent();
personsViewModel = new PersonsViewModel(Navigation, UserDialogs.Instance, this);
this.BindingContext = personsViewModel;
}
Here is running result.
the samples show how to do this
// Dialogs is an IUserDialogs that you pass to your VM from your page
// using UserDialogs.Instance
var result = await Dialogs.PromptAsync(new PromptConfig()
.SetTitle("Max Length Prompt")
.SetPlaceholder("Maximum Text Length (10)")
.SetInputMode(InputType.Name)
.SetMaxLength(10));
// result.Text will contain the user's response

Close/Hide Window from UserControl

I develop a wpf application on which I interact with a huge grid of buttons (built with an ItemsControl).
When I click on one button, the app displays a new window. This new window displays an UserControl.
I use a service to show the new window :
public class WindowService
{
#region Variable
#endregion Variable
#region Constructor
public WindowService()
{
}
#endregion Constructor
#region Properties
#endregion Properties
#region Public Method
public void ShowWindow(object viewModel)
{
var win = new WindowView();
//win.Content = viewModel; <-- not the best way, go to see in comment why (thanks #Ndubuisi Jr)
win.DataContext = viewModel;
win.Show();
}
#endregion Public Method
#region Private Method
#endregion Private Method
}
And the code to call this method :
public void display_InfoPoste(object commandParameter)
{
windowPoste = new WindowService();
windowPoste.ShowWindowCommandParameter(new InfoPosteViewModel(commandParameter));
}
No Problem with that. (The window displayed is only a content to receive different UserControl)
Now, I have a button "close" on the user control, but I don't find any way to close the window.
I work with MVVM pattern, that's why I don't find yet how to do that.
Could you help me?
(I can share a screenshot with you if you need)
Thanks a lot
Picture : Part of the project's arborescence
Just below, the requested code of the "InfoPosteViewModel.cs"
#region Variable
private string _commandParameter;
#endregion Variable
#region Constructor
public InfoPosteViewModel()
{
//FermerCommand = new RelayCommand(Action_FermerWindow);
}
public InfoPosteViewModel(object commandParameter)
{
SelectedViewModel = new InfoPosteViewModel();
_commandParameter = (string)commandParameter;
ID = _commandParameter;
}
#endregion Constructor
#region Properties
public ICommand FermerCommand { get; set; }
private static string _id;
public string ID
{
get { return _id; }
set
{
_id = value;
OnPropertyChanged("ID");
}
}
private object _selectedViewModel;
public object SelectedViewModel
{
get
{
return _selectedViewModel;
}
set
{
_selectedViewModel = value;
OnPropertyChanged("SelectedViewModel");
}
}
#endregion Properties
#region Public Method
public void Action_FermerWindow(object commandParameter)
{
}
#endregion Public Method
Try passing the close command to the VM that is being displayed in the Dialog. Then link that to the close button or action. Because it's the close command of the VM that opened the dialog you can then close the dialog using the reference in that VM and continue with any clean up or follow up code you need.
Oh, and you'll need to make the window an instance variable rather than a local variable.
The cleanest way to close a Window from its ViewModel in MVVM is using an attached property.
public static class perWindowHelper
{
public static readonly DependencyProperty CloseWindowProperty = DependencyProperty.RegisterAttached(
"CloseWindow",
typeof(bool?),
typeof(perWindowHelper),
new PropertyMetadata(null, OnCloseWindowChanged));
private static void OnCloseWindowChanged(DependencyObject target, DependencyPropertyChangedEventArgs args)
{
if (!(target is Window view))
return;
if (view.IsModal())
view.DialogResult = args.NewValue as bool?;
else
view.Close();
}
public static void SetCloseWindow(Window target, bool? value)
{
target.SetValue(CloseWindowProperty, value);
}
public static bool IsModal(this Window window)
{
var fieldInfo = typeof(Window).GetField("_showingAsDialog", BindingFlags.Instance | BindingFlags.NonPublic);
return fieldInfo != null && (bool)fieldInfo.GetValue(window);
}
}
This can be used in the View's xaml file
<Window
....
vhelp:perWindowHelper.CloseWindow="{Binding ViewClosed}" />
bound against the ViewClosed property (of type bool?) from the ViewModel. Setting this property value will close the View.
More details on my blog post

Passing data from one VM to another VM, using Unity and prism EventAggregator

Trying to pass data from one ViewModel to another using prism EventAggregator, but when I debug on the subscriber, the data is null.
Using version 5.0 of prism.
Update
Okay, I have tried implement the EventAggregator using prism 5.0 version. It still dosen't work, but here is what I have done.
1: Create event class
public class RoomsSelectedEvent : PubSubEvent<ObservableCollection<Room>>
{
}
2: Inject IEventAggregator on publisher ViewModel (BookingViewModel)
public class BookingViewModel : INotifyPropertyChanged, IViewBookingViewModel
{
//aggregator
protected readonly IEventAggregator _eventAggregator;
//commands
public ICommand ContinueCommand { get; set; }
public ObservableCollection<Room> RoomsList { get; private set; }
public ObservableCollection<RoomList> DropDownRooms { get; private set; }
public ObservableCollection<CustomerList> DropDownCustomers { get; private set; }
//enities
private readonly IDialogService<ContactDetailsView> _dialogServiceContactView;
private readonly IGetRoomsService _getRoomsService;
public BookingViewModel(IDialogService<ContactDetailsView> dialogServiceContactview, IGetRoomsService GetRoomsService, IEventAggregator eventAggregator)
{
// Injection
_dialogServiceContactView = dialogServiceContactview;
_getRoomsService = GetRoomsService;
_eventAggregator = eventAggregator;
//commands
ContinueCommand = new RelayCommand(ContinueCommand_DoWork, () => true);
}
// Continue Command
public void ContinueCommand_DoWork(object obj)
{
ObservableCollection<Room> RoomsSelected = new ObservableCollection<Room>();
RoomsSelected = _getRoomsService.FilterSelectedRooms(RoomsList);
//Publish event:
_eventAggregator.GetEvent<RoomsSelectedEvent>().Publish(RoomsSelected);
// Open new dialog
_dialogServiceContactView.ShowDialog();
}
}
3: Inject IEventAggregator in subscriber viewModel (ContactViewModel)
public class ContactViewModel : IViewContactViewModel, INotifyPropertyChanged
{
//aggregator
protected readonly IEventAggregator _eventAggregator;
//properties
public ObservableCollection<Room> SelectedRooms { get; set; }
public ContactViewModel(IEventAggregator eventAggregator)
{
//Injection
_eventAggregator = eventAggregator;
//Subscripe to event
_eventAggregator.GetEvent<RoomsSelectedEvent>()
.Subscribe((data) => { SelectedRooms = data; });
}
public ObservableCollection<Room> Rooms
{
get { return SelectedRooms; }
set { SelectedRooms = value; NotifyPropertyChanged(); }
}
}
I have read it's maybe because of the IEventAggregator is not the same in both ViewModels. I'm using Unity to inject it like this code:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
//view & viewModels
_container = new UnityContainer();
_container.RegisterType<IViewMainWindowViewModel, MainWindow>();
_container.RegisterType<IViewMainWindowViewModel, MenuViewModel>();
_container.RegisterType<IViewBookingViewModel, BookingView>();
_container.RegisterType<IViewBookingViewModel, BookingViewModel>();
_container.RegisterType<IViewContactViewModel, ContactDetailsView>();
_container.RegisterType<IViewContactViewModel, ContactViewModel>();
_container.RegisterType<IGetRoomsService, GetRoomsService>();
_container.RegisterType<IPostReservationService, PostReservationService>();
_container.RegisterType<IGetReservationsListService, GetReservationsListService>();
//types
_container.RegisterType<IEventAggregator, EventAggregator>(new ContainerControlledLifetimeManager());
_container.RegisterType(typeof(IDialogService<>), typeof(DialogService<>));
_container.Resolve<MainWindow>().Show();
}
Found that I need to add the ContainerControlledLifetimeManager, but it still wont work.
When I debug in the subscriber viewModel, I can see that there is an event inside the instance, like on the image:
It wont catch it , thats the problem :(
Today I tried delete all packages I installed (prism 5.0) and go for the Prism.Core (6.1 version)
That resulted in the same, I can see when I debug that the event in published, but when I subscribe, it's still null.

Issue with putting Command CanExecute and Executed handlers out of the main window class

Basically I've got a command binding for the command itself assigned to Window.CommandBindings:
<CommandBinding Command="local:TimerViewModel.AddTimer"
CanExecute="local:TimerViewModel.AddTimer_CanExecute"
Executed="local:TimerViewModel.AddTimer_Executed" />
local is a namespace generated by default pointing to the namespace of the application. What I'm trying to achieve here is to have the command handling inside the TimerViewModel but I keep getting the following error:
CanExecute="local:TimerViewModel.AddTimer_CanExecute" is not valid. 'local:TimerViewModel.AddTimer_CanExecute' is not a valid event handler method name. Only instance methods on the generated or code-behind class are valid.
The TimerViewModel is pretty simple though but I believe I am missing something:
public class TimerViewModel : ViewModelBase
{
public TimerViewModel()
{
_timers = new ObservableCollection<TimerModel>();
_addTimer = new RoutedUICommand("Add Timer", "AddTimer", GetType());
}
private ObservableCollection<TimerModel> _timers;
public ObservableCollection<TimerModel> Timers
{
get { return _timers; }
}
private static RoutedUICommand _addTimer;
public static RoutedUICommand AddTimer
{
get { return _addTimer; }
}
public void AddTimer_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
public void AddTimer_Executed(object sender, ExecutedRoutedEventArgs e)
{
_timers.Add(new TimerModel(TimeSpan.FromSeconds((new Random()).Next())));
}
}
Can anyone point out the mistakes I'm making?
Also take a look at Josh Smith's RelayCommand. Using it would enable you to write the above like this:
public class TimerViewModel : ViewModelBase {
public TimerViewModel() {
Timers = new ObservableCollection<TimerModel>();
AddTimerCommand = new RelayCommand(() => AddTimer());
}
public ObservableCollection<TimerModel> Timers {
get;
private set;
}
public ICommand AddTimerCommand {
get;
private set;
}
private void AddTimer() {
Timers.Add(new TimerModel(TimeSpan.FromSeconds((new Random()).Next())));
}
}
Take a look at http://www.wpftutorial.net/DelegateCommand.html for an example of how to implement the delegate command for WPF. It allows you to hook up Execute and CanExecute as event handlers. If you're using RoutedUICommand directly you need to derive a custom command from it and override Execute and CanExecute with your functions.

Categories