I am new to Wpf and Caliburn and I have the following scenario that i desire some help with, I am sure that the code is correct but this is not working as expected, to elaborate
My AppBootstrapper is thus:
public class AppBootstrapper : BootstrapperBase
{
private CompositionContainer _container;
public AppBootstrapper()
{
this.Initialize();
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
this.DisplayRootViewFor<ShellViewModel>();
}
protected override void Configure()
{
try
{
this._container = new CompositionContainer(new AggregateCatalog(new DirectoryCatalog(".", "*")));
var batch = new CompositionBatch();
batch.AddExportedValue<IWindowManager>(new WindowManager());
batch.AddExportedValue<IEventAggregator>(new EventAggregator());
batch.AddExportedValue(this._container);
this._container.Compose(batch);
}
catch (Exception ex)
{
this._log.Error(ex.ToString());
}
}
protected override object GetInstance(Type service, string key)
{
try
{
var contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(service) : key;
var exports = this._container.GetExportedValues<object>(contract);
if (exports.Any())
{
return exports.First();
}
throw new Exception(string.Format("Could not locate any instances of contract {0}.", contract));
}
catch (Exception ex)
{
if (ex is ReflectionTypeLoadException)
{
var typeLoadException = ex as ReflectionTypeLoadException;
var loaderExceptions = typeLoadException.LoaderExceptions;
if (loaderExceptions != null)
{
this._log.Error(loaderExceptions.First().ToString());
}
}
throw;
}
}
protected override IEnumerable<object> GetAllInstances(Type service)
{
return this._container.GetExportedValues<object>(AttributedModelServices.GetContractName(service));
}
protected override void BuildUp(object instance)
{
this._container.SatisfyImportsOnce(instance);
}
}
My ShellViewModel is as follows (i have removed some of the code to keep it short)
[Export(typeof(ShellViewModel))]
public class ShellViewModel : Conductor<IScreen>,
IHandle<NavigationEvent>,
IHandle<WindowCloseEnabledEvent>,
IShell,
IShellViewModel
{
private readonly IEventAggregator _eventAggregator;
[ImportingConstructor]
public ShellViewModel(IEventAggregator eventAggregator)
{
this._eventAggregator = eventAggregator;
}
[Import(typeof(LoginViewModel))]
public Lazy<IScreen> LoginViewModel { get; set; }
public void Loaded()
{
this._eventAggregator.Subscribe(this);
this.ActivateItem(this.LoginViewModel.Value);
}
}
I have an event trigger in the ShellView.xaml that triggers the Loaded method above.
I also have a LoginViewModel and LoginView (note that these are in ViewModels and Views folders in the project.
The important bit of LoginViewModel is
[Export(typeof(LoginViewModel))]
public class LoginViewModel : Screen, IAnimatableViewModel, ILoginViewModel
{
protected override void OnViewLoaded(object view)
{
this._view = view;
IoC.Get<ShellViewModel>().DisableClose = true;
base.OnViewLoaded(view);
}
}
And the LoginView.xaml at the moment simply displays some text.
I am using MEF as the DI container, the issue is that when I run up the application the ShellView is loaded which should load the LoginView into it but the LoginView does not load (or doesn't display)
If anyone can help it would be greatly appreciated.
Related
My TabbedPage uses a Binding Property, which is defined in the tabbed page's ViewModel, for showing a Badge text.
I am setting the badge property when initializing the view (actually when it (re)appears). However, sometimes the badge text is changing from outside of my ViewModel(s), this is because I have a SignalR method which is called when a new message is being added by another application.
Though, when this happens the OnAppearing method of my tabbed viewmodel is obviously not called. So the question is, how can I 'notify' the tabbedpage viewmodel that the badge text should be changed.
I think the (best) way to do this is using somekind of Event. Since all of my ViewModels inherit from a 'ViewModelBase' I could implement the event notification / change in the ViewModelBase and override the property in my TabbedPage ViewModel.
Though, sadly my knowledge about using Events / EventArgs is limited and the stuff I found about it is not working.
Is using EventArgs the best way to solve this problem? And if so, could anyone give any pointers how to implement it properly.
*On a side-note, I am also using Prism
My TabbedPage ViewModel:
public class RootTabbedViewModel : ViewModelBase, IPageLifecycleAware
{
private readonly INavigationService _navigationService;
private int _messageCount;
public RootTabbedViewModel(INavigationService navigationService)
: base(navigationService)
{
_navigationService = navigationService;
}
public int MessageCount
{
get { return _messageCount; }
set { SetProperty(ref _messageCount, value); }
}
public void OnDisappearing()
{
}
void IPageLifecycleAware.OnAppearing()
{
// (omitted) Logic for setting the MessageCount property
}
}
ViewModelVase:
public class ViewModelBase : BindableBase, IInitialize, IInitializeAsync, INavigationAware, IDestructible, IActiveAware
{
public event EventHandler MessageAddedEventArgs; // this should be used to trigger the MessageCount change..
protected INavigationService NavigationService { get; private set; }
public ViewModelBase(INavigationService navigationService)
{
NavigationService = navigationService;
Connectivity.ConnectivityChanged += Connectivity_ConnectivityChanged;
IsNotConnected = Connectivity.NetworkAccess != NetworkAccess.Internet;
}
private bool _isNotConnected;
public bool IsNotConnected
{
get { return _isNotConnected; }
set { SetProperty(ref _isNotConnected, value); }
}
~ViewModelBase()
{
Connectivity.ConnectivityChanged -= Connectivity_ConnectivityChanged;
}
async void Connectivity_ConnectivityChanged(object sender, ConnectivityChangedEventArgs e)
{
IsNotConnected = e.NetworkAccess != NetworkAccess.Internet;
if (IsNotConnected == false)
{
await DataHubService.Connect();
}
}
public virtual void Initialize(INavigationParameters parameters)
{
}
public virtual void OnNavigatedFrom(INavigationParameters parameters)
{
}
public virtual void OnNavigatedTo(INavigationParameters parameters)
{
}
public virtual void Destroy()
{
}
public virtual Task InitializeAsync(INavigationParameters parameters)
{
return Task.CompletedTask;
}
}
SignalR Datahub which should trigger the event:
public static class DataHubService2
{
// .. omitted some other SignalR specific code
public static async Task Connect()
{
try
{
GetInstanse();
hubConnection.On<Messages>("ReceiveMessage", async (message) =>
{
if(message != null)
{
// event that message count has changed should be triggered here..
}
});
}
catch (Exception ex)
{
// ...
}
}
}
As pointed out by #Jason, this specific problem is a good use case for using the MessagingCenter.
In the end the implementation looks as following:
public static class DataHubService2
{
// .. omitted some other SignalR specific code
public static async Task Connect()
{
try
{
GetInstanse();
hubConnection.On<Messages>("ReceiveMessage", async (message) =>
{
if(message != null)
{
MessagingCenter.Send("UpdateMessageCount", "Update");
}
});
}
catch (Exception ex)
{
// ...
}
}
}
public class RootTabbedViewModel : ViewModelBase, IPageLifecycleAware
{
private readonly INavigationService _navigationService;
private int _messageCount;
public RootTabbedViewModel(INavigationService navigationService)
: base(navigationService)
{
_navigationService = navigationService;
MessagingCenter.Subscribe<string>("UpdateMessageCount", "Update", async (a) =>
{
await UpdateMessageCount();
});
}
public int MessageCount
{
get { return _messageCount; }
set { SetProperty(ref _messageCount, value); }
}
public void OnDisappearing()
{
}
void IPageLifecycleAware.OnAppearing()
{
UpdateMessageCount();
}
async Task UpdateMessageCount()
{
int messageCount = await App.Database.GetNewMessageCountAsync();
MessageCount = messageCount.ToString();
}
}
I am updating the old web forms project which is working on the MVP model. While refactoring that into the layered project I started DI using unity.
I have a below files with my new DI, but in my Presenter parameterized constructor is not called and ObjView is returning null. How can I fix this error
IUsersView
{
int UserID { get; set; }
List<UsersModel> oPTList { get; set; }
//Have only few methods
}
and i have page shown below
Users(.aspx.cs) : System.Web.UI.Page, IUsersView
{
UserPresenter obj = null; //Old code
IUserPresenter _userPresenter = null;
public readonly IUsersView _userView;
public readonly IUser _user;
public User(IUser user,
IUserPresenter userPresenter)
{
_user= user;
_userPresenter = userPresenter;
}
protected void Page_Load(object sender, EventArgs e)
{
try{
obj = new UserPresenter(this, _projectUpload); //Old Code
obj.CheckLoginUserExist(usrLoginID, usrName); //Old Code
_UserPresenter.CheckLoginUserExist(usrLoginID, usrName); // New Code with DI
}
}
}
and Presenter code
public class UserPresenter : IUserPresenter
{
IUserView objView;
IUser _user= null;
public UserPresenter()
{
}
public UserPresenter(IUserView view, IUser user)
{
objView = view;
_user = user;
}
public void CheckLoginUserExist(int usrLoginID, string usrName)
{
objView.oPTList = objBL.GetUserRoles(usrLoginID,usrName); //Old Code and i am getting date from DAL layer
objView.GetUserRoles(); //Old Code
}
}
try this, it looks like you have defined your interface incorrectly.
Users(.aspx.cs) : System.Web.UI.Page, IUsersView
{
IUserPresenter _userPresenter;
public readonly IUsersView _userView;
public readonly IUser _user;
public User(IUser user,
IUserPresenter userPresenter)
{
_User = user;
_userPresenter= userPresenter;
}
protected void Page_Load(object sender, EventArgs e)
{
try{
obj = new UserPresenter(this, _projectUpload); //Old Code
obj.CheckLoginUserExist(usrLoginID, usrName); //Old Code
_userPresenter.CheckLoginUserExist(usrLoginID, usrName); // New Code with DI
}
}
}
experts! I'm new in learning WPF and MVVM. I decided to develop some small WPF application. So I have a LoginViewModel which interacts with Database. Once the login operation is successful I need to hide the LoginView and display the MainView. I'm using the Caliburn.Micro for these purposes. But I have got a problem - I don't know how can I hide the LoginView and show the MainView. I would be appreciated if someone would help me to solve this problem :(
I've already tried to use the following actions:
- Using the IEventAggregator _events: _events.PublishOnUIThread("message"), but no result :(
- Using the Conductor.Collection.OneActive and ActiveteItem()/DeactivateItem() methods, but still no result :(
Here is my Bootstrapper class:
public class Bootstrapper : BootstrapperBase
{
private SimpleContainer _container = new SimpleContainer();
public Bootstrapper()
{
Initialize();
ConventionManager.AddElementConvention<PasswordBox>(
PasswordBoxHelper.BoundPasswordProperty,
"Password",
"PasswordChanged");
}
protected override void Configure()
{
_container.Instance(_container);
_container
.Singleton<IWindowManager, WindowManager>()
.Singleton<IApiHelper, ApiHelper>()
.Singleton<IEventAggregator, EventAggregator>()
.Singleton<ILoggedInUserModel, LoggedInUserModel>();
GetType().Assembly.GetTypes()
.Where(type => type.IsClass)
.Where(type => type.Name.EndsWith("ViewModel"))
.ToList()
.ForEach(viewModelType => _container.RegisterPerRequest(
viewModelType, viewModelType.ToString(), viewModelType));
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
DisplayRootViewFor<ShellViewModel>();
}
protected override object GetInstance(Type service, string key)
{
return _container.GetInstance(service, key);
}
protected override IEnumerable<object> GetAllInstances(Type service)
{
return _container.GetAllInstances(service);
}
protected override void BuildUp(object instance)
{
_container.BuildUp(instance);
}
}
Also here is my LoginViewModel:
public class LoginViewModel : Conductor<IScreen>.Collection.OneActive
{
private IWindowManager _windowManager;
private IApiHelper _apiHelper;
private ILoggedInUserModel _user;
private IEventAggregator _events;
private string _login = "test";
private string _password = "test";
private bool _isAdmin;
public ShellViewModel(IWindowManager windowManager, IApiHelper apiHelper, ILoggedInUserModel user, IEventAggregator events)
{
_windowManager = windowManager;
_apiHelper = apiHelper;
_user = user;
_events = events;
}
public string Login
{
get { return _login; }
set
{
_login = value;
NotifyOfPropertyChange(() => Login);
NotifyOfPropertyChange(() => CanPerformLogin);
}
}
public string Password
{
get { return _password; }
set
{
_password = value;
NotifyOfPropertyChange(() => Password);
NotifyOfPropertyChange(() => CanPerformLogin);
}
}
public bool IsAdmin
{
get { return _isAdmin; }
set
{
_isAdmin = value;
NotifyOfPropertyChange(() => IsAdmin);
NotifyOfPropertyChange(() => CanPerformLogin);
}
}
public bool CanPerformLogin
{
get
{
bool output = false;
if (Login?.Length > 0 && Password?.Length > 0)
{
output = true;
}
return output;
}
}
public ShellViewModel(IApiHelper apiHelper, IWindowManager windowManager)
{
_windowManager = windowManager;
_apiHelper = apiHelper;
}
public void OpenSignUpView()
{
_windowManager.ShowWindow(new SignUpViewModel());
}
public async Task PerformLogin()
{
//Here I need to hide my LoginView and show the ShellView
}
public void CloseForm()
{
TryClose();
}
And here is my ShellViewModel:
public class ShellViewModel : Screen, IHandle<LogOnEvent>
{
private IWindowManager _windowManager;
private IApiHelper _apiHelper;
private ILoggedInUserModel _user;
private IEventAggregator _events;
public SelectYourTreeViewModel(IWindowManager windowManager, IApiHelper apiHelper, ILoggedInUserModel user, IEventAggregator events)
{
_windowManager = windowManager;
_apiHelper = apiHelper;
_user = user;
_events = events;
_events.Subscribe(this);
}
public void Handle(LogOnEvent message) //Here I've tried to use events to show/or hide view
{
System.Windows.MessageBox.Show("Some text!");
}
Can you please show the structure of the WindowManager class. ? You can open a window in WPF like this:
new LoginView().Show();
And so close it again.
myAlreadyOpenedLoginView.Close().
Here LoginView must be a window and inherit from the class Window.
My code looks like this:
Bootstrapper.cs
public class Bootstrapper : BootstrapperBase
{
private SimpleContainer _container = new SimpleContainer();
public Bootstrapper()
{
Initialize();
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
base.OnStartup(sender, e);
DisplayRootViewFor<ShellViewModel>();
}
protected override void Configure()
{
_container.Singleton<IEventAggregator, EventAggregator>();
_container.Singleton<IWindowManager, WindowManager>();
_container.RegisterPerRequest(typeof(ShellViewModel), null, typeof(ShellViewModel));
}
protected override object GetInstance(Type service, string key)
{
return _container.GetInstance(service, key);
}
protected override IEnumerable<object> GetAllInstances(Type serviceType)
{
return _container.GetAllInstances(serviceType);
}
protected override void BuildUp(object instance)
{
_container.BuildUp(instance);
}
}
And my ShellViewModel looks like this:
ShellViewModel.cs
public class ShellViewModel : Conductor<Screen>
{
public ShellViewModel
{
var aViewModel = IoC.Get<AViewModel>();
ActivateItem(aViewModel);
}
}
But whenever I run the program, a blank screen is shown. When I debug it, it said that the aViewModel is null.
Is there anything wrong with the Bootstrapper?
Based on the code provided, AViewModel is not registered with the container in the Bootstrapper so IoC does not know it exists, thus it will return null when requested to Get that type
For example
_container.RegisterPerRequest(typeof(AViewModel), null, typeof(AViewModel));
All types that need to be resolved by IoC should first be registered with the backing container.
I've added a DialogService in order to open a ProductView, so far the ShowDetailDialog() is working as expected.
Issue:
I call Close() on the ProductView, the view isn't closed. I debugged this issue by setting a break point on the call to the dialog service close method.
When I stepped through the code, the null check shows that productView is null, which prevents Close() from being called.
Does anyone have idea why productView is null? (although it's showing data on the view)
DialogService:(hosts the Show and Close methods)
namespace MongoDBApp.Services
{
class DialogService : IDialogService
{
Window productView = null;
ProductView _productView;
public DialogService()
{
_productView = new ProductView();
}
public void CloseDetailDialog()
{
if (productView != null)
productView.Close();
}
public void ShowDetailDialog()
{
_productView.ShowDialog();
}
}
}
ProductViewModel: (summary of ProductVM, calls the close method on SaveCommand)
private void SaveProduct(object product)
{
_dialogService.CloseDetailDialog();
Messenger.Default.Send<ProductModel>(SelectedProduct);
}
CustomerOrdersViewmodel: (Where the ShowDetailDialog() is called initially)
private void EditOrder(object obj)
{
Messenger.Default.Send<ProductModel>(SelectedProduct);
_dialogService.ShowDetailDialog();
}
This is how I have always closed my windows.
Here would be my command:
class CancelCommand : ICommand
{
private NewTruckViewModel newTruck;
public CancelCommand(NewTruckViewModel vm)
{
newTruck = vm;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
newTruck.Cancel();
}
}
Here is my view Model and the method that gets called from my command:
private NewTruck myWnd; //View Declaration
//Ctor where I set myView (myWnd) equal to a view that is passed in.
public NewTruckViewModel(ObservableCollection<Truck> Trucks, NewTruck wnd, bool inEditTruck)
{
myEngine.stopHeartBeatTimer();
editTruck = inEditTruck;
myWnd = wnd;
SaveTruckCommand = new SaveTruckCommand(this);
CancelCommand = new CancelCommand(this);
ClearCommand = new ClearCommand(this);
SetLevel1MTCommand = new SetLevel1MTCommand(this);
SetLevel2MTCommand = new SetLevel2MTCommand(this);
SetLevel3MTCommand = new SetLevel3MTCommand(this);
SetLevel1FLCommand = new SetLevel1FLCommand(this);
SetLevel2FLCommand = new SetLevel2FLCommand(this);
SetLevel3FLCommand = new SetLevel3FLCommand(this);
myTrucks = Trucks;
}
public void Cancel()
{
myWnd.Close();
}
This works for me.
I resolved the issue by implementing an IDialogService on the View. Then calling the Show() and Close() methods from the ViewModel.
Solution:
Interface:
public interface IDialogService
{
void CloseDialog();
void ShowDialog(EditProductViewModel prodVM);
}
View:
public partial class ProductView : Window, IDialogService
{
public ProductView()
{
InitializeComponent();
this.DataContext = new EditProductViewModel(this);
}
public void CloseDialog()
{
if (this != null)
this.Visibility = Visibility.Collapsed;
}
public void ShowDialog(EditProductViewModel prodVM)
{
this.DataContext = prodVM;
this.Show();
}
private void Window_Closed(object sender, EventArgs e)
{
this.Visibility = Visibility.Collapsed;
}
}
ViewModel #1:
private IDialogService _dialogService;
public CustomerOrdersViewModel(IDialogService dialogservice)
{
this._dialogService = dialogservice;
}
private void EditOrder(object obj)
{
EditProductViewModel pvm = new EditProductViewModel(_dialogService);
pvm.Present(pvm);
Messenger.Default.Send<ProductModel>(SelectedProduct);
}
ViewModel #2:
private IDialogService _dialogService;
public EditProductViewModel(IDialogService dialogService)
{
this._dialogService = dialogService;
}
private void SaveProduct(object product)
{
SelectedProduct = SelectedProductTemp;
_dialogService.CloseDialog();
}
public void Present(EditProductViewModel prodVM)
{
_dialogService.ShowDialog(prodVM);
}