I am attempting to translate a WPF example of IOC using StructureMap into Silverlight using AutoFac
This is proving to be very difficult
I have got a static BootStrapper class defined
public class BootStrapper
{
public static IContainer BaseContainer { get; private set; }
public static FlexContractStructureViewModel FlexContractStructureViewModel()
{
return BaseContainer.Resolve<FlexContractStructureViewModel>();
}
public static void Build()
{
if (BaseContainer == null)
{
var builder = new ContainerBuilder();
builder.RegisterAssemblyTypes();
BaseContainer = builder.Build();
}
}
static BootStrapper()
{
}
}
This is initialised in the Application_Startup in App.xaml.cs
private void Application_Startup(object sender, StartupEventArgs e)
{
BootStrapper.Build();
this.RootVisual = new MainPage();
}
I have set the DataContext of one of my views to use my BootStrapper
DataContext="{Binding Path=FlexContractStructureViewModel,
Source={StaticResource classes:BootStrapper}}"
But I get the error Cannot find a Resource with the Name/Key classes:BootStrapper
The book I am using states to make a change to the App.xaml to add
But I cant do that because ObjectDataProvider is not recognised
I have tried the equivalent below with no luck
<bs:BootStrapper xmlns:bs="clr-namespace:SLDashboard2.Classes" x:Key="BootStrapper"/>
I think this may be related to having BootStrapper static? But I dont want to be constantly creating new Containers
Can someone help please?
Paul
Wrong. Shouldn't you be registering all your ViewModels in your IoC? and then you inject them to your constructors. They should never be static and I don't usually use static resources as my datacontext in my view
Related
I have been trying to set up dependency injection in a wpf application using Unity, but can't seem to fully understand how the views and viewmodels should be set up.
Have looked into another SO post --> Wpf Unity but can't seem to understand it quite yet. I have used Unity before, but just in a MVC application, so I know how to inject it in the contructors.
Here is my views and viewModels in the application.
Views:
MainWindow.xaml
BookingView.xaml
ContactDetailsView.xaml
ReservationsView.xaml
ViewModels:
MenuViewModel (MainWindow uses this viewModel)
BookingViewModel
ContactViewModel
ReservationsViewModel
My ViewModels all have Interfaces implemented, like IMenuViewModel, should the view also have an interface?
I guess that since the MainWindow is the starting point, it should be here to register the container right?
Update:
Have found something, but not sure if I have done it right. Here is what I have done so far!
1: Using startup method in app.cs
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
IUnityContainer container = new UnityContainer();
container.RegisterType<IViewMainWindowViewModel, MainWindow>();
container.RegisterType<IViewMainWindowViewModel, MenuViewModel>();
var mainWindow = container.Resolve<MainWindow>(); // Creating Main window
mainWindow.Show();
}
}
2: Remove uri from start up.
3: Make IViewMainWindowViewModel interface in MainWindow class, the interface is empty.
public interface IViewMainWindowViewModel
{
}
4: Make a reference to this interface in the MainWindow
public partial class MainWindow : Window, IViewMainWindowViewModel
{
public MainWindow(IViewMainWindowViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
}
5: Also for the MenuViewModel
public class MenuViewModel : IViewMainWindowViewModel
{
Code not shown!
}
This will not even start the application..
Update 2
My MainWindow class look like this:
public interface IViewMainWindowViewModel
{
}
public partial class MainWindow : Window, IViewMainWindowViewModel
{
public MainWindow(IViewMainWindowViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
}
App class now look like this:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
IUnityContainer container = new UnityContainer();
container.RegisterType<IViewMainWindowViewModel, MainWindow>();
container.RegisterType<IViewMainWindowViewModel, MenuViewModel>();
container.Resolve<MainWindow>().Show();
//Do the same actions for all views and their viewmodels
}
I get an exception on this line when running the application
container.Resolve<MainWindow>().Show();
Update 3
In my MenuViewModel it has two command which will open two views, do I then need to inject those views in the MenuViewModel's constructor or can you just make another empty interface between MenuViewModel and BookingView as an example?
Let me show an example with explanations just for your MainWindows, as for the rest views and viewmodels steps to do are the same.
At first, you should create a contract between View and ViewModel. It shoud be some interface and let it call IViewMainWindowViewModel (keep in mind that name has to be different for other view and viewModels, for example IViewBookingViewViewModel):
public interface IViewMainWindowViewModel
{
/*This interface should not have any methods or commands. It is just
contract between View and ViewModels and helps to decide to Unity
container what it should inject(appropriate viewModel to necessary
View)*/
}
then in your viewmodel we should implement this interface:
public MenuViewModel:IViewMainWindowViewModel
{}
The view should inject this interface MainWindows.xaml.cs:
public partial class MainWindows : UserControl, IContentAView
{
public MainWindows(IViewMainWindowViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
}
}
Delete StartupUri and override a method OnStartup in App.xaml.cs:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
IUnityContainer container = new UnityContainer();
container.RegisterType<IViewMainWindowViewModel, MainWindow>();
container.RegisterType<IViewMainWindowViewModel, MainWindowViewModel >();
container.Resolve<MainWindow>().Show();
//Do the same actions for all views and their viewmodels
}
I would like create generic behavior. My problem is generic declaration in XAML.
public class GenericBehavior<T> : Behavior<DataGrid>
where T : class
{
}
I can’t use x:TypeArguments because I don’t have loose XAML file.
In WPF and when targeting .NET Framework 4, you can use XAML 2009
features together with x:TypeArguments but only for loose XAML (XAML
that is not markup-compiled). Markup-compiled XAML for WPF and the
BAML form of XAML do not currently support the XAML 2009 keywords and
features
I found some workaround with MarkupExtension but with Behaviors not work.
In my current solution I attach behavior in code.
Any idea?
you can create the generic Behavior inside your view model then inject it to your control using attached properties.
public class ViewModel
{
public Behavior MyBehavior
{
get
{
return new GenericBehavior<SomeType>();
}
}
}
public class AttachedBehaviors
{
public static Behavior GetBehavior(DependencyObject sender) => (Behavior)sender.GetValue(BehaviorProperty);
public static void SetBehavior(DependencyObject sender, Behavior value) => sender.SetValue(BehaviorProperty, value);
public static readonly DependencyProperty BehaviorProperty =
DependencyProperty.RegisterAttached("Behavior", typeof(Behavior), typeof(AttachedBehaviors),
new PropertyMetadata(null, new PropertyChangedCallback(BehaviorChanged)));
private static void BehaviorChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
if (sender is FrameworkElement elem)
{
if (e.NewValue is Behavior behavior)
{
BehaviorCollection Behaviors = Interaction.GetBehaviors(elem);
Behaviors.Add(behavior);
}
}
}
}
public class GenericBehavior<T> : Behavior<DataGrid> where T : class
{
public T TestValue { get; set; }
protected override void OnAttached()
{
}
}
now you can use it like this
<DataGrid local:AttachedBehaviors.Behavior="{Binding MyBehavior}" >
</DataGrid>
PS: you just need to install Microsoft.Xaml.Behaviors.Wpf NuGet package.
How do we inject IRegionManager in the ViewModel using MEF Container. I have to switch view in my ViewModel's Command delegate. Here is the brief description of what I am doing. I have an entity called Product whose list is displayed in one View (ProductListView). In that view the user can select the Product and click on Edit button. This would switch the view and present a new View(ProductEditView). For activating a different view, I would need a reference to IRegionManager something like this
public class ProductListVM : NotificationObject { //The Product List View Model
[Import]
public IRegionManager RegionManager { get; set; }
private void EditProduct() { //EditCommand fired from ProductListView
IRegion mainContentRegion = RegionManager.Regions["MainRegion"];
//Switch the View in "MainContent" region.
....
}
}
The above code fails with NullReferenceException for RegionManager. This seems logical because the above View Model is constructed by WPF through DataContext property in Xaml and DI doesn't come into play, so it doesn't get a chance to import the RegionManager instance. How do we resolve the IRegionManager in this scenario.
The Container instance can be exported in the bootstrapper using following
container.ComposeExportedValue<CompositionContainer>(container);
Then in the viewmodel, the IRegionManager instance can be imported using the code
IServiceLocator serviceLocator = ServiceLocator.Current;
CompositionContainer container = serviceLocator.GetInstance<CompositionContainer>();
RegionManager = container.GetExportedValue<IRegionManager>();
However, referring a View in ViewModel is a violation of the MVVM pattern. But since I was following an article here to learn Prism , I had to get along the same. Also the article was in Silverlight and I had to find a way to import RegionManager in wpf, which is little different.
regards,
Nirvan.
Try using [ImportingConstructor] like this:
public class ProductView : Window
{
private IProductViewModel viewModel;
[ImportingConstructor]
public ProductView(IProductViewModel ViewModel)
{
this.viewModel = ViewModel;
this.DataContext = this.viewModel;
}
}
public class ProductViewModel: IProductViewModel, INotifyPropertyChanged
{
private IRegionManager regionManager;
private ICommand editUserCommand;
[ImportingConstructor]
public ProductViewModel(IRegionManager InsertedRegionManager)
{
this.regionManager = InsertedRegionManager;
editUserCommand = new DelegateCommand(ExecuteEditUserCommand, CanExecuteEditUserCommand);
}
public ICommand EditUserCommand
{
get {return this.editUserCommnad;}
}
private bool CanExecuteEditUserCommand()
{
return true;
}
private void ExecuteEditUserCommand()
{
this.regionManager......
}
}
im passing my webservice url to my silverlight application via the parameters.
when my application launches it creates the viewmodel for the mainpage before it application_startup event is fired.
in my viewmodel constructor i have a call to my serviceagent to load some data from the webservice, but the webservice url is not initialised yet due the the viewmodel being constructed before the application_startup event is raised. whats the best way to get around this. Its a friday evening and my brain seems to be pretty fried trying to think of a good solution.
An instance of the ViewModelLocator is created in the app.xaml
<vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" />
Then in the ViewModelLocator constructor there is a call to create the main page
public ViewModelLocator()
{
CreateMain();
}
public static void CreateMain()
{
if (_main == null) _main = new MainViewModel();
}
and in my MainViewModel i make a call to my serviceagent
public MainViewModel() : this(new MyServiceAgent()) { }
public MainViewModel(IMyServiceAgent myServiceAgent)
{
if (IsInDesignMode)
{
}
else
{
ServiceAgent = myServiceAgent;
ServiceAgent.GetData();
RegisterMessageListeners();
WireUpCommands();
}
}
App.xaml.cs
public App()
{
Startup += Application_Startup;
Exit += Application_Exit;
UnhandledException += Application_UnhandledException;
InitializeComponent();
}
private void Application_Startup(object sender, StartupEventArgs e)
{
if (e.InitParams != null && e.InitParams.Count > 0)
ParseInitParams(e.InitParams);
RootVisual = new MainPage();
DispatcherHelper.Initialize();
}
Cheeers
to fix my issue i had to remove the line of code from the viewmodellocator constructor that was initialising the MainViewModel
public ViewModelLocator()
{
//CreateMain();
}
i want to do add custom event handlers to default framework elements using DependencyProperties.
Something like the following:
<Border custom:MyProps.HandleMyEvent="someHandler">...</Border>
Here is the code behind for the control that contains the Border element:
public class MyPage : Page{
public void someHandler(object sender, EventArgs e){
//do something
}
}
Here is rough sample of how i imagine the class that defines the property:
public class MyProps{
public event EventHandler MyInternalHandler;
public static readonly DependencyProperty HandleMyEventProperty = ...
public void SetHandleMyEvent(object sender, EventHandler e){
MyInternalHandler += e;
}
}
The problem is that I don't know/didn't find any hints how to combine DependencyProperties with events/delegates and EventHandlers.
Do you have a clue?
I'm going to assume this has nothing to do with WPF, this is a silverlight question.
First of all you can't simply add an Event to an existing control. After all you are adding attached Properties whereas events are handled differently, they're not properties.
You need to create a new type which has this event then create an attached property of this type.
Here is a basic type that simply has an event:-
public class MyEventer
{
public event EventHandler MyEvent;
// What would call this??
protected void OnMyEvent(EventArgs e)
{
if (MyEvent != null)
MyEvent(this, e);
}
}
Now we create an attached property which has MyEventer as its property, I prefer to place these in a separate static class.
public static class MyProps
{
public static MyEventer GetEventer(DependencyObject obj)
{
return (MyEventer)obj.GetValue(EventerProperty );
}
public static void SetEventer(DependencyObject obj, MyEventer value)
{
obj.SetValue(EventerProperty , value);
}
public static readonly DependencyProperty EventerProperty =
DepencencyProperty.RegisterAttached("Eventer", typeof(MyEventer), typeof(MyProps), null)
}
}
Now you attach this to a control like this:-
<Border ...>
<custom:MyProps.Eventer>
<custom:MyEventer MyEvent="someHandler" />
</custom:MyProps.Eventer>
</Border>
If you compile the project before writing this xaml you'll note that Visual Studio will offer you the option for it to create the event handler in the code behind for you.
Of course this still leaves a significant question: How did you intend to cause the event to fire?