ContentControl Content Property не изменяется с размещенным контентом - PullRequest
0 голосов
/ 20 ноября 2018

Я пытаюсь выучить MVVM и наткнулся на странную загадку. У меня есть главное меню с выдвижным ящиком, которое выходит и показывает меню: enter image description here

В главном окне, где находится этот ящик, у меня есть ContentControl, где я устанавливаю его содержимое с помощью Binding.

<ContentControl x:Name="MainWindowContentControl" Content="{Binding Path=WindowContent}"/>

Для этого окна привязана модель представления.

<Window.DataContext>
    <viewmodels:MainWindowViewModel/>
</Window.DataContext>

а вот ViewModel:

MainWindowViewModel.cs

public class MainWindowViewModel: ViewModelBase
{

    private object _content;

    public object WindowContent
    {
        get { return _content; }
        set
        {
            _content = value;
            RaisePropertyChanged(nameof(WindowContent));
        }
    }
    public ICommand SetWindowContent { get; set; }

    public MainWindowViewModel()
    {
        SetWindowContent = new ChangeWindowContentCommand(this);
    }


}

Пока что до этого момента все работает нормально. Так, например, если я нажимаю «Операции восстановления», я получаю это:

RecoveryOperationsView.xaml enter image description here

В " RecoveryOperationsView.xaml " (что является UserControl) я также ссылаюсь на модель вида сверху, как это ..

<UserControl.DataContext>
    <viewmodels:MainWindowViewModel/>
</UserControl.DataContext>

и иметь кнопку для вызова команды для изменения свойства Content ContentControl из главного окна ..

<Button Grid.Row="2" Content="Restore Database" Width="150" Style="{StaticResource MaterialDesignFlatButton}" Command="{Binding SetWindowContent}" CommandParameter="DatabaseRecovery" >

В моем классе для обработки команд я изменяю содержимое на основе переданного параметра, используя оператор switch, например:

ChangeWindowContentCommand.cs

public class ChangeWindowContentCommand : ICommand
{
    private MainWindowViewModel viewModel;
    public ChangeWindowContentCommand(MainWindowViewModel vm)
    {
        this.viewModel = vm;
    }

    public event EventHandler CanExecuteChanged;

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
        switch (parameter)
        {
            case "Home":
                viewModel.WindowContent = new HomeView();
                break;
            case "RecoveryOps":
                viewModel.WindowContent = new RecoveryOperationsView();
                break;
            case "DatabaseRecovery":
                viewModel.WindowContent = new DatabaseRestoreView();
                break;
        }
    }
}

Однако именно здесь я теряюсь ... Если я щелкну что-нибудь в этом новом окне, скажу «Восстановить базу данных» и осмотрю его с точкой останова, я вижу изменяемое свойство, но фактическое ContentControl Свойство содержимого не меняется на новый UserControl, который я сделал ... Я могу изменить содержимое с помощью чего угодно в ящике, но если я попытаюсь нажать кнопку в размещенном Содержании ContentControl, то ничего не изменится. Чего мне не хватает?

1 Ответ

0 голосов
/ 20 ноября 2018

Трудно быть на 100% уверенным без тестирования вашего проекта, но я вполне уверен, что по крайней мере одна из проблем заключается в том, что ваш UserControl и ваш MainWindow используют разные экземпляры MainWindowViewModel. Вам не нужно создавать экземпляр виртуальной машины для пользовательского элемента управления, поскольку она унаследует DataContext от MainWindow. В WPF он работает так, что если для любого данного UIElement не явно назначен DataContext, он унаследует его от первого элемента логического дерева, которому назначен один.

Итак, просто удалите этот код, и он должен решить хотя бы эту проблему.

<UserControl.DataContext>
    <viewmodels:MainWindowViewModel/>
</UserControl.DataContext>

И поскольку вы изучаете WPF, я чувствую себя обязанным дать еще пару советов. Даже если вы используете ViewModel, вы все равно смешиваете пользовательский интерфейс и логику, создавая очень специфическую реализацию ICommand и назначая элемент пользовательского интерфейса через вашу ViewModel. Это нарушает схему MVVM. Я знаю, что MVVM занимает немного времени, чтобы понять, но как только вы это сделаете, его очень легко использовать и поддерживать.

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

Для переключения разных видов у вас есть несколько вариантов. Вы можете использовать TabControl, или если вы хотите использовать команду, вы можете иметь один ContentControl, связанный со свойством MainWindowViewModel, которое имеет тип ViewModelBase. Давайте назовем это CurrentViewModel. Затем при запуске команды вы назначаете модель представления желаемого пользовательского элемента управления этому связанному свойству. Вам также нужно будет использовать неявные шаблоны данных . Основная идея состоит в том, что вы создаете шаблон для каждого из типов виртуальных машин пользовательского элемента управления, который будет просто содержать экземпляр Views. Когда вы назначаете виртуальную машину пользовательского элемента управления свойству CurrentViewModel, привязка найдет эти шаблоны данных и отобразит пользовательский элемент управления. Например:

<Window.Resources>
  <DataTemplate DataType = "{x:Type viewmodels:RecoveryOperationsViewModel}">
    <views:RecoveryOperationsView/>
  </DataTemplate> 
  <!-- Now add a template for each of the views-->
</Window.Resources>

<ContentControl x:Name="MainWindowContentControl" Content="{Binding CurrentViewModel}"/>

Посмотрите, как этот подход держит интерфейс и логику на расстоянии вытянутой руки?

И, наконец, рассмотрите возможность создания очень общей реализации ICommand для использования во всех ваших моделях ViewModel, а не во многих конкретных реализациях. Я думаю, что большинство программистов WPF имеют более или менее точную реализацию RelayCommand в своем арсенале.

...