0
votes

I am having issues getting an ICommand, in this case a MVVMLight RelayCommand, to fire in the ViewModel from a ControlTemplate in my Silverlight application.

Everything on the UI renders correctly but when clicking the Telerik RadMenu item with an embedded button the LoadTopFiveFaults method on the ViewModel does not fire.

Should I be using a RelativeSource in the ControlTemplate? Is so what am I missing? Any better ways to do this?

Control Template

<ControlTemplate x:Key="SubMenuItem" TargetType="telerik:RadMenuItem">
    <Grid>
        <Grid x:Name="ContentGrid" Margin="{TemplateBinding Padding}">
            <Button Height="70" Style="{StaticResource MenuButtonStyle}" Margin="2" 
                    CommandParameter="{Binding ID}"
                    Command="{Binding DataContext.MenuCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=navigation:Page}}">
            </Button>
        </Grid>
    </Grid>
</ControlTemplate>  

ControlTemplate Button Style

<!--MenuButton-->
<Style x:Key="MenuButtonStyle" TargetType="Button">
    <Setter Property="Background" Value="#FF1F3B53"/>
    <Setter Property="Foreground" Value="#FF000000"/>
    <Setter Property="Padding" Value="3"/>
    <Setter Property="BorderThickness" Value="1"/>
    <Setter Property="BorderBrush">
        <Setter.Value>
            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                <GradientStop Color="#FFA3AEB9" Offset="0"/>
                <GradientStop Color="#FF8399A9" Offset="0.375"/>
                <GradientStop Color="#FF718597" Offset="0.375"/>
                <GradientStop Color="#FF617584" Offset="1"/>
            </LinearGradientBrush>
        </Setter.Value>
    </Setter>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <Grid>
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualState x:Name="Normal"/>
                            <VisualState x:Name="MouseOver">
                                <Storyboard>
                                    <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="BackgroundAnimation"/>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Pressed">
                                <Storyboard>
                                    <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="BackgroundAnimation"/>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Disabled">
                                <Storyboard>
                                    <DoubleAnimation Duration="0" To=".55" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="DisabledVisualElement"/>
                                </Storyboard>
                            </VisualState>
                        </VisualStateGroup>
                        <VisualStateGroup x:Name="FocusStates">
                            <VisualState x:Name="Focused">
                                <Storyboard>
                                    <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="FocusVisualElement"/>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Unfocused"/>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                    <Border x:Name="Background" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="3" BorderBrush="Black">
                        <Grid Margin="1">
                            <Border x:Name="BackgroundAnimation" Background="#FFF7B000" Opacity="0"/>
                            <Rectangle x:Name="BackgroundGradient" Fill="{StaticResource PageBackground}"/>
                        </Grid>
                    </Border>
                    <ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                    <Rectangle x:Name="DisabledVisualElement" Fill="#FFFFFFFF" IsHitTestVisible="false" Opacity="0" RadiusY="3" RadiusX="3"/>
                    <Rectangle x:Name="FocusVisualElement" IsHitTestVisible="false" Margin="1" Opacity="0" RadiusY="2" RadiusX="2" Stroke="#FF6DBDD1" StrokeThickness="1"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Silverlight Page

<navigation:Page x:Class="QSmart_Cabs_Viewer.Views.Report" 
           xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
           xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
           xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
           xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
           xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"              
           xmlns:local="clr-namespace:QSmart_Cabs_Viewer.Views"
           xmlns:viewmodels="clr-namespace:QSmart_Cabs_Viewer.ViewModels"
           d:DesignWidth="640" d:DesignHeight="480" mc:Ignorable="d"
           Title="QSmart Cab Systems Report">
    <navigation:Page.Resources>
        <viewmodels:ReportPageViewModel x:Key="ViewModel" />
    </navigation:Page.Resources>

    <Border x:Name="ReportBorder" BorderBrush="Black" BorderThickness="3" CornerRadius="10" Background="{StaticResource PageBackground}">
        <Border.Effect>
            <DropShadowEffect Direction="318"/>
        </Border.Effect>
        <Grid x:Name="LayoutRoot"  DataContext="{StaticResource ViewModel}" Background="{StaticResource PageBackground}">

            <Grid.RowDefinitions>
                <RowDefinition Height="40"/>
                <RowDefinition Height="33*"/>
                <RowDefinition Height="40*"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="3*"/>
                <ColumnDefinition Width="4*"/>
                <ColumnDefinition Width="1*"/>
            </Grid.ColumnDefinitions>

            <TextBlock x:Name="ConfigTitle" Text="QSmart Cabs Report" 
                        Grid.Row="0" Grid.ColumnSpan="3" 
                        FontSize="32" TextAlignment="Center"  
                        VerticalAlignment="Center" Foreground="{StaticResource ReportHeaders}" />

            <Border Grid.Row="0" Grid.Column="3" Grid.RowSpan="3"
                        BorderBrush="{StaticResource JCBOrangeBoarder}" Background="{StaticResource JCBOrange}"  
                        Margin="3" BorderThickness="5" CornerRadius="20">
                <telerik:RadMenu x:Name="Businesses" IconColumnWidth="0" HorizontalAlignment="Left"
                                    ItemsSource="{Binding MenuItems}" ItemContainerStyle="{StaticResource ItemStyle}"
                                    Margin="5,10" Background="{x:Null}" Orientation="Vertical" ClickToOpen="True">                
                </telerik:RadMenu>
            </Border>

        </Grid>
    </Border>
