Переход со страницы на страницу в WPF

216
24 марта 2018, 13:40

Этот вопрос является логическим продолжеием этого вопроса. Я попытался реализовать схему, изложенную здесь , но программа не работает.
Я опишу свои шаги по реализации схемы по второй ссылке, и буду цитировать шаги оттуда со своим кодом. Цитаты выделены курсивом.

Положите в App.xaml шаблоны, привязывающие VM к UserControl'ам:

<Application x:Class="PresentationLayer.App"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:local="clr-namespace:PresentationLayer"
         xmlns:vm="clr-namespace:PresentationLayer.WPFViewModels"
         xmlns:view="clr-namespace:PresentationLayer.Views"
         StartupUri="StartWindow.xaml">
<Application.Resources>
    <DataTemplate DataType="{x:Type vm:StartPageVM}">
        <view:StartPage/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type vm:FLPPage1VM}">
        <view:FLPPage1></view:FLPPage1>
    </DataTemplate>
</Application.Resources>

Заведите в главной VM свойство `ActivePage`, в начале присвойте ему экземпляр `StartPageVM`...
 public  class MainVM : BaseVM
{
    public static BaseVM ActivePage { get; set; }
    public MainVM()
    {
        ActivePage = new StartPageVM();
    }
}

Здесь все работает. При запуске программы меня автоматически перебрасывает на StartPage с таким кодом:

<UserControl x:Class="PresentationLayer.Views.StartPage"
         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:PresentationLayer.Views"
         xmlns:vm="clr-namespace:PresentationLayer.WPFViewModels"
         mc:Ignorable="d" 
         d:DesignHeight="150" d:DesignWidth="500">
<UserControl.DataContext>
    <vm:StartPageVM></vm:StartPageVM>
</UserControl.DataContext>
<Grid Background="LightGray">
    <Grid.ColumnDefinitions>
        <ColumnDefinition></ColumnDefinition>
        <ColumnDefinition></ColumnDefinition>
    </Grid.ColumnDefinitions>
    <Button Grid.Column="0" FontSize="16" Foreground="DarkGreen" FontWeight="Bold" Command="{Binding FLPStartCommand}">
        <TextBlock HorizontalAlignment="Center" TextWrapping="Wrap">Регистрационные действия  ФЛП</TextBlock>
    </Button>
    <Button Grid.Column="1"  FontSize="16" Foreground="DarkGreen" FontWeight="Bold" IsEnabled="False" Command="{Binding SettingsCommand}">
        <TextBlock HorizontalAlignment="Center" TextWrapping="Wrap">Настройки </TextBlock>
    </Button>
</Grid>

Соответствующая VM

 public class StartPageVM : BaseVM
{
   public BaseVM ActivePage { get; set; }
    public ICommand FLPStartCommand { get; set; }
    public ICommand SettingsCommand { get; set; }
    public StartPageVM()
    {
        FLPStartCommand = new RelayCommand(x => FLPStartMethod());
        SettingsCommand = new RelayCommand(x => SettingsMethod());

    }
    private void FLPStartMethod()
    {
        MainVM.ActivePage = new FLPPage1VM();
        //OnPropertyChanged("PresentationLayer.WPFViewModels.MainVM.ActivePage");

    }
    private void SettingsMethod()
    {
    }
}

для переключения на первую страницу присвойте экземпляр FLPPage1VM и т. д
Я ожидаю, что при нажатии на кнопку меня перебросит на страницу FLPPage1. Однако, программа на нажатbе кнопки не реагирует. Что я сделал не так? Если раскомментировать вызов OnPropertyChanged ничего не меняется.

Код FLPPage1 и FLPPage1VM:

<UserControl x:Class="PresentationLayer.Views.FLPPage1"
         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:PresentationLayer.Views"
         xmlns:vm ="clr-namespace:PresentationLayer.WPFViewModels"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<UserControl.DataContext>
    <vm:FLPPage1VM></vm:FLPPage1VM>
</UserControl.DataContext>
<Grid Background="Aquamarine">
    <TextBlock>PAGE FLP1</TextBlock>
</Grid>

VM пустая пока, но приведу для полноты картины:

 public  class FLPPage1VM: BaseVM
{
    FLPCompleteEntity FLP { get; set; }
}

Для справки: Класс VMBase Брал с предыдущего проекта, должен быть рабочий:

public class BaseVM : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public virtual void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Класс RelayCommand (тоже с предыдущего проекта)

public class RelayCommand:ICommand
{
    public Predicate<object> CanExecuteDelegate { get; set; }
    public Action<object> ExecuteDelegate { get; set; }

    public RelayCommand(Action<object> action)
    {
        ExecuteDelegate = action;
    }
    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }
    public bool CanExecute(object parameter)
    {
        if (CanExecuteDelegate != null)
        {
            return CanExecuteDelegate(parameter);
        }
        return true;
    }
    public void Execute(object parameter)
    {
        if (ExecuteDelegate != null)
        {
            ExecuteDelegate(parameter);
        }
    }
}
Answer 1

У вас неверно реализован интерфейс INotifyPropertyChanged. Вы должны отправлять NotifyPropertyChanged, а имя свойства не должно содержать «пути» к свойству. Причём отправлять NotifyPropertyChanged должен именно тот объект, в котором меняется свойство. А также, это самое свойство не должно быть статическим.

Я бы посоветовал реализовать это так:

// https://ru.stackoverflow.com/a/632894/10105
class BaseVM : INotifyPropertyChanged
{
    protected bool Set<T>(ref T field, T value,
                         [CallerMemberName] string propertyName = null)
    {
        if (EqualityComparer<T>.Default.Equals(field, value))
            return false;
        field = value;
        NotifyPropertyChanged(propertyName);
        return true;
    }
    protected void NotifyPropertyChanged([CallerMemberName] string propertyName = null) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    public event PropertyChangedEventHandler PropertyChanged;
}
public class MainVM : BaseVM
{
    BaseVM activePage;
    public BaseVM ActivePage
    {
        get => activePage;
        set => Set(ref activePage, value);
    }
    public MainVM()
    {
        ActivePage = new StartPageVM();
    }
}

При этом нужное имя отправляется в событие PropertyChanged автоматически (посредством «волшебного» атрибута CallerMemberName).

Следующая ошибка — задание DataContext внутри контрола. Это неправильно. Во-первых, вы уже и так задаёте DataContext снаружи, а значит, внешний DataContext будет проигнорирован, хотя как раз он у вас правильно связан с другими VM. А во-вторых, с точки зрения MVVM у View нет прав определять свой DataContext.

Просто уберите

<UserControl.DataContext>
    ...
</UserControl.DataContext>

из кода.

READ ALSO
Разница в применении атрибутов [InverseProperty] и [ForeignKey]

Разница в применении атрибутов [InverseProperty] и [ForeignKey]

Обьясните, пожалуйста, максимально простыми словами, в чем же разница между [InverseProperty] и [ForeignKey], ведь оба атрибута устанавливают однозначную...

156
Как считать данные из адресной строки браузера на с#?

Как считать данные из адресной строки браузера на с#?

Всем приветСтоит задача получить название вводимого адреса сайта в адресную строку браузера

187
EPPlus портит даты

EPPlus портит даты

В общем, читаю Excel, где есть колонки с датой

179
Передача контекста базы данных

Передача контекста базы данных

Начал изучать юнит-тестыВозникла проблема

196