Catel in Extensibility Apllication

Topics: Questions
Jul 25, 2013 at 12:47 PM
Hello.

I was creating extensible application with using MEF and AvalonDock. Now i found Catel and i want to use it, but i faced with few problems.
I use a lot of [ImportingConstructor] in my code. I found answer how to solve it via creating custom ViewModelFactory.

But i want to ask. All my App divided to few assembies/projects:
  • Shell - main shell, only MainWindow
  • Base - main interfaces and core classes. All other assemblies use it and only it to add itself to shell or to interact with others.
  • Core - MainViewModel and other implementations of Base interfaces
  • Tools - Views, VIewModels, Models to show some tools, lise DatabaseView, TraceView etc.
  • Database - database service and other stuff
  • ManagedEngineBridge - bridge from c++ to .NET
  • Pipeline - services which use ManagedEngineBridge and Database a lot and take a lot of proccessing time.
  • .... a may be other parts not implemented yet
There are a lot of blocks which must be separated from each other and i thought that MEF is the best option for this, but i am new in MEF, Prism, Unity or Catel.

I don't want to hard link it all together, for example if Tool.SomeTool needs interaction with DatabaseService it resolving (now via MEF) type Base.IDatabaseService which implemented in Database.DatabaseSevice and if this implementation needs MenuItem in MainMenu in Shell it's exporting IMenuItem or in IPartImportsSatisfiedNotification.OnImportsSatisfied i calling IMenu.RegisterMenuItem(new MenuItem(...)).
So, in result, i have a flexible system, i think.

But i have a bit problem with assigning view to viewmodels. Now i'm exporting ResourceDictionary where i'm defining DataTemplate's for views declared in this assembly.
<ResourceDictionary 
  ...>
  <DataTemplate DataType="viewmodels:MyViewModel">
    <views:MyViewForViewModel/>
  </DataTemplate>
</ResourceDictionary>
And in constructor of MainWindow i'm importing all ResourceDictionaries and merging them with MainWidow.ResourceDictionary.

Is Catel.IoC able to import some interface implimentation from another assembly not referenced in compile time, like MEF.
And how i can acces interface implementation after it called SatisfyImportsOnce(this)?

Thank you.
Coordinator
Jul 27, 2013 at 5:38 PM
Hello,

It is best to make sure that Catel can resolve the views and view models by using naming conventions. To see how to customize these, take a look at this documentation:

https://catelproject.atlassian.net/wiki/display/CTL/Naming+conventions

Can you explain how MEF is able to check an interface while it is not really there at compile time? IMHO you will always get compilation errors. It is possible though to expose an interface and register it in a completely different assembly.
Jul 29, 2013 at 9:21 AM
Edited Jul 30, 2013 at 7:28 AM
Yes, sorry that I confused you.
I have a some problems, but at all, Catel is great framework, thank you.
I want to ask few questions.
  • I want to save some poles in views, like UserName, Layout, etc. It can be done via creating custon SerializerBase to save to some location in %ApplicationData% or registry and in ViewModelBase override method Save(). Is there any other ways?
  • I want add custom button to my DataWindow. So i created it via behaviors, like it is done in Catel.Examples.WPF.AdvancedDemo:
<catel:DataWindow
<!-- -->
>
    <i:Interaction.Behaviors>
        <catel:WindowBehavior x:Name="mvvmBehavior" ViewModelType="viewmodels:MyViewModel"
                                      Save="connectButton" CancelAndClose="cancelButton"
                                      Close="cancelButton.Click" />
    <WrapPanel Grid.ColumnSpan="2" Style="{StaticResource RightAlignedButtonsWrapPanelStyle}">
                    <Button x:Name="connectButton" Content="Connect" Style="{StaticResource RightAlignedFixedSizeButtonStyle}" />
                    <Button x:Name="cancelButton" Content="Cancel" Style="{StaticResource RightAlignedFixedSizeButtonStyle}" />
    </WrapPanel>
code behind like in example.

When i press Connect or close it is OK, but when i close window via red cross it throws an error that DialogResult can be set only when ShowDialod called. I checked it via overriding ShowDialog in my DataWindow class and it is called, not just Show. Also tried set Close="cancelButton", nothing changed. Is there any way to implement this window without behaviors?
  • ISplashScreenService. It's more .Not question than Catel, but i haven't found any info. I don't understand how to use it. I just have to create bootstaper and in Run method write:
