This is a migrated thread and some comments may be shown as answers.

RibbonView Tabs with separated ViewModel

1 Answer 142 Views
RibbonView and RibbonWindow
This is a migrated thread and some comments may be shown as answers.
Klemens
Top achievements
Rank 1
Klemens asked on 18 Apr 2013, 07:32 AM
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

1 Answer, 1 is accepted

Sort by
0
Klemens
Top achievements
Rank 1
answered on 19 Apr 2013, 05:46 AM
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



Tags
RibbonView and RibbonWindow
Asked by
Klemens
Top achievements
Rank 1
Answers by
Klemens
Top achievements
Rank 1
Share this question
or