MVVM EventToCommand

1k Views Asked by At

Hi Friends, I am developing MVVM WPF application, I need to execute the event for TelerikRadTab Control SelectionChanged event, I am aware using MVVM light it is simple using EventToCommand behavior, but as I am using MVVM framework(Link) I have to do using Interaction triggers suggested @ Link.

For the below I added the interactivity dll reference from

C:\Program Files (x86)\Microsoft SDKs\Expression\Blend\Silverlight\v4.0

and in XAML I included

xmlns:I="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity



<i:Interaction.Triggers>
    <i:EventTrigger EventName="SelectionChanged">
     <Command:ChangePropertyOnTarget
          Target="{Binding}" PropertyName="SelectedItems"
          Value="{Binding SelectedItems, ElementName=ItemsToChoose}" />
    </i:EventTrigger>
  </i:Interaction.Triggers>

When I build the app I get the below error.

The property 'EventName' does not exist in XML namespace 'Link'.

Any suggestion or help on this would be of great help.

1

There are 1 best solutions below

2
On

I'm using mvvm light but haven't needed to use the event to command feature often. I've managed the same functionality you are after a slightly different way:

<telerik:RadTabControl Template="{StaticResource TabControlTemplate}" 
                ItemsSource="{Binding TabbedViewModels }" SelectedItem="{Binding ActiveTabbedViewModel, Mode=TwoWay}"
                ItemTemplate="{StaticResource TabItemTemplate}" Style="{StaticResource RadTabControlStyleBorderless}" />

so on the main windows viewmodel I have a collection of viewmodels, one per tabbed item which I bind to as the item source of the control. Then I bind the selecteditem to a property on the main view model. Then in the setter of that property I am able to do anything specific that I would have required to do in the SelectionChangedEvent. In my case the viewmodels for the tabs are dynamically loaded based upon what the user has initiated and each one is potentially quite different so they all inherit from a common base class, although you could also use an interface. the collection is thus an ObservableCollection of that bas class and the ActiveTabbedViewModel property that selectedItem binds to is also of that type.

May or may not be suitable to your scenario, but by using this approach I've not needed any Interaction Triggers

EDIT: going into a bit more detail - afraid I don't know of a blog to point you to, so will explain a bit more about how I've approached this

So MainPage.xaml with the TabControl and a content frame. MainPageViewModel is the view model for the mainpage. It has a property called TabbedViewModels which is an ObservableCollection of a TabbedWindowViewModelBase (a class I've created for this purpose)

        /// <summary>
    /// Gets or sets a collection of TabbedViewModels - each one will be represented by a tab on the top bar of the main window
    /// </summary>
    public ObservableItemCollection<CastleTabbedWindowViewModelBase> TabbedViewModels
    {
        get
        {
            return this.tabbedViewModels;
        }

        set
        {
            this.tabbedViewModels = value;
            this.RaisePropertyChanged(() => this.TabbedViewModels);
        }
    }

        /// <summary>
    /// Gets or sets the Active Tab - is bound to the Tab bars SelectedItem - when changing to
    /// another tab / view model it sets the page menu item to the correct one for that view model
    /// </summary>
    public CastleTabbedWindowViewModelBase ActiveTabbedViewModel
    {
        get
        {
            return this.activeTabbedViewModel;
        }

        set
        {
            this.activeTabbedViewModel = value;
            this.RaisePropertyChanged(() => this.ActiveTabbedViewModel);

            if (value != null)
            {
                // when the active tab has changed want to ensure we open the previously opened page that the new tab was on
                value.DoSomething();
            }
        }
    }

At this point it kind of depends on how complex your scenario is. If you have the same view but are just changing data then your view can bind to ActiveTabbedViewModel, because that raises the propertychanged event it will automatically refresh the bindings and show the data relating to the new tab. You could use the DoSomething on the child viewmodel to load it's data initially - you'd only need to do this once, unless you wantd to refresh it.

From what you've described, one way around this would be to create the TabbedWindowViewModel Class below:`

public class TabbedWindowViewModel :ViewModelBase
{
    private RelayCommand<NavigationEventArgs> navigationCommand;

    /// <summary>
    /// Gets or sets the position /order that this tab item is relative to the other tab items
    /// </summary>
    public int MenuOrder { get; set; }

    /// <summary>
    /// Gets or sets the Navigation Id
    /// </summary>
    public int NavigationId { get; set; }

    /// <summary>
    /// Gets or sets the Title that will be displayed for this tab item
    /// </summary>
    public string Title { get; set; }

    /// <summary>
    /// Gets or sets the navigation target that will be navigated to when the tab item is clicked
    /// </summary>
    public string NavigationTarget { get; set; }

    /// <summary>
    /// Gets or sets a value indicating whether the tab item is enabled
    /// </summary>
    public bool IsEnabled { get; set; }

    /// <summary>
    /// Gets the command for regular navigation.
    /// </summary>
    public virtual ICommand NavigationCommand
    {
        get
        {
            return this.navigationCommand
                   ??
                   (this.navigationCommand =
                    new RelayCommand<NavigationEventArgs>(
                        this.ExecuteNavigationCommand, x => this.IsEnabled));
        }
    }

   public void DoSomething()
   {
       //do whatever you need to
       //then navigate to the correct page
       this.NavigationCommand.Execute(null);

   }

    private void ExecuteNavigationCommand(NavigationEventArgs e)
    {
        NavigationManager.NavigateToView(this.NavigationTarget);
    }


}

Note that NavigationManager is a static helper class to aid navigation. Then each of the views that you potentially navigate to would inherit the datacontext from the MainPage - so then you have two choices - you could have a DataViewModel property on the TabbedWindoViewModel one and bind everything to that via ActiveTabbedWindow.DataViewModel. Or, as we do, have a property for each child view model on the Main View Model and bind directly to that property:

        public SummaryViewModel SummaryViewModel
    {
        get
        {
                  return this.summaryViewModel
                  ?? (this.summaryViewModel = (SummaryViewModel)ViewModelFactory.GetPageViewModel<SummaryViewModel>());
        }
    }

Hope this helps...