splashScreenService.Enqueue(new ActionTask("Creating the shell", OnCreateShell));
//....
public void OnCreateShell(ITaskProgressTracker tracker)
{
    _mainWindow.Show();
}
The same for MEF and other long run task. And task in another thread which will collect messages from my code that some module initialized and g2g and increase progress. But i want to show progress of assembly resolving and preloading and other system task done automaticaly and other stages of loading, i can create custom AssemblyHelper?
  • CommandBindings. I have a main view which contain ContentControl for IMenu and IWorkspace. And i have static class where stored all standart RoutedCommands. Ofcourse i can get parent window or even Application.Current from WorkspaceViewModel and create command bindings via code, but, i think it's not the best way, cause later i will face the same problem when this solution is not applicable. The reason why i can't use RelayCommand is modularity. I Have IWorkspace whitch have to display imported via MEF Views, which create Menu items in main menu on first import with command WorkspaceCommands.ShowView and Parametr = typeof(MyView) and i don't want create ICommand ShowView(Type viewType) in interface IWorkspace. Is there any other already created ways to solve it?
  • Also i have some problems with MEF and IServiceLocator connection when set ServiceLocator.Instance.AutomaticallyKeepContainersSynchronized = true. It's somehow trying resolve type IServiceLocator from MEF Container before it was added to it. But i can't provide more info now.
Coordinator
Jul 31, 2013 at 9:32 AM
1) This is done automatically. If you want custom serialization (if both Xml and Binary do not fit your needs), you can indeed create your own serializer.
2) You don't have to use the WindowBehavior. In the ctor of your datawindow, just call AddButton.
3) We are currently working on the SplashScreenService. A demo will be available soon in the Examples repository.
4) It's best not to use shared RoutedCommands but create a command on each VM that you want to expose the command. In the command execution, just forward the command to a shared service which actually handles the command logic. For example, a ShellService could have methods like CloseDocument, etc. Then you just forward your RelayCommand (we just call this Command) to the service.
5) If it tries to resolve a type, then it is already being used by the ServiceLocator. If you go for Catel, it is best not to use MEF at all (much slower).
Jul 31, 2013 at 11:44 AM
Thank you for your answers.
But I steel have some a newby questions.

2) For what cases WindowBehavior is designed for?
4) So, i just have to manualy route my command to function? Why? I thought RoutedCommands is very powerful concept and i have to use it. Also what about cases when on Button click i have to do some actions ( i mean call few member functions etc) with View members, like, on button SaveLayout i have to create serializer for AvalonDock Layout manager, do i have to pass this LayoutManager to ViewModel via command arg. or i have to create it via EventHandler and call function in ViewModel and pass LayoutManager to it, or i have to call command in ViewModel and from it retrieve LayoutManager from view. I think the 1st variant is the best, but anyway want to ask.
5) I already thought about it, but i need modularity, so UnityContainer is the only solution, or Adn-ins also is good?

Thank you
Coordinator
Jul 31, 2013 at 11:48 AM
2) In very special cases where you cannot use a custom base class. It is best to implement it in the base, even when using a custom base window from telerik or devexpress.
4) You don't have to. It is best to do so if you have the same command (say undo/redo) on several view models. Then it is best to forward the logic to a service. But you can always implement it again and again on each vm if you like. You will still have to declare the commands on your vm, you just don't repeate the logic in each OnMyCommandExecute.
5) Modularity != IoC. You can use any IoC container (and we recommend the Catel one). For modularity, we really like prism (we even have an extension for that). Catel and Prism go hand in hand and the ServiceLocator in Catel can perfectly handle modularity.
Jul 31, 2013 at 2:00 PM
Thank you.
I still have 1 last question: Do i need Prism?
I ask it becaurse i don't want to overload my code.
My app don't have something out-of-box. It is using EntityFramework to retriew data from SQL server.
May be in future it'll use some file server to retrieve files to local storage.
And it have some simple views like Solution Explorer, Propertie View in MSVC.

And it have Documents.
They are realy complex. Some of them is 3d Editor, some of them is some kind of shader editor (like in msvc 2012). But all hard work done by c++.
C# role in this play is UI, events(MouseClick, KeyPress, DragAndDrop) etc.
And it uses a Windows Service, to build all data.

How i understand, while read about Prism it as all about mvvm and modularity. Also it have some Regions, but i have AvalonDock for it.
So i have Catel as MVVM base provider and i can use MEF or Unity for modularity, like Prism does.

Whot else prism can propose to me?
Coordinator
Jul 31, 2013 at 4:20 PM
No, you don't need prism. It will help you create modular applications though. It's up to you whether you want to use it.

Prism != SQL data stuff

Prism has a lot of functions, but these are which we like best:
  • Modularity (creating separate modules which will be automatically discovered)
  • Layout regions (you can dynamically load parts of your views (regions))
If you want to use AvalonDock as well, take a look at Orchestra. It's based on Catel, AvalonDock and Prism.
Aug 1, 2013 at 8:40 PM
Hello.

I decided to take a look at PRISM and try it, cause of dynamic load of regions.

I have a ShellView where i defined 3 Regions, MainMenuRegion, WorkspaceRegion, TaskBarRegion and a in modules i do this:
RegionManager.RegisterViewWithRegion("MainMenuRegion", typeof (Views.MainMenuView));
//..
I want this Regions automatically create ViewModels for them selves.
I can manually set DataContext to view in constructor and call
RegionManager.Regions["OrderRegion"].Add(new Views.MainMenuView(new ViewModels.MainMenuViewModel));
or via ServiceLocator or if i'll define ContentControls in ShellView and bind it ViewModel property with ViewModelToViewConverter it'll solve my problem, but i'll loose regions, i don't want it.

And also want to ask.
Do i have to use PRISM MVVM ar Catel's when i'm using PRISM and Catel.
Coordinator
Aug 1, 2013 at 9:03 PM
Though we like prism for its modularity and regions, we don't think that prism has a good MVVM framework. That's one of the reasons we built catel. So, use Catel views + view models and use the modules and regions by prism.

Take a look at the example apps:

https://github.com/Catel/Catel.Examples
Aug 8, 2013 at 10:33 PM
Thank you.
I have a one more questions, sorry for that.

I have, for example, Menu and all ViewModels have to use one instance. I create it via creating Model.
So in MainMenuViewModel i have
[Model]
public IMenu Menu
{
    get { return GetValue<IMenu>(MenuProperty); }
}

public MainMenuViewModel()
{
    SetValue(MenuProperty, ServiceLocator.ResolveType<IMenu>(); 
}
And i can use it without any problems. Is it good with catel design?

Is there any generic way to use one instance of ViewModel for all Views which require this ViewModel.
I've tried do it with custom ViewModelLocator but in this way i have to manually specify something like type of ViewModel (IMenu).
May be prism somehow can help to solve it?

The another question is about SplashScreen (i've tried Orchestra, but i cant figure out how they solve this problem).
The only solution i've find is to create MainWindow before SplashScreen and move all initialization to some method like Initialize();
I've also tried to set different Shutdown modes to me WPF App, but nothing changes. I have filling that the method
_progressNotifyableViewModel.CloseViewModel(null); in finally block of Catel.MVVM.Services.SplashScreenService.Execute method is closing SplashScreenViewModel and it's closing all child ViewModels and my MainWindowViewModel is first child, so it's also closing with splash screen.

Thank you.
Coordinator
Aug 9, 2013 at 9:41 AM
I think it is better to make it a "simple" property instead of calling SetValue yourself:
[Model]
public IMenu Menu { get; private set; }
Then in your ctor, use this:
public MainMenuViewModel(IMenu menu)
{
    Argument.IsNotNull(() => menu);

    Menu = menu;
}
When you want to re-use existing VM instances (why?), then you have to implement your own ViewModelFactory which will re-use existing view models.

Please raise a new question for the splash. I don't want too many questions in the same thread.
Aug 9, 2013 at 6:44 PM
Ok.

I don't want to reuse VM. i want reuse data, cause at start-up all modules add some menu items to MainMenu and to other, like DocumentContextMenu, menus etc. So for all MainMenus, but it's only 1 View for it type, i have to use the same Collection of MenuItems, same for other many things.
And I really don't see a point of using models, cause of i don't wan't, for now, to save anything from Menus or some other VM or Models, so, when i using Model + ViewModel solution i have to create Commands and bind Execute to some method in Model and Expose properties.

But i think it goes well with WPF MVVM arhitecture, so i'll do it like you said.

Thank you!
Coordinator
Aug 10, 2013 at 3:08 PM
A model doesn't mean that you need to persist it. It's just a way to move data around, whether you persist it or not.