I am currently experimenting with the ChildWindowDialog and with prism I have created a controller class. I would like my popup to be displayed on all the screen (a bit like fullscreen mode). I have HtmlPage.Window.Eval() below but I am not sure if this is the correct thing to do. One of the reasons it feels wrong is I have no idea how to test this class in the future. Also, I have coupled the controller to the Browser class which will mean I could not reuse it in a WPF app.
public class GalleryCoverFlowChildWindowController
{
private readonly IEventAggregator _eventAggregator;
private readonly IUnityContainer _container;
public GalleryCoverFlowChildWindowController(IEventAggregator eventAggregator, IUnityContainer container)
{
_eventAggregator = eventAggregator;
_container = container;
_eventAggregator.GetEvent<GalleryCoverViewPopupEvent>().Subscribe(PopupShow, ThreadOption.UIThread, true, Filter);
}
private bool Filter(string obj)
{
return true;
}
private void PopupShow(string obj)
{
var galleryPopup = _container.Resolve<GalleryCoverFlowChildWindow>();
galleryPopup.Width = (double)System.Windows.Browser.HtmlPage.Window.Eval("screen.availWidth");
galleryPopup.Height = (double)System.Windows.Browser.HtmlPage.Window.Eval("screen.availHeight");
galleryPopup.Show();
}
}
JD.
To resolve the issue with coupling, I created a ScreenService and injected it in via Unity. That way I do not have a dependency on DOM. This will make testing the code easier.
Any thoughts?
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".
Recently I am going through some old code and found the below code
public class ProfileModule : IModule
{
private readonly IRegionManager regionManager;
private readonly IUnityContainer container;
private IEventAggregator eventAggregator;
public ProfileModule(IUnityContainer c, IRegionManager r, IEventAggregator e)
{
container = c;
regionManager = r;
eventAggregator = e;
}
public void Initialize()
{
// Create and add profiles as new Tab items
container.RegisterType<IProfileViewModel, Profile1ViewModel>(new ContainerControlledLifetimeManager());
regionManager.Regions[RegionNames.HomeRegion].Add(container.Resolve<ProfileView>());// HomeRegion is of type TabControl
container.RegisterType<IProfileViewModel, Profile2ViewModel>(new ContainerControlledLifetimeManager());
regionManager.Regions[RegionNames.HomeRegion].Add(container.Resolve<ProfileView>());
container.RegisterType<IProfileViewModel, Profile3ViewModel>(new ContainerControlledLifetimeManager());
regionManager.Regions[RegionNames.HomeRegion].Add(container.Resolve<ProfileView>());
}
}
Below is the ProfileView.xaml.cs
public partial class ProfileView : INotifyPropertyChanged
{
[InjectionConstructor]
public ProfileView(IProfileViewModel vm)
{
DataContext = vm;
InitializeComponent();
}
}
Below are the viewModels
public abstract class ProfileViewModelBase : IProfileViewModel, IDataErrorInfo, INotifyPropertyChanged
{
public ProfileViewModelBase(IEventAggregator eventAggregator, IRegionManager regionManager)
{
}
}
public class Profile1ViewModel : ProfileViewModelBase
{
[InjectionConstructor]
public Profile1ViewModel(IEventAggregator eventAggregator, IRegionManager regionManager)
: base (eventAggregator, regionManager)
{
}
}
public class Profile2ViewModel : ProfileViewModelBase
{
[InjectionConstructor]
public Profile2ViewModel(IEventAggregator eventAggregator, IRegionManager regionManager)
: base (eventAggregator, regionManager)
{
}
}
public class Profile3ViewModel : ProfileViewModelBase
{
[InjectionConstructor]
public Profile3ViewModel(IEventAggregator eventAggregator, IRegionManager regionManager)
: base (eventAggregator, regionManager)
{
}
}
The part of the code that is not clear for me is the ProfileModule.Initialise().
Everytime when the region manager is adding a view a new new instance of ProfileView is getting created and the viewModel is the one that is registered last.
First time ProfileView is created with Profile1ViewModel as a Datacontext.
Second time ProfileView is created with Profile2ViewModel as a Datacontext.
Third time ProfileView is created with Profile3ViewModel as a Datacontext.
How the container knows exactly which viewmodel to create when creating the view.
Also I understand , container.Resolve will return the view if it already got one, first time view is created and returned, second time I except same view will be returned, but a new view is created. same with third.
Can anyone explain what is happening?
Here goes:
What you can see inside the Initialize method is that after registering the IProfileViewModel the code is then immediately calling Resolve<ProfileView> which on the first Resolve is providing Profile1ViewModel to the ProfileView constructor. Then the second Register replaces the first registration with Profile2ViewModel. Therefore subsequent calls to Resolve will never give you an instance (or the singleton instance) of Profile1ViewModel.
If for some reason you really want to resolve the same instance of ProfileView then you need to Register this with the Unity container as a singleton like the below.
container.RegisterType(new ContainerControlledLifetimeManager());
This is obviously assuming you have an interface defined called IProfileView
I got confused after reading the documentation of Unity framework.
link
I'am writing a WPF application which will search for some devices.
Below my code from my main Window.
As you can see, now i'am still declaring UnitOfWork and DeviceService inside my main Window. I want to replace this code by applying Dependency Injection. At the same time i would also inject my viewmodel inside my main window.
public Window1()
{
InitializeComponent();
UnitOfWork myUnitOfWork = new UnitOfWork();
DeviceService dService = new DeviceService(myUnitOfWork);
_vm = new DeviceViewModel(dService);
this.DataContext = _vm;
_vm.SearchAll();
}
I gave a try in below code but i failed in setting the container. The real question is how should i start? Do i need to completely change the stucture of my program?
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
IUnityContainer container = new UnityContainer();
UnitOfWork myUnitOfWork = new UnitOfWork();
container.RegisterInstance<UnitOfWork>(myUnitOfWork);
Window1 w1 = new Window1();
w1.Show();
}
}
I went trough the suggested tutorial. It is still not clear for me on how i should configure the property injection.
My viewmodel should be injected in the Window 1 class, so i assume that i have to create a dependency property.
private DeviceViewModel viewModel;
[Dependency]
public DeviceViewModel ViewModel
{
get { return viewModel; }
set { this.DataContext = value; }
}
How can i inject my viewmodel into window 1, knowing that DeviceViewModel has dependency on DeviceService and again on UnitOfWork ?
//CONSTRUCTOR
public DeviceViewModel(DeviceService service)
{
Service = service;
SearchCommand = new SearchCommand(this);
}
private UnitOfWork myUnit;
public DeviceService(UnitOfWork unit)
{
myUnit = unit;
}
You need to tell container how to build all the objects needed by other objects, then the container will instantiate whatever is needed when needed.
your property injection is only missing one line:
private DeviceViewModel viewModel;
[Dependency]
public DeviceViewModel ViewModel
{
get { return viewModel; }
set { viewModel = value; this.DataContext = viewModel; }
}
Then on you OnStartup()
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
IUnityContainer container = new UnityContainer();
container.RegisterType<UnitOfWork>();
container.RegisterType<DeviceService>();
container.RegisterType<DeviceViewModel>();
Window1 w1 = container.Resolve<Window1>();
w1.Show();
}
There are different parameters you can use in RegisterType(), so you can control the lifetime and the creation of your objects.
You need to go through this example: http://visualstudiogallery.msdn.microsoft.com/3ab5f02f-0c54-453c-b437-8e8d57eb9942
You are on the right tracks, just you should resolve the window, not new it up.
//instead of Window1 w1 = new Window1();
Window1 w1 = container.Resolve<Window1>();
w1.DataContext = container.Resolve<DeviceViewModel>();
Window1 will no longer need to set its own DataContext
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).
Any ideas on how i get MVP working with Silverlight? How Do I get around the fact there is no load event raised?
This the view I have:
public partial class Person: IPersonView
{
public event RoutedEventHandler Loaded;
public Person()
{
new PersonPresenter(this);
InitializeComponent();
}
public Person Person
{
set { Person.ItemsSource = value; }
}
}
And my presenter:
public class PersonPresenter
{
private readonly IPersonView _view;
private readonly ServiceContractClient _client;
public PersonPresenter(IPersonView view)
{
_client = new ServiceContractClient();
_view = view;
WireUpEvents();
}
private void WireUpEvents()
{
_view.Loaded += Load;
}
private void Load(object sender, EventArgs e)
{
_client.GetPersonCompleted += Client_GetPerson;
_client.GetPersonAsync();
}
private void Client_GetPerson(object sender, GetPersonCompletedEventArgs e)
{
_view.Person= e.Result;
}
}
Nothing happened for me as the Loaded event dont seem to get called, how do i get around this?
Tim Ross has a good introduction to Silverlight MVP implementation, with source code.
I believe the loaded event gets called when the control has been initialized, loaded, rendered and ready for use. This means that as long as you don't place it inside a visible container (so that it is rendered), the loaded event won't be risen.
You may consider using MVC# - a Model View Presenter framework with Silverlight 2.0 support.
Oleg Zhukov