Search code examples
c#wpfxamldata-bindingrelativesource

Binding to ViewModel not working using RelativeSource


I have an application that has a Navigation Menu that uses Commands to go from page to page.

The Navigation Menu has been created in Xaml and i have it stored in Navigation.xaml see below

<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:Cirdan.Wpf.Navigation"
xmlns:infrastructure="clr-namespace:Cirdan.Wpf.Infrastructure">

<ResourceDictionary.MergedDictionaries>
    <ResourceDictionary Source="/Cirdan.Wpf;component/Resources/Styles/Stylesheet.xaml" />
</ResourceDictionary.MergedDictionaries>

<DataTemplate x:Key="Navigation" >        
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="4*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <UniformGrid Grid.Row="0" Columns="1">
            <Button DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type vm:NavigationViewModelBase}}}" Visibility="{Binding ElementName=Menu, Path=IsChecked, Converter={StaticResource VisibilityConverter}}" Command="{Binding ViewerPageCmd}" >Viewer Screen</Button>
            <Button DataContext="{Binding NavigationViewModel, Source={x:Static infrastructure:MainWindow.LocatorX}}" Visibility="{Binding ElementName=Menu, Path=IsChecked, Converter={StaticResource VisibilityConverter}}" Command="{Binding}" >Acquisition Screen</Button>
            <Button DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type vm:NavigationViewModelBase}}}" Visibility="{Binding ElementName=Menu, Path=IsChecked, Converter={StaticResource VisibilityConverter}}" Command="{Binding WorklistPageCmd}" >Worklist Screen</Button>
        </UniformGrid>
        <ToggleButton Grid.Row="1" Style="{StaticResource ToggleBtnToolStyle}" x:Name="Menu" IsChecked="true" Background="Transparent" BorderThickness="0" >
            <StackPanel Orientation="Horizontal">
                <ContentPresenter Margin="5" Height="50" Content="{StaticResource MenuIcon}"></ContentPresenter>
                <Viewbox>
                    <TextBlock Margin="5" Style="{StaticResource TxtToolStyle}">Menu</TextBlock>
                </Viewbox>
            </StackPanel>
        </ToggleButton>
    </Grid>
</DataTemplate>

The ViewModel that I am trying to Bind these Button Commands to is called NavigationViewModelBase.cs

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;

namespace Cirdan.Wpf.Navigation
{
public abstract class NavigationViewModelBase : ViewModelBase
{
    private List<DicomMetadataModel> _dicomMetadata;

    //Navigation Cmd
    public ICommand AcquisitionPageCmd { get; private set; }
    public ICommand ManualEntryWindowCmd { get; private set; }
    public ICommand SessionWindowCmd { get; private set; }
    public ICommand SettingsWindowCmd { get; private set; }
    public ICommand StudyInfoPageCommandCmd { get; private set; }
    public ICommand ViewerPageCmd { get; private set; }
    public ICommand WorklistPageCmd { get; private set; }


    protected NavigationViewModelBase()
    {
        AcquisitionPageCmd = new RelayCommand(() => Messenger.Default.Send(new GoToPageMessage(Pages.AcquisitionScreen)));
        ManualEntryWindowCmd = new RelayCommand(() => Messenger.Default.Send(new ShowDialogMessage(Pages.ManualEntry, DicomMetadata)));
        SessionWindowCmd = new RelayCommand(() => Messenger.Default.Send(new ShowDialogMessage(Pages.Session)));
        SettingsWindowCmd = new RelayCommand(() => Messenger.Default.Send(new ShowDialogMessage(Pages.Settings)));
        ViewerPageCmd = new RelayCommand(() => Messenger.Default.Send(new GoToPageMessage(Pages.Viewer)));
        WorklistPageCmd = new RelayCommand(() => Messenger.Default.Send(new GoToPageMessage(Pages.Worklist)));
    }
}
}

On each page i then use the following code to add the Navigation

<ContentControl Grid.Column="2" Grid.Row="2" ContentTemplate="{StaticResource Navigation }" />

At the moment I'm not getting any errors, and when I have set the DataContext in one of my buttons above, when I go to bind my commands i can see all the properties of that Viewmodel so that bit is working correctly, However when i run the program and click on these buttons nothing is happening.

Thanks for any help


Solution

  • Ancestor Binding will only work for element in visualtree/view or simply elemnts in your page. A viewModel is not anywhere in the page as a control. So in place of viewModel give the type of view which has NavigationViewModelBase as datacontext.write binding As below (if your view/control is NavigationView the binding will be). and in path write the property of vm:NavigationViewModelBase class :

    <Button DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type v:NavigationView}}, path= PropertyYouWantToBind}"
    

    just make sure if you have not defined DataContext for any other control in hierarchy, Button will be getting same DataContext as view, then you just have to bind the command. which you have already done correctly.

    if you have not given the DataContext to any control in your view then just give that to you contentControl or Grid. simply as

    <Grid>
      <Grid.DataContext>
          <vm:NavigationViewModelBase />
      </Grid.DataContext>
    <Grid.RowDef.......
    

    and then use simple binding for command.