</navigation:Page>

View Model

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows.Input;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using QSmart_Cabs_Viewer.Helpers.IOC;
using QSmart_Cabs_Viewer.Helpers.Menus;
using QSmart_Cabs_Viewer.Models;
using QSmart_Cabs_Viewer.ServiceAgents;
using QSmart_Cabs_Viewer.Web.Models;

namespace QSmart_Cabs_Viewer.ViewModels
{
    public class ReportPageViewModel : ViewModelBase
    {
        private const string _topFiveCaption = "Top 5 Faults @ {0}";
        private const string _narrativeCaption = "Top Faults @ {0}";

        private string _topFiveTitle;
        private string _narrativeTitle;

        private IDataAgent ServiceAgent { get; set; }
        private IMenuBuilder MenuBuilder { get; set; }

        public ObservableCollection<MenuModel> MenuItems { get; set; }
        public ReadOnlyObservableCollection<CabFaultsCountByBusiness_Result> FaultCountByCustomer { get; set; }
        public ICommand MenuCommand { get; set; }

        public string TopFiveTitle 
        {
            get
            {
                return _topFiveTitle;
            }
            private set
            {
                _topFiveTitle=string.Format(_topFiveCaption,value);
                _narrativeTitle = string.Format(_narrativeCaption, value);
                RaisePropertyChanged("TopFiveTitle");
                RaisePropertyChanged("NarrativeTitle");
            }
        }

        public string NarrativeTitle
        {
            get
            {
                return _narrativeTitle;
            }
        }

        public ReportPageViewModel()
        {
            if (!IsInDesignMode)
            {
                ServiceAgent = UnityComponentContainer.ResolvePart<IDataAgent>();
                MenuBuilder = UnityComponentContainer.ResolvePart<IMenuBuilder>();
                LoadStaticData();
                TopFiveTitle = "All Customers";
                MenuCommand = new RelayCommand(() => LoadTopFiveFaults());
            }
        }

        private void LoadStaticData()
        {
            ServiceAgent.GetBusinessUnit((cb) => CreateMenuItems(cb.Entities));
            ServiceAgent.GetFaultsCountByBusiness((fcb) => 
                                                    FaultCountByCustomer = (ReadOnlyObservableCollection<CabFaultsCountByBusiness_Result>)fcb.Entities);
        }

        private void CreateMenuItems(IEnumerable<BusinessUnit> items)
        {
            MenuItems = MenuBuilder.Create(items);
        }

        private void LoadTopFiveFaults()
        {
            TopFiveTitle = "Command Fired";
        }

    }
}
1
Does the output window show any binding errors when you run it in debug mode?tsiorn
Nothing in the Output windowsPhil Murray
Maybe something to try... instead of using RelativeSource, try using ElementName (and of course you will have to add x:Name="myPage" to your page. Something like: Command="{Binding DataContext.MenuCommand, ElementName=myPage}"tsiorn
Changing the Command binding to Command="{Binding DataContext.MenuCommand, ElementName=ReportPage} gives me a binding error to the wrong VM- System.Windows.Data Error: BindingExpression path error: 'MenuCommand' property not found on 'QSmart_Cabs_Viewer.ViewModels.MainPageViewModel' 'QSmart_Cabs_Viewer.ViewModels.MainPageViewModel' (HashCode=54432515). BindingExpression: Path='DataContext.MenuCommand' DataItem='QSmart_Cabs_Viewer.Views.Report' (HashCode=51415138); target element is 'System.Windows.Controls.Button' (Name=''); target property is 'Command' (type 'System.Windows.Input.ICommand')..Phil Murray
Not sure why its pulling through the wrong view model as everything else on the XAML works with the correct ViewModelPhil Murray

1 Answers

1
votes

Your original example seems like it should work. Perhaps the Telerik RadMenu is being built outside of the visual tree ? Not sure.

Try being more explicit to remove the unknown... Name your page (which is bound to your DataContext view model) and try a different binding method:

<Button Height="70" Style="{StaticResource MenuButtonStyle}" Margin="2"  
                    CommandParameter="{Binding ID}" 
                    Command="{Binding DataContext.MenuCommand, ElementName=ReportPage}"> 
            </Button>