Caliburn Micro Part 6: Introduction to Screens and Conductors

来源:互联网 发布:重庆seo服务外包 编辑:程序博客网 时间:2024/06/01 19:32

Welcome to the next instalment of our simple Caliburn Micro tutorial series. This time we take a look at a powerful concept known as screens and conductors. If you have a look at the documentation on this topic, you’ll see there is quite a lot of content to cover. In this post I will just describe what screens and conductors actually are, and then take you step-by-step through building an application that uses screens and conductors in a very simple way.

So what are these things?

Most commonly, a screen is part of an application that goes through a life cycle. It can be activated, deactivated or closed. A good example of this which is used well in the documentation are the code editors in Visual Studio. A code editor is “activated” when a user opens a file to edit, it is “deactivated” when the user switches to a different tab, and it can be “closed” if the user closes the tab. An event is raised when the state of a screen changes so that external logic can be applied such as changing a toolbar based on the currently active screen. So far it sounds like screens are a special type of view-model, but this is not always the case. It is advised that screens are thought of more like roles, not view-models. Other usage scenarios are outside the scope of this tutorial series, so I will be sticking with using screens as view-models.

Conductors manage the life cycle state of one or more screens. They are responsible for activating, deactivating and closing the screens that it manages which will be done differently based on the scenario. Part of the closing operation includes querying the screen to see if it can close or not. If the screen is holding unsaved data for example, it would halt the closing operation so that unsaved work isn’t lost.

Screens and Conductors in Caliburn Micro

Caliburn Micro includes many interfaces related to screens and conductors. Each interface is for a very simple part of the whole system such as providing custom “can this screen close” logic or specifying whether or not a particular screen needs to be deactivated before the conductor activates a different screen. If you create your own custom screens or conductors, you can mix and match whichever interfaces you need so you only have to implement the functionality that you care about. Caliburn Micro does not expect screens or conductors to implement all the related interfaces; it will just work with whatever you give it. Fortunately, Caliburn Micro also includes some concrete classes that implement these interfaces. It is easy to extend these classes and override the bits of logic that you need to which is what I’ll be doing in this tutorial series. First I’d like to give a brief description of the three conductors that Caliburn Micro provides which are useful for different scenarios.

Conductor<T> – Manages a single screen at a time. Once it activates a new screen, any previous screen is deactivated, closed and forgotten by the conductor. This is used for very simple navigation/display scenarios.

Conductor<T>.Collection.OneActive – This one manages many screens at once and allows one screen to be active at one time, much like a tab control. When a screen is activated, the previously active screen is simply deactivated, it is not closed and remains under the management of the conductor. Screens can be explicitly closed to remove them. This type of conductor is also responsible for activating one of its screens if the active screen is closed. This has simple default logic that you can override if you need to.

Conductor<T>.Collection.AllActive – Very similar to the previous conductor, but allows multiple screens to be in the active state at once.

One common aspect of all the Caliburn Micro conductor implementations is that they extend the Screen class. This means the conductors can also be managed by other conductors which creates a very flexible model for building up applications in composable bits.

Now that you have the basic understanding of screens and conductors and what Caliburn Micro has to offer, let’s get straight into using them in a very simple demo.

Step 1: Getting started

I’ve designed this simple tutorial to continue directly from the Getting Started Tutorial. So if you haven’t done so already, follow through the steps of that tutorial which will only take a few minutes. By the end of today’s tutorial the application will display three buttons at the top of the window. Clicking a button will cause a conductor to activate and display a single screen in the area below the buttons. – Similar to the SimpleNavigation sample provided with Caliburn Micro.

Step 2: The conductor

Now lets change the AppViewModel to be a conductor. We will only be managing a single screen at a time, so we can use the very basic conductor. This is what AppViewModel will look like now:

public class AppViewModel : Conductor<object>{}

Note that deep down in the class hierarchy, Conductor extends PropertyChangedBase. This is why we can change what AppViewModel extends and still allow it to easily raise property change notifications which is an important part of an MVVM application.

Something to keep in mind: Now we have a conductor at the root of out application, and as mentioned above, Caliburn Micro conductors extend the Screen class. Screens require a conductor to activate them, so how will the root of our application be activated? The answer is that the Caliburn Micro Bootstrapper and WindowManager classes have support for displaying the root screens. So when building a Caliburn Micro application, make sure the root screens/conductors are managed by either the bootstrapper or a window manager.

Step 3: The screens

Now let’s add some screens to our application that the conductor will manage. For this tutorial, the screens will be view-models so make sure their names end with “ViewModel”. In the downloadable project you will see I’ve called them RedViewModel, GreenViewModel and BlueViewModel. These all extend the Screen class. For this tutorial we’ll be leaving these as empty classes, but I recommend seeing what methods you can override and play around with customizing their behaviors.

Next add a UserControl to each of these screens – remember to name them correctly using Caliburn Micro naming conventions (RedView, GreenView, BlueView). In each view I am simply displaying a colored TextBlock as in the following example:

<UserControl x:Class="CaliburnMicroApp_Navigation.RedView"             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">  <Grid>    <TextBlock Text="Red" FontSize="48" FontWeight="Bold" Foreground="#FF463D" VerticalAlignment="Center" HorizontalAlignment="Center" />  </Grid></UserControl>

Step 4: Interactivity

Now open AppView.xaml and add some buttons along the top of the application like this:

<DockPanel Width="300" Height="300" Background="LightBlue">  <StackPanel Orientation="Horizontal" DockPanel.Dock="Top" HorizontalAlignment="Center">    <Button x:Name="ShowRedScreen" Content="Red" Width="50" />    <Button x:Name="ShowGreenScreen" Content="Green" Width="50" Margin="12,0,0,0" />    <Button x:Name="ShowBlueScreen" Content="Blue" Width="50" Margin="12,0,0,0" />  </StackPanel></DockPanel>

In AppViewModel.cs (our conductor) the response of clicking a button will be to activate the appropriate screen. This is done by calling the ActivateItem method. Here is what AppViewModel.cs looks like now (remember that by naming the methods the same as the buttons, Caliburn Micro hooks them up for us):

public class AppViewModel : Conductor<object>{  public void ShowRedScreen()  {    ActivateItem(new RedViewModel());  }   public void ShowGreenScreen()  {    ActivateItem(new GreenViewModel());  }   public void ShowBlueScreen()  {    ActivateItem(new BlueViewModel());  }}

Step 5: displaying the active screen

Last of all, we need to display the currently active screen. This can be done by adding this mysterious-looking piece of code under the buttons:

<ContentControl x:Name="ActiveItem" />

This is another ingenious naming convention from Caliburn Micro. We have previously seen that by setting the name of a control, Caliburn Micro will automatically bind the main content/usage property of the control to the property on the view-model with the same name. In this case “ActiveItem” is a property on conductor which our AppViewModel extends. But the special thing about this scenario is that the ContentControl ends up displaying the appropriateview, NOT the view-model that the property actually returns. Once again showing that we can save a lot of time using Caliburn Micro.

Now run up the application and click the buttons to see the conductor do it’s thing:

Conductors

And that’s it for part 6 of our Caliburn Micro tutorial series. I hope this has been an easy introduction to understanding screens and conductors and using them in your applications. Although this was a simple demo, screens and conductors are a powerful MVVM way to manage life-cycle scenarios such as modal dialogs, navigation and dynamic tabs. For lots more info, check out the official documentation here. There is a lot to be read on this topic but it is well written.

Download the full Visual Studio solution for this tutorial from here.

Happy coding :)

0 0
原创粉丝点击