I have a class library which contains several user controls. This library uses caliburn micro to bind the UserControls to their corresponding ViewModels using x:Name approach in the same class library.
Now I have an host application which hosts these UserControls in a Window. But this host application doesn't use Caliburn Micro at all. Then how can I initialize caliburn bootstrapper in the library itself.
HostApplication:
<Window>
<ContentControl>
<library:MainUserControl DataContext="{Binding MainUserControlViewModel}"/>
</ContentControl>
</Window>
Class Library
<UserControl x:Class="MainUserControl">
<ContentControl x:Name="OtherUserControlInSameLibrary"/>
</UserControl>
I tried to inherit Bootstrapper base class like AppBootstrapper: Bootstrapperbase and passing false to constructor parameter useApplication. And then I called Initialize() method. But still conventions are not applied.
MainUserControlViewModel Constructor
public MainUserControlViewModel()
{
AppBootstrapper appBootstrapper = new AppBootstrapper();
appBootstrapper.Initialize();
if (Debugger.IsAttached)
LogManager.GetLog = type => new DebugLog(type);
}
BootstrapperBase override
public class AppBootstrapper : BootstrapperBase
{
public AppBootstrapper():base(false)
{
}
}
I also tried to run caliburn debugger but nothing is showed in my output window.
Putting together the mentioned feedback by Caliburn.Micro mantainers and this blog post by Andy Race, it seems somehow it is doable, but the client application code has still to reference CM as well.
Apart from other needed settings (see references), the constraint is due to how the custom control view-model is bound to its view stated in XAML: the only way is to explicitly set the view-model by means of cal:Bind.Model.
...
xmlns:controls="clr-namespace:Control.Library;assembly=Control.Library"
...
<controls:SomeControlView
cal:Bind.Model="Control.Library.SomeControlViewModel" />
For the rest, the client application can totally ignore Caliburn.Micro.
Related
I am trying to create a simple dialog-type control that is based on the WPF Window class (Popup won't do the trick here).
In my app I register a DataTemplate in Application.Resources:
<Application.Resources>
<DataTemplate DataType="{x:Type local:EntitySelectorViewModel}">
<local:EntitySelector></local:EntitySelector>
</DataTemplate>
</Application.Resources>
In my Window control I set Window.Content and I expect WPF will set the ContentTemplate to an instance of EntitySelector based on the DataTemplate registration shown above:
[Export(typeof(EntitySelectorDialog))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public partial class EntitySelectorDialog : Window
{
[ImportingConstructor]
public EntitySelectorDialog(EntitySelectorViewModel vm)
{
InitializeComponent();
// DataContext = vm; // does not work
// EDIT: Per two answers shown below the following should work but it does not.
Content = vm;
}
}
The problem is that WPF does not resolve the ContentTemplate i.e. an instance of EntitySelector is not created. Furthermore, when I look at the XAML for EntitySelectorDialog I see that an instance of the shell has been injected
into the Window control (EntitySelectorDialog).
I don't know enough about Prism to know if I want to go with the flow and use the shell somehow or if I want to prevent Prism from injecting it at all. I don't think I have any need for it in this specific control so if it makes sense to just prevent Prism from injecting it I would prefer that route.
For the record I have tried removing the Prism attributes from my Window control and I new up the components manually. That appears to have no effect - Prism still manages to somehow inject the shell and my ContentTemplate is not resolved.
There is no XAML to show for the Window control (EntitySelectorDialog) except the Window declaration itself - I want the content to come entirely from the ContentTemplate (EntitySelector).
I've looked at this which may provide an answer but I don't know how to implement it without breaking the rest of the app:
Getting Unity to Resolve views in XAML
Set the Content of the window to a ContentControl and set or bind the Content property of this one to the view model:
[Export(typeof(EntitySelectorDialog))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public partial class EntitySelectorDialog : Window
{
[ImportingConstructor]
public EntitySelectorDialog(EntitySelectorViewModel vm)
{
InitializeComponent();
DataContext = vm;
Content = new ContentControl() { Content = vm };
}
}
You need to set vm to EntitySelectorDialog.Content to trigger WPF solve the DataTemplate according to the type. So you either add
Content = vm;
in constructor or add
Content = {Bing}
in Xaml.
I am working on refactoring an application to use property injection. This has required me to create the views from the code behind like such. Originally everything was created in XAML and bindings were set to the view models by static resources. This gave no control over injecting the service for getting data into the view model.
This is the App.xaml.cs:
public App()
{
this.MainWindow = new MainWindow(new MainWindowViewModel(new DbDataService()));
MainWindow.Show();
}
MainWindowViewModel is set as the datacontext.
App.xaml contains this resource for viewing the product type class in a listbox:
<DataTemplate x:Key="DataTemplate" DataType="{x:Type classes:Product}">
The MainWindow has a tab for each view. I have a tab's view created like this in MainWindow's constructor:
ProductsTab.Content = new MainView(mainWindowViewModel);
When I do this, I get the following error in the MainView on this line:
<dxe:ListBoxEdit Grid.Column="0" ItemTemplate="{StaticResource DataTemplate}" ...etc... />
"'Provide value on 'System.Windows.StaticResourceExtension' threw an exception.' Line number '37' and line position '31'."
What I have tried:
I have tried creating the Tab's view in XAML and setting its data context to the parents, however, I still got this exception.
I have tried changing my static resources to dynamic resources, which causes no error at runtime, but the list box does not display the information correctly.
What I need to know:
How to use static/dynamic resources with decency injection.
If there is a better approach to this problem, such as creating the templates in code and injecting them into the views?
Some of my concerns:
I want to add dependency injection to this application, however, I feel like the approach I'm using not only breaks the resources but breaks the MVVM pattern.
Try to create and call your MainWindow in the OnStartup method of your App class:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
this.MainWindow = new MainWindow(new MainWindowViewModel(new DbDataService()));
MainWindow.Show();
}
}
This should work provided that you define the "DataTemplate" resource in your App.xaml (or in a ResourceDictionary that you merge from App.xaml).
I was wondering if it is possible to make a reference to an instance made in code behind of a class, with XAML.
For example:
I have two clasess, MainWindow and MainWindow_ViewModel.
MainWindow is defined like so:
public partial class MainWindow : Window
{
static public MainWindow wn;
private MainWindow_ViewModel _mwvm;
public MainWindow_ViewModel mwvm
{
get
{
return _mwnm;
}
}
public MainWindow()
{
InitializeComponent();
wn = this;
_mwvm = new MainWindow_ViewModel();
}
}
1) How could I, in MainWindow.xaml, make a reference to the property MainWindow.wn.mwvm without creating a new instance of MainWindow_ViewModel (the purpose to this is to do some binding without using DataContext but I need to use the same instance of MainWindow_ViewModel throughout the whole application)
2) Is it possible to make a reference to that same property (MainWindow.wn.mwvm), from a XAML other than MainWindow.xaml ?
Thanks again for all the support.
First of you should set the DataContext of your window to your ViewModel. Only that way will it's properties be visible in the MainWindow's XAML.
Example:
<Window DataContext="{Binding mwvm}">
<TextBlock Text="{Binding PathToYourPropertyInVM} />
</Window>
Allways make sure your viewmodels either implement INotifyPropertyChanged or use DependencyProperties for Bindable properties. That is required in order for the UI to "listen" to the changes in the properties values.
If you want a Globally accessible ViewModel, you should look into the ServiceLocator pattern. All of the MVVM framework implement it for you (MVVMLight, Caliburn, etc). It basically consist of a class where your register your components and it handles all the instatiation when it's required. You should do a little research about that topic because it's rather extensive and hard to put on a single answer.
Hope this helps ;)
To instantiate your class in the app.xaml :
Add the namespace in the app.xaml
xmlns:myApp="clr-namespace:WpfApplication1"
Create the object of your class
<myApp:MainWindow_ViewModel x:Key="mwvm" />
An example of use :
<TextBlock Text="{Binding Source={StaticResource mvvm}, Path=A_PROPERTY}"/>
Im trying to get to grips with writing testable ViewModels in Silverlight 4. Im currently using MVVM light.
Im using AutoFac and the IoCContainer is doing its job fine. However to inject into the constructor of ViewModels, which are bound to Views I have this constructor chaining:
public UserViewModel() : this(IoCContainer.Resolve<IUserServiceAsync>())
{
}
public UserViewModel(IUserServiceAsync userService)
{
if (this.IsInDesignMode) return;
_userService = userService;
}
Which doesn't feel clean, but is the best option i have found so far. This works and my app works as desired, and is testable with control inverted.
However, with my VM bound to my view like this:
<UserControl.DataContext>
<ViewModel:UserViewModel />
</UserControl.DataContext>
In my XAML page attributes, design mode in both VS2010 and Blend doesnt work.
Is there are nicer way to achieve what im trying in Silverlight that still works with design mode? Losing design mode isnt a deal breaker but will be handy while learning XAML. A cleaner none chained way would be nice though!
Im thinking it maybe possible to use AutoFac / IoC to resolve viewmodels to views, as apposed to the XAML mark-up approach above, and go down this route?
Thanks.
Instead of implementing the first constructor, I suggest you implement a ViewModelLocator, like this:
public class ViewModelLocator
{
IoCContainer Container { get; set; }
public IUserViewModel UserViewModel
{
get
{
return IoCContainer.Resolve<IUserViewModel>();
}
}
}
Then in XAML you declare the locator as a static resource:
<local:ViewModelLocator x:Key="ViewModelLocator"/>
While you initialize your application, it is necessary to provide the locator with the instance of the container:
var viewModelLocator = Application.Current.Resources["ViewModelLocator"] as ViewModelLocator;
if(viewModelLocator == null) { // throw exception here }
viewModelLocator.Container = IoCContainer;
Then in XAML you can use the resource cleanly:
<UserControl
DataContext="{Binding Path=UserViewModel, Source={StaticResource ViewModelLocator}}"
/>
<!-- The other user control properties -->
For design time, you can implement a MockViewModelLocator:
public class MockViewModelLocator
{
public IUserViewModel UserViewModel
{
get
{
return new MockUserViewModel();
}
}
}
Declare it in XAML appropriately:
<local:MockViewModelLocator x:Key="MockViewModelLocator"/>
And finally use it in your user control:
<UserControl
d:DataContext="{Binding Path=UserViewModel, Source={StaticResource MockViewModelLocator}}"
DataContext="{Binding Path=UserViewModel, Source={StaticResource ViewModelLocator}}"
/>
<!-- The other user control properties -->
You can make your mock view model locator return safe and easily readable data for Blend to use and during runtime you will be using your real service.
This way you do not lose design time data and you do not have to sacrifice the cleanliness of the dependency injection methodology in your view models.
I hope this helps.
My WPF project will be organised like this :
Screens
Group1
Screen1
View.xaml
ViewModel.cs
Group2
Screen2
View.xaml
ViewModel.cs
To show the Screen1 from the Screen2 I'll use something like this: ScreenManager.Show("Group1.Screen1") This looks (using reflection) in the Screens.Group1.Screen1 namespace for a View and a ViewModel and instantiates them.
How can I eliminate the magic string without coupling Screen1 and Screen2 (I don't want the classes in Screen2 to use the Screen1 namespace). Also I would like some kind of screen discovery (autocompletion/intellisense)
Or maybe some way (automate test) to verify that all calls to ScreenManager.Show are valid.
Update :
I came up with this:
public class ScreenNames
{
public Group1Screens Group1;
public class Group1Screens
{
public ScreenName Screen1;
}
}
public sealed class ScreenName
{
private ScreenName() { }
}
public class ScreenManager : IScreenManager
{
public void Show(Expression<Func<ScreenNames, ScreenName>> x) {}
}
Usage:
screenManager.Show(x=>x.Group1.Screen1);
Not ideal but I suppose violating DRY is still better than magic strings. And I can automatically test (with reflection) that all calls are valid.
You don't need all that ScreenManager stuff in WPF, because the DataTemplate engine can take care of this for you with pure markup.
You can simply databind a particular area of your application with a ContentPresenter and a bunch of DataTemplates. Bind the area to a property of a 'root' ViewModel, and let the 'root' ViewModel implement INotifyPropertyChanged so that WPF knows if you change the ViewModel in that area.
public class RootViewModel : INotifyPropertyChanged
{
public object Screen1ViewModel { get; }
public object Screen2ViewModel { get; }
}
Databind one ContentPresenter control to the Screen1ViewModel property using
<ContentControl Content="{Binding Path=Screen1ViewModel}" />
and similarly for the next one. When you need to change the content of Screen1, you simply re-assign Screen1ViewModel from code, and because of the raised PropertyChanged event, WPF will pick it up and bind the new ViewModel to a new View.
The DataTemplates may be as simple as this:
<Window.Resources>
<DataTemplate DataType="{x:Type foo:MyViewModel}">
<self:MyControl />
</DataTemplate>
<DataTemplate DataType="{x:Type foo:MyOtherViewModel}">
<self:MyOtherControl />
</DataTemplate>
</Window.Resources>
In case you are not familiar with it, this article on MVVM in WPF is an excellent introduction.
Finally I used T4 code generation to generate my ScreenNames class. I did that by adapting this code : Auto generate strong typed navigation class for all user controls in ASP.NET web application