I stumbled around an issue in which I did not find a solution in the official documentation of CM.
Our Application looks similar to this image:
While searching StackOverflow, I found solutions in doing something like this with the Conductor<object>.Collection.AllActive class (more active screens / views in shell caliburn micro or another MVVM framework). The biggest problem I ran into is, that I can't find a way to make the dependency injection, using a factory. The constructor of the ShellViewModel currently looks like this.
public ShellViewModel(
Func<FooterViewModel> footerViewModelFactory,
Func<LoginViewModel> loginViewModelFactory)
{
this.ActivateItemAsync(loginViewModelFactory);
this.ActivateItemAsync(footerViewModelFactory);
}
Obviously this does not work, because the ContentControl can not display the factory and needs a Screen. But how do I manage to bind the objects to the shell in the first place, while still maintaining the features of dependency injection? Otherwise an easy workaround would just be to create a new instance for the ViewModels and pass down all of the parameters, which is just super dirty in my eyes.
I finally figured it out. While I hate the factory pattern in Java, it seems necessary in Caliburn Micro and makes sense.
Anyways, I created a factory similar to this:
public class FooterFactory
{
// Add all the needed interfaces you get injected from the IoC container here.
private readonly IMyInterface myImplementation;
public FooterFactory(IMyInterface myImplementation, ...)
{
this.myImplementation = myImplementation;
}
//Creates the ViewModel. You can also pass some parameters here.
public FooterViewModel Create(...)
{
return new FooterViewModel (..., this.myImplementation);
}
}
After the factory is then added to the bootstrapper it can then easily be added like followed. I had some issues with my initial approach though. Therefore, I changed my code a little bit.
public ShellViewModel(
FooterFactory footerFactory,
Func<LoginViewModel> loginViewModelFactory)
{
this.ActivateItemAsync(loginViewModelFactory);
this.FooterViewModel = footerFactory.Create(...);
}
And my XAML looks something like this.
<Window x:Class="My.Client.ShellView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
WindowState="Maximized"
WindowStartupLocation="CenterScreen"
Title="Dummy" Icon="/Resources/favicon.ico">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="120" />
</Grid.RowDefinitions>
<ContentControl Name="ActiveItem" Grid.Row="0"/>
<ContentControl Name="FooterViewModel" Grid.Row="1" />
</Grid>
</Window>
Note: As I said, this is a simplified version of the code I use. Anyway, it should highlight the approach on how to solve this issue. The good thing about this approach is, that you do not need the Conductor<>.Collection with multiple item controls. Therefore, you do not need to mess around with the order of the objects, if for example one object takes longer to load than the other, or you have to deactivate something. + you can create multiple ContentControls in the XAML if you want more stuff on your screen (e.g. Header, Menubar, etc.).
Related
Suppose I have two Pages in WPF namely Page1.xaml and Page2.xaml. I have one viewmodel named PageViewModel.cs. Both of those pages share the same viewmodel.
I can write my code by two methods:
Mehod1:
PageViewModel.cs
public static class PageViewModel
{
}
Page1.xaml
<Window.........>
<Window.DataContext>
<vm:PageViewModel />
</Window.DataContext>
</Window>
Page2.xaml
<Window.........>
<Window.DataContext>
<vm:PageViewModel />
</Window.DataContext>
</Window>
App.xaml
Default xaml code.
Method2:
PageViewModel.cs
public class PageViewModel
{
}
Page1.xaml
<Window DataContext={StaticResource PageViewModel}>
..........
..........
</Window>
Page2.xaml
<Window DataContext={StaticResource PageViewModel}>
..........
..........
</Window>
App.xaml
<vm:PageViewModel x:Key="PageViewModel" />
Can anybody explain the difference between above mentioned two methods?
The primary difference will be that in your first example, any object or method can access your view model, its data, and its methods.
In the second, you have an actual instance (albeit contained in a globally accessible object), so while other objects could still get to it, its not as easy as "access the static (read, global) instance".
Both have the same effect, you get data shared between the two views.
One additional option you may want to consider would be to pass the view model in on the constructor of the view. You have to use the code-behind, but you can give both view's a reference to the same view model object without having any global variables.
If these are subviews, then you could do the following:
MainView.xaml.cs
public void MainView()
{
SubViewModel subVm = new SubViewModel();
//If you are instantiating your views
MySubView view1 = new MySubView(subVm);
MySecondSubView view2 = new MySecondSubView(view2);
//Otherwise
view1.DataContext = subVm;
view2.DataContext = subVm;
}
In the spirit of the locator pattern, you could also simply bind the DataContext property of the sub views to a SubViewModel property on your main View Model.
The one thing to be aware of with this is that the View Model's lifetime will end once both sub views are destroyed. If you need a longer lifetime, then you should use the latter option and point it towards a long-lived object.
In general, I would stay away from static classes. They make unit testing, and good design in general, much more difficult to achieve. If you need a singleton, at least implement one properly as opposed to just using a static class.
This won't answer your question, BradleyDotNET has done that, but I just can't help my self here.
This is a perfect example for using a ViewModelLocator, try to install a framework such as GalaSoft MVVM Light. You may use your Locator to keep track of your viewmodels, static viewmodels are bad pie(you are going to run into alot of problems you can avoid).
I can't see where you have declared your static resource, but I assume it is in App.xaml ?
Check this post for using a viewmodel locator, don't be alarmed by the IOC stuff :). This is really handy and a great way to solve your issue.
Binding to someviewmodel, assuming the vmlocator is defined in App.xaml and that SomeViewModel is present there.
DataContext="{Binding Source={StaticResource ViewModelLocator}, Path=SomeViewModel}"
Hope it helps,
Cheers Stian
i have some UserControls that are shown fine in designer, but i can't make any changes to the design-time example content from the constructor. It seems like it is not executed at all.
XAML:
<UserControl x:Class="Example.Test"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBlock Name="testx" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
</Grid>
Code:
using System.ComponentModel;
using System.Windows.Controls;
namespace Example
{
/// <summary>
/// Interaction logic for Test.xaml
/// </summary>
public partial class Test : UserControl
{
public Test()
{
InitializeComponent();
if (DesignerProperties.GetIsInDesignMode(this))
testx.Text = " IN DESIGN!";
}
}
}
I've tried many options, but still can't get it how to display design-time data in WPF designer :( Different context binding also shows nothing...
PS: Tried clean VS2012 and VS2013 projects on Win8. NOTHING WORKS! :( I don't know what to do, haven't found anything similar on the net... Is it sufficient to just add design check in constructor and set existent control text? It should work, right?
K, the short answer is: You're on the right path.
The long one is: It's a bit more complicated than that.
Your example will "kinda" work, as in, if you'll put an else testx.Text = RUNTIME; after your if, like that:
if (DesignerProperties.GetIsInDesignMode(this))
testx.Text= " IN DESIGN!";
else
testx.Text= " Runtime";
you'll see what you want on runtime, but you're design time will stay empty.
For the Design time, you also need to set the context if I'm not mistaken.
If you're using any of the MVVM framework out there, you kinda get this functionality for "free". As in, you'll have a "in design time" property and you can set whatever data you want for the design. The catch is that you need to have an empty constructor if my memory serves me right.
You'll also use bindings, and not set the text property directly.
I remember that the default WPF and binding for design time was lacking a bit last time I tried to do something like that in "vanilla" wpf (as in, no MVVM, no bindings), but I believe that with a bit of a hack it's achievable. Again, can't remember it from the top of my head.
How is it possible to re-use and compose parts in CM managed windows? I have found posts regarding using two UserControls to bind to the same ViewModel, but not so much if I want to have multiple views and viewmodels all composed in the same window. (a viewmodel for each view composed into a "master view")
The first part of my question would be how to break up components for re-use? If I have two areas of a window where one is a datagrid and another is a details view with labels and text boxes should these be in separate usercontrols, customcontrols or windows? Each one would ideally be stand alone so they can be separated and used in other windows.
So I would end up with 2 viewmodels and 2 views if they were separated. Now lets say I would like to create 3 windows, one window with the first view, the second with the second view and a third with both views. How do I use CM to create the window for each and wire up each view to their viewmodel? From the examples I have seen I see for the most part a single view and viewmodel in a window.
I'm not going to claim to be an expert in CM by any means, but I've had reasonable success with a simple "benchmark explorer" I've been writing. That uses a single "shell view" that composes two other views, each with its own ViewModel. The shell view looks like this:
<Window x:Class="NodaTime.Benchmarks.Explorer.Views.ShellView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="NodaTime Benchmarks" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>...</Grid.ColumnDefinitions>
<ContentControl x:Name="BenchmarkPicker" Grid.Column="0"/>
<GridSplitter ... />
<ContentControl x:Name="ResultsGraph" Grid.Column="2"/>
</Grid>
</Window>
then ResultsGraphView and BenchmarkPickerView are each like this:
<UserControl x:Class="NodaTime.Benchmarks.Explorer.Views.ResultsGraphView"
... namespaces etc ...>
<Grid>
<Grid.RowDefinitions>...</Grid.RowDefinitions>
<Grid.ColumnDefinitions>...</Grid.ColumnDefinitions>
... controls ...
</Grid>
</UserControl>
The ShellViewModel exposes the other two ViewModels as properties. Those are then passed to the views automatically on construction. (The bootstrapper doesn't provide any way of getting them.)
Now this doesn't quite fit your description, because I don't think you could use the two individual views individually as windows - I suspect you would end up with 5 views in total:
SubViewOne - a UserControl with the first view parts
SubViewTwo - a UserControl with the second view parts
JustViewOne - a Window containing just SubViewOne
JustViewTwo - a Window containing just SubViewTwo
BothViews - a Window containing both SubViewOne and SubViewTwo
I don't think there's a way of getting around the fact that you don't want one Window within another, and the top level window has to be... well, a Window.
Hope this helps, and let me know if you want more details of the small project where I'm doing this - it's far from production quality, particularly in terms of DI, but it may be enough to help you get going.
I think I've previously done something similar to what you're asking. I'd been playing around with one of the TabControl with the intention of hosting several different tools for a game I enjoy playing.
The main tool is an item browser similar to the usual file explorer type programs, and similar to what Jon has described above. I'll explain some of the parts which may be of interest/relevance (I've removed some of the slightly obscure naming).
The main ExplorerView tab is essentially exactly the same the one Jon describes (which is hopefully a good sign - means I'm not crazy =D)
<UserControl x:Class="ItemsBrowser.Views.ItemsTabView"
<!-- namespaces -->
>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<ContentControl x:Name="ItemsExplorer" Grid.Column="0" Grid.Row="0" />
<GridSplitter HorizontalAlignment="Right" VerticalAlignment="Stretch"
ResizeBehavior="PreviousAndNext" Width="4" Grid.Column="1" Background="#FFAAAAAA" />
<ContentControl x:Name="PanelView" Grid.Column="2" Grid.Row="0" />
</Grid>
</UserControl>
The associated ViewModel holds two other ViewModels, used for composing the main explorer view:
public class ItemsTabViewModel : Conductor<IScreen>.Collection.AllActive
{
public ItemsViewModel ItemsExplorer { get; set; }
public ExplorerPanelViewModel PanelView { get; set; }
// Ctor etc.
}
The ItemsExplorer hosts a TreeView style control, allowing users to explore various categories of Item from the game. This is used in multiple places in the application, and is composed into a few different controls.
The ExplorerPanelView is a panel on the right hand side, that changes to display a number of ViewModels, based on what type of item the user is viewing. The user also have the option to toggle a few different Views over the ViewModel displayed in the ExplorerPanelView.
The ExplorerPanelView looks like:
<UserControl x:Class="MIS_PTBrochure.Views.ExplorerPanelView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:cal="http://www.caliburnproject.org">
<Grid>
<ContentControl cal:View.Model="{Binding Path=ActiveItem}"
cal:View.Context="{Binding Path=ActiveItem.State}"
Content="Select a folder."/>
</Grid>
</UserControl>
And the ExplorerPanelViewModel behind:
public class ExplorerPanelViewModel : Conductor<IScreen>.Collection.OneActive,
IHandle<ItemSelectedEvent> // More events.
{
public ItemViewModel ItemInfo { get; set; }
public CategoryFolderViewModel CategoryFolderInfo { get; set; }
public ExplorerPanelViewModel()
{
// My helper to access the `Caliburn.Micro` EventAggregator.
EventAggregatorFactory.EventAggregator.Subscribe(this);
// Other code
}
public void Handle(ItemSelectedEvent message)
{
// Other code to check active status
ItemInfo = message.selected;
ActivateItem(ItemInfo);
}
protected override void OnDeactivate(bool close)
{
Debug.WriteLine("Deactivated " + this.ToString() + close.ToString());
if (close) { EventAggregatorFactory.EventAggregator.Unsubscribe(this); }
base.OnDeactivate(close);
}
// Other code
}
I've tried to remove a lot of non-relevant code. Essentially I'm again hosting multiple ViewModels as properties (although you could hold a collection) and activating the relevant ViewModel when an approriate event is raised by my ItemsExplorerViewModel. I'm using the Caliburn.Micro EventAggregator to handle communication between multiple ViewModels.
In theory you could dispense with the properties, and just activate the ViewModels referenced in the events themselves.
Regarding the cal:View.Context and cal:View.Model - I'm using these all the user to toggle different available View states available (each ViewModel displayed in that panel inherits from a base ViewModel class which all have a State property).
There are a few places where I pop up different windows using some of the same Views and ViewModels. To achieve this, I make use of the Caliburn.Micro WindowManager. There isn't a great deal about it in the official documentation (you're best off searching Google and the CM discussions), it pretty does what is says on the tin.
If you have a look at the Caliburn.Micro.IWindowManager interface you'll see some handy methods that you can call from a WindowManager instance.
public interface IWindowManager
{
bool? ShowDialog(object rootModel, object context = null, IDictionary<string, object> settings = null);
void ShowPopup(object rootModel, object context = null, IDictionary<string, object> settings = null);
void ShowWindow(object rootModel, object context = null, IDictionary<string, object> settings = null);
}
So to pop up a new Window with a ViewModel of your choice, I did something along these lines:
// Some basic Window settings.
dynamic settings = new ExpandoObject();
settings.Title = "Test Window";
settings.WindowStartupLocation = WindowStartupLocation.Manual;
settings.SizeToContent = SizeToContent.Manual;
settings.Width = 450;
settings.Height = 300;
var TestViewModel new TestViewModel();
WindowManagerFactory.WindowManager.ShowWindow(this.classSearch, null, settings);
Caliburn.Micro should again, resolve your Views to the correct ViewModels.
Hopefully there's something useful in there somewhere. I sort of arrived at this solution through a few design iterations, so this may not be the optimal approach to some of these problems. If anyone has any constructive criticism, please let me know =D
I think I'm asking for a lecture on the proper application of WPF here but I'm going to take my chances since I'm at my wit's end. I think this is probably largely a result of my lethargy in fully embracing WPF templates and styles so I'm happy to listen to any such lectures.
I'm writing a sort of audio editor / event orchestrator. I've got a track editor that I'm fairly happy with. However, I built it largely out of custom controls (I know, this is probably a WPF sin). In keeping with that theme, I want to make a standard header for the tracks but I want the individual track "types" to be able to define what goes in that header. I thought a control that defines a sort of "grip" on the edge and then allowed the implementer to "fill in" the substance would work well. However, I have no idea how to do this in WPF without using styles and even if I end up using styles, I would like to understand this.
This probably comes down to wanting a sort of exemplar implementation of a simple ContentControl control (e.g. a button) and not being able to find one (other than AvalonDock, which ultimately uses - correctly i'm sure - templates for this). In my head, the xaml looks something like this:
<ContentControl x:Class="TestArea.CustomContentControl2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Hello"/>
<ContentPresenter Grid.Column="1"/>
</Grid>
But of course, that doesn't work. I'm fairly sure I could pull the same thing off by playing tricks with overloads behind the scenes, but it would be nice if I could do something like this. Do I really have to put all my terrible, procedural ways behind me and use these styles you speak of? If so, can someone at least tell me what that button looks like down in the framework?
Here is a complete example of deriving from ContentControl to accomplish what you want: Creating Customized UserControls (Deriving from ContentControl) in WPF 4
Pete's ContentPresenter is doing the same thing as it does in your example.
Using styles allows you to seperate functionality of a control with representation of a control; such as the Button.
Think as a control at the start as nothing more then functionality. A simple class containing predefined events, properties, etc... Once that control takes on the job of becoming part of a visual tree it now needs a visual identity. It didn't need one previously; however now it does. Defining a default style allows that control to now have a visual representation which it did not need prior as it was not living within the visual tree.
Ignoring styles would be like ignoring CSS when making use of HTML.
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.