I'm fairly inexperienced with WPF, Ribbon menus and C#, as well as the MVVM pattern. So please excuse any stupidity.
My problem is that I have a WPF app thai I am attempting to have a MainView which contains the ribbon component as well as a ContentControl displaying a current view. How do I trigger a method in the current view from the MainView ribbon? It does seem to have some awareness of the view as it is getting the "Name" paramter from it, but throws an error if I attempt to bind to anything else (as that thing isn't in the interface I used to create the list of views).
Here is the code that I hope is enough to give a sense of my issue.
MainWindowView.xaml
<RibbonWindow x:Class="LIA.MainWindowView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
xmlns:local="clr-namespace:LIA" d:DataContext="{d:DesignInstance Type=local:MainWindowViewModel}"
Title="LIA"
Width="Auto" Height="480">
<RibbonWindow.Resources>
<!-- views and models for the app -->
<DataTemplate DataType="{x:Type local:SampleViewModel}">
<local:SampleView />
</DataTemplate>
</RibbonWindow.Resources>
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Ribbon x:Name="Ribbon" Title="Ribbon Title" HorizontalAlignment="Center" Width="{Binding ElementName=LayoutRoot, Path=ActualWidth}">
<Ribbon.HelpPaneContent>
<RibbonButton SmallImageSource="Images/smallicon.png" />
</Ribbon.HelpPaneContent>
<Ribbon.QuickAccessToolBar>
<RibbonQuickAccessToolBar>
<RibbonButton x:Name="QATButton1"
SmallImageSource="Images/SmallIcon.png" />
<RibbonButton x:Name="QATButton2"
SmallImageSource="Images/SmallIcon.png" />
</RibbonQuickAccessToolBar>
</Ribbon.QuickAccessToolBar>
<Ribbon.ApplicationMenu>
<RibbonApplicationMenu SmallImageSource="Images\SmallIcon.png">
<RibbonApplicationMenuItem Header="Hello _Ribbon"
Width="Auto"
x:Name="MenuItem1"
ImageSource="Images\LargeIcon.png" />
</RibbonApplicationMenu>
</Ribbon.ApplicationMenu>
<RibbonTab x:Name="SampleTab"
Header="Aname">
<RibbonGroup x:Name="SampleTabGroup1"
Header="A header">
<RibbonButton x:Name="aLargeButton"
LargeImageSource="Images\LargeIcon.png"
Label="Button5"
/>
</RibbonGroup>
</RibbonTab>
<RibbonTab x:Name="tab"
Header="Sample Tab">
<RibbonGroup x:Name="Group1"
Header="">
<ItemsControl ItemsSource="{Binding PageViewModels}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<RibbonButton
x:Name="Button1"
LargeImageSource="Images\LargeIcon.png"
Label="{Binding Name}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</RibbonGroup>
</RibbonTab>
</Ribbon>
<ContentControl Content="{Binding CurrentPageViewModel}" Grid.Row="1" />
</Grid>
</RibbonWindow>
MainWIndowView.xaml.cs:
using System.Windows.Controls.Ribbon;
namespace LIA
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindowView : RibbonWindow
{
public MainWindowView()
{
InitializeComponent();
this.DataContext = new MainWindowViewModel();
}
}
}
MainWindowViewModel.cs:
using System.Collections.Generic;
using System.Linq;
using System.Windows.Input;
namespace LIA
{
class MainWindowViewModel : ObservableObject
{
#region MainWindowViewModel Fields
private ICommand _changePageCommand;
private IPageViewModel _currentPageViewModel;
private List<IPageViewModel> _pageViewModels;
#endregion MainWindowViewModel Fields
public MainWindowViewModel()
{
//Add pages
PageViewModels.Add(new SampleViewModel());
CurrentPageViewModel = PageViewModels[0];
}
#region MainWindowViewModel Properties
public List<IPageViewModel> PageViewModels
{
get
{
if (_pageViewModels == null)
_pageViewModels = new List<IPageViewModel>();
return _pageViewModels;
}
}
public IPageViewModel CurrentPageViewModel
{
get
{
return _currentPageViewModel;
}
set
{
if (_currentPageViewModel != value)
{
_currentPageViewModel = value;
OnPropertyChanged("CurrentPageViewModel");
}
}
}
#endregion MainWindowViewModel Properties
#region MainWindowViewModel Commands
public ICommand ChangePageCommand
{
get
{
if (_changePageCommand == null)
{
_changePageCommand = new RelayCommand(
p => ChangeViewModel((IPageViewModel)p),
p => p is IPageViewModel
);
}
System.Diagnostics.Debug.WriteLine("page change command");
return _changePageCommand;
}
}
#endregion MainWindowViewModel Commands
#region MaiWindowViewModel Methods
private void ChangeViewModel(IPageViewModel viewModel)
{
if (!PageViewModels.Contains(viewModel))
{
PageViewModels.Add(viewModel);
CurrentPageViewModel = PageViewModels.FirstOrDefault(vm => vm == viewModel);
}
}
#endregion MaiWindowViewModel Methods
}
}
Interface:
using System.Collections.Generic;
using System.Windows.Input;
namespace LIA
{
public interface IPageViewModel
{
string Name { get; }
}
}
SampleViewModel.cs:
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Collections.ObjectModel;
using System.Data.OleDb;
using System.Drawing;
using System.Drawing.Printing;
using System.Linq;
using System.Windows.Input;
using ZXing;
using ZXing.Datamatrix;
namespace LIA
{
class SampleViewModel : ObservableObject, IPageViewModel
{
#region SampleViewModel Fields
private ICommand _generateLabel;
#endregion SampleViewModel Fields
public SampleViewModel()
{
}
#region SampleViewModel Properties
public string Name
{
get { return "Sample"; }
}
#endregion SampleViewModel Properties
#region SampleViewModel Commands
public ICommand GenerateLabelCommand
{
get
{
if (_generateLabel == null)
{
_generateLabel = new RelayCommand(
param => GenerateLabel(),
);
}
return _generateLabel;
}
}
#endregion SampleViewModel Commands
#region SampleViewModel Methods
private void GenerateLabel()
{
//do the stuff I need to generate a label
}
#endregion SampleViewModel Methods
}
}
I haven't included the SampleView.xaml or SampleView.xaml.cs as that seems to be working ok and I can trigger the method when the button is on that view.
and heres the output: Menus with button showing correct name
Been going crazy looking for solutions on this, which probably means there's a fundamental concept here I don't get yet.
If I understand this correctly from your code: in your
RibbonGroupnamedGroup1, you want to have aButtonfor each item inPageViewModels, which will execute some code from thatIPageViewModelitem when clicked, correct?In your example above, adding
Command="{Binding GenerateLabelCommand}"toButton1should link it to the command inSampleViewModel. It shouldn't matter that the collection it's a part of is defined as aList<IPageViewModel>. If that doesn't work, more debugging details are needed.That said, the above will only work for
SampleViewModel, unless everyIPageViewModelhas a member namedGenerateLabelCommand. If you want this to work for allIPageViewModelimplementations, you should add anICommandmember to the interface and then bind explicitly to the interface implementation as explained in this question: WPF databinding to interface and not actual object - casting possible?