0
votes

I use MVVMLight and MahApps to create Flat UI application (WPF). I'm trying to get navigation working.

Everything seems to be ok, but there is a problem with data binding on my Page hosted by Frame on main window. I declared that i want to bind LoginViewModel in Login View, but when i try to click a button (which is connected to RelayCommand in viewmodel), nothing happens (I've had checked it with debugger).

How to bind page to viewmodel good way?

MVVM Light Navigation

Here is my Login.xaml (Page):

<Page x:Class="Project.View.Login"
      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" 
      xmlns:local="clr-namespace:Project"
      xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
      mc:Ignorable="d" 
      d:DesignHeight="480" d:DesignWidth="640"
      Title="Login"
      DataContext="{Binding LoginViewModel, Source={StaticResource Locator}}">

    <Grid>
        <Button Command="{Binding GoToVote}" Content="{Binding Path=ButtonText}" HorizontalAlignment="Left" Margin="224,220,0,0" VerticalAlignment="Top" Width="75"/>
        <TextBox x:Name="textBox" HorizontalAlignment="Left" Height="23" Margin="224,320,0,0" TextWrapping="Wrap" Text="{Binding Path=ButtonText, Mode=TwoWay}" VerticalAlignment="Top" Width="120"/>
    </Grid>
</Page>

MainWindow.xaml (Window):

<Controls:MetroWindow x:Class="Project.MainWindow"
        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:local="clr-namespace:Project"
        mc:Ignorable="d"
        xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
        DataContext="{Binding MainViewModel, Source={StaticResource Locator}}"
        Title="MainWindow" Height="480" Width="640">
    <Frame Source="View/Login.xaml" x:Name="MainFrame" NavigationUIVisibility="Hidden" />
</Controls:MetroWindow>

ViewModelLocator.cs:

using Project.Tools;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Ioc;
using Microsoft.Practices.ServiceLocation;
using System;

namespace Project.ViewModel
{
    /// <summary>
    /// This class contains static references to all the view models in the
    /// application and provides an entry point for the bindings.
    /// </summary>
    public class ViewModelLocator
    {
        /// <summary>
        /// Initializes a new instance of the ViewModelLocator class.
        /// </summary>
        public ViewModelLocator()
        {
            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

            ////if (ViewModelBase.IsInDesignModeStatic)
            ////{
            ////    // Create design time view services and models
            ////    SimpleIoc.Default.Register<IDataService, DesignDataService>();
            ////}
            ////else
            ////{
            ////    // Create run time view services and models
            ////    SimpleIoc.Default.Register<IDataService, DataService>();
            ////}

            SimpleIoc.Default.Register<MainViewModel>();
            SimpleIoc.Default.Register<LoginViewModel>();
            SimpleIoc.Default.Register<VoteViewModel>();

            var navigationService = new FrameNavigationService();
            navigationService.Configure("Login", new Uri("../View/Login.xaml", UriKind.Relative));
            navigationService.Configure("Vote", new Uri("../View/Vote.xaml", UriKind.Relative));

            SimpleIoc.Default.Register<IFrameNavigationService>(() => navigationService);
        }

        public MainViewModel MainViewModel
        {
            get
            {
                return ServiceLocator.Current.GetInstance<MainViewModel>();
            }
        }

        public LoginViewModel LoginViewModel
        {
            get
            {
                return ServiceLocator.Current.GetInstance<LoginViewModel>();
            }
        }

        public VoteViewModel VoteViewModel
        {
            get
            {
                return ServiceLocator.Current.GetInstance<VoteViewModel>();
            }
        }

        public static void Cleanup()
        {
            // TODO Clear the ViewModels
        }
    }
}

LoginViewModel.cs:

using Project.Tools;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Project.ViewModel
{
    public class LoginViewModel : ViewModelNavigationBase
    {
        private string _buttonText;

        public RelayCommand GoCommand { get; private set; }

        void GoToVote()
        {
            _navigationService.NavigateTo("Vote");
        }

        public LoginViewModel(IFrameNavigationService navigationService) : base(navigationService)
        {
            GoCommand = new RelayCommand(GoToVote);
        }

        public string ButtonText
        {
            get { return _buttonText; }
            set
            {
                _buttonText = value;
            }
        }
    }
}
3

3 Answers

0
votes

Have you tried setting the data binding on the first <Grid> instead? Setting it on the root level object might be causing the problem because that property is probably being set by the control that hosts the Page / Window.

<Page x:Class="Project.View.Login"
      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" 
      xmlns:local="clr-namespace:Project"
      xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
      mc:Ignorable="d" 
      d:DesignHeight="480" d:DesignWidth="640"
      Title="Login">

    <Grid DataContext="{Binding LoginViewModel, Source={StaticResource Locator}}">
        <Button Command="{Binding GoToVote}" Content="{Binding Path=ButtonText}" HorizontalAlignment="Left" Margin="224,220,0,0" VerticalAlignment="Top" Width="75"/>
        <TextBox x:Name="textBox" HorizontalAlignment="Left" Height="23" Margin="224,320,0,0" TextWrapping="Wrap" Text="{Binding Path=ButtonText, Mode=TwoWay}" VerticalAlignment="Top" Width="120"/>
    </Grid>
</Page>

Probably same thing on the main window.

(I'm guessing that you declare Locator in you App.xaml.)

0
votes

Try to change )

using GalaSoft.MvvmLight.Command; -> using GalaSoft.MvvmLight.CommandWpf;

0
votes

Ok, solved.

I did two terrible mistakes (i don't know how). First: In my class ViewModelNavigationBase i forget about implementing ViewModelBase (it's extension with navigation object).

Second - i binded button command to function, not to property (ICommand).