Reputation: 43
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.
Upvotes: 0
Views: 434
Reputation: 6766
If I understand this correctly from your code: in your RibbonGroup
named Group1
, you want to have a Button
for each item in PageViewModels
, which will execute some code from that IPageViewModel
item when clicked, correct?
In your example above, adding Command="{Binding GenerateLabelCommand}"
to Button1
should link it to the command in SampleViewModel
. It shouldn't matter that the collection it's a part of is defined as a List<IPageViewModel>
. If that doesn't work, more debugging details are needed.
That said, the above will only work for SampleViewModel
, unless every IPageViewModel
has a member named GenerateLabelCommand
. If you want this to work for all IPageViewModel
implementations, you should add an ICommand
member 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?
Upvotes: 0
Reputation: 59
Have you tried using Dispatcher.BeginInvoke
? I don't have any rep to comment sorry.
Upvotes: -1