RibbonView Tabs with separated ViewModel

2 posts, 0 answers
  1. Markus
    Markus avatar
    42 posts
    Member since:
    Nov 2012

    Posted 18 Apr 2013 Link to this post

    Hi,
    I'm using Caliburn Micro as my MvvM framework and the Simple Injector IOC Container for developing an plugin based application. On the main application i have a RibbonView which loads its tabs on startup from .dll files.

    So I have created a class library project where I define my xaml file with the RadRibbonTab Item.

    <telerik:RadRibbonTab x:Class="TestTabLibrary.RibbonTabView"
                          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                          xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
                          xmlns:cal="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro"
                          Header="Plugin Test">
        <telerik:RadRibbonGroup Header="TestGoup 1">
            <telerik:RadRibbonButton Text="Button 1" cal:Message.Attach="[Event Click] = [Action Group1Button1]"/>
            <telerik:RadRibbonButton Text="Button 2" cal:Message.Attach="[Event Click] = [Action Group1Button2]"/>
        </telerik:RadRibbonGroup>
     
        <telerik:RadRibbonGroup Header="TestGoup 2">
            <telerik:RadRibbonButton Text="Button 1" cal:Message.Attach="[Event Click] = [Action Group2Button1]"/>
            <telerik:RadRibbonButton Text="Button 2" cal:Message.Attach="[Event Click] = [Action Group2Button2]"/>
        </telerik:RadRibbonGroup>
    </telerik:RadRibbonTab>
     
    Also I want to define the ViewModel class in this library.

    public class RibbonTabViewModel : IViewModel
        {
            public void Group1Button1()
            {
                Console.WriteLine("Group1Button1");
            }
     
            public void Group1Button2()
            {
                Console.WriteLine("Group1Button2");
            }
     
            public void Group2Button1()
            {
                Console.WriteLine("Group2Button1");
            }
     
            public void Group2Button2()
            {
                Console.WriteLine("Group2Button2");
            }
        }

    I have defined two Interface ITabItem and IViewModel to decorate the RibbonTabView and RibbonTabViewModel which are then registered in the bootstrapper calss in the main application.

    public class AppBootstrapper : Bootstrapper<MainViewModel>
       {
           public static readonly Container PublicContainer = new Container();
           readonly string _pluginDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins");
            
           protected override void Configure()
           {
               PublicContainer.Register<IWindowManager, WindowManager>(Lifestyle.Singleton);
               PublicContainer.Register<IEventAggregator, EventAggregator>(Lifestyle.Singleton);
               PublicContainer.Register<MainViewModel>();
     
               var pluginAssemblies = from file in new DirectoryInfo(_pluginDirectory).GetFiles()
                                      where file.Extension == ".dll"
                                      select Assembly.LoadFile(file.FullName);
     
               var pluginTabItemsView = from dll in pluginAssemblies
                                        from type in dll.GetExportedTypes()
                                        where typeof(ITabItem).IsAssignableFrom(type)
                                        where !type.IsAbstract
                                        where !type.IsGenericTypeDefinition
                                        select type;
     
               PublicContainer.RegisterAll<ITabItem>(pluginTabItemsView);
     
               var pluginTabItemsViewModel = from dll in pluginAssemblies
                                        from type in dll.GetExportedTypes()
                                        where typeof(IViewModel).IsAssignableFrom(type)
                                        where !type.IsAbstract
                                        where !type.IsGenericTypeDefinition
                                        select type;
     
               PublicContainer.RegisterAll<IViewModel>(pluginTabItemsViewModel);
                
               PublicContainer.Verify();
           }
     
           protected override IEnumerable<object> GetAllInstances(Type service)
           {
               return PublicContainer.GetAllInstances(service);
           }
     
           protected override object GetInstance(Type service, string key)
           {
               return PublicContainer.GetInstance(service);
           }
       }

    So far, so good. The classes are registered correctly and I can get an instance from them.

    Next in the main xaml I simply have my RadRibbonView

    <telerik:RadRibbonWindow x:Class="EtaStudio.View.MainView"
                             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                             xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
                             Title="MainView" Height="300" Width="300">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <telerik:RadRibbonView Grid.Row="0" ApplicationName="Eta Studio" ItemsSource="{Binding Items}"/>
        </Grid>
    </telerik:RadRibbonWindow>

    This View is bound to my MainVIewModel

    public class MainViewModel : Conductor<ITabItem>.Collection.OneActive
       {
           public MainViewModel()
           {
               IEnumerable<ITabItem> instances = AppBootstrapper.PublicContainer.GetAllInstances<ITabItem>();
               Items.AddRange(instances);
           }
       }

    Overall the tab is loaded correctly but when I click a button the wrong ViewModel is used, in this case the MainViewModel.
    How can i achieve it to bind to the RibbonTabViewModel?

    Best Regards
    Markus
  2. Markus
    Markus avatar
    42 posts
    Member since:
    Nov 2012

    Posted 19 Apr 2013 Link to this post

    Hi,
    i found a solution for this. The main problem here is that i not have to add the view to the items list. So I'm mixing the ViewModel first and View first approach. The MainViewModel should look like that:

    public class MainViewModel : Conductor<ITabViewModel>.Collection.OneActive
        {
            public MainViewModel()
            {
                var instances = AppBootstrapper.PublicContainer.GetAllInstances<ITabViewModel>();
                Items.AddRange(instances);
            }
        }

    Next on the MainView:

    <telerik:RadRibbonWindow x:Class="EtaStudio.Views.MainView"
                             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                             xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
                             xmlns:l="clr-namespace:EtaStudio"
                             Title="MainView" Height="300" Width="300">
        <telerik:RadRibbonWindow.Resources>
            <CollectionViewSource x:Key="Tabsource">
                <CollectionViewSource.Source>
                    <l:ViewCollection Source="{Binding Path=Items}"/>
                </CollectionViewSource.Source>
            </CollectionViewSource>
        </telerik:RadRibbonWindow.Resources>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <telerik:RadRibbonView Grid.Row="0" ApplicationName="Eta Studio" ItemsSource="{Binding Source={StaticResource Tabsource}}"/>
        </Grid>
    </telerik:RadRibbonWindow>

    Here I have to implement the ViewCollection class which binds my TabViewModels with the TabViews. I have fouhnd that on this thread: WPF RibbonGroup backed by ViewModel?

    The last step is that you have to bind the RibbonTabViewModel with the RibbonTabView in the external class library.

    public class RibbonTabViewModel : Screen, ITabViewModel
        {
            public RibbonTabViewModel()
            {
                Console.WriteLine("RibbonTabViewModel");
     
                ViewModelBinder.Bind(this, new RibbonTabView(), null);
            }
     
            public void Group1Button1()
            {
                Console.WriteLine("Group1Button1");
            }
     
            public void Group1Button2()
            {
                Console.WriteLine("Group1Button2");
            }
     
            public void Group2Button1()
            {
                Console.WriteLine("Group2Button1");
            }
     
            public void Group2Button2()
            {
                Console.WriteLine("Group2Button2");
            }
        }

    Then you have it. Starting the project will load the dlls and import the TabViewModels bound with the TabViews. I hope this is a good solution and can help some of you.

    Best Regards
    Markus



  3. UI for WPF is Visual Studio 2017 Ready
Back to Top