Привязка присоединенных свойств и моделей представлений в MVVM | WPF

Вот мое дерево проекта:

MRE_WPF
|
--ViewModels
  |
  --MainWindowButtons
    |
    --HomeButtonViewModel.cs
    --UserSettingsButtonViewModel.cs
--MainWindow.xaml
--MainWindow.xaml.cs

У меня есть три кнопки в главном окне.

<Window x:Class = "MRE_WPF.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:MRE_WPF"
        xmlns:main_window_buttons = "clr-namespace:MRE_WPF.ViewModels.MainWindowButtons"
        mc:Ignorable = "d"
        Title = "MainWindow" Height = "450" Width = "800">
    <Canvas Height = "480" Name = "MainCanvas">
        <Button x:Name = "homeButton" 
                Height = "55" 
                Width = "75" 
                Background = "#FF334166" 
                Foreground = "{x:Null}" 
                FontSize = "24" 
                BorderBrush = "{x:Null}" 
                FontFamily = "Tahoma"
                Canvas.Top = "{Binding Path=(main_window_buttons:HomeButtonViewModel.CanvasTop), Mode=TwoWay, RelativeSource = {RelativeSource Self}}"
                >
            <TextBlock Text = "Home" Foreground = "White"/>
        </Button>

        <Button x:Name = "userSetingsButton" 
                Height = "55"
                Width = "75"
                Background = "#FF334166"
                Foreground = "{x:Null}"
                FontSize = "20"
                BorderBrush = "{x:Null}"
                FontFamily = "Tahoma"
                Canvas.Top = "{Binding Path=(main_window_buttons:UserSettingsButtonViewModel.CanvasTop), Mode=TwoWay, RelativeSource = {RelativeSource Self}}"
                Canvas.Left = "{Binding Path=(main_window_buttons:UserSettingsButtonViewModel.CanvasLeft), Mode=TwoWay, RelativeSource = {RelativeSource Self}}"
                >
            <TextBlock Text = "User set" Foreground = "White"/>
        </Button>

        <Button x:Name = "changeButton" 
                Height = "55" 
                Width = "75" 
                Background = "#FF334166" 
                Foreground = "{x:Null}" 
                FontSize = "20" 
                Click = "changeButton_Click" 
                BorderBrush = "{x:Null}" 
                FontFamily = "Tahoma" 
                Canvas.Top = "100"
                Canvas.Left = "500">
            <TextBlock Text = "Change" Foreground = "White"></TextBlock>
        </Button>
    </Canvas>
</Window>

HomeButtonViewModel класс:

public class HomeButtonViewModel:INotifyPropertyChanged {
    public event PropertyChangedEventHandler? PropertyChanged;

    private double canvasTop;

    public double CanvasTop {
        get => canvasTop;
        set {
            canvasTop = value;
            OnPropertyChanged(nameof(CanvasTop));
        }
    }

    public void OnPropertyChanged([CallerMemberName] string name = "") =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}

UserSettingsButtonViewModel почти то же самое:

public class UserSettingsButtonViewModel:INotifyPropertyChanged {
    public event PropertyChangedEventHandler? PropertyChanged;

    private double canvasTop = 102;
    private double canvasLeft = -1;

    public double CanvasTop {
        get => canvasTop;
        set {
            canvasTop = value;
            OnPropertyChanged(nameof(CanvasTop));
        }
    }

    public double CanvasLeft {
        get => canvasLeft;
        set {
            canvasLeft = value;
            OnPropertyChanged(nameof(canvasLeft));
        }
    }

    public void OnPropertyChanged([CallerMemberName] string name = "") =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}

Я хочу изменить эти свойства при нажатии на кнопку Change:

private void changeButton_Click(object sender, RoutedEventArgs e) {
    bool mode = !(bool)Settings.Default["mode7"];
    Settings.Default["mode7"] = mode;
    Settings.Default.Save();
    var h = new HomeButtonViewModel() {
        CanvasTop = mode ? 1 : 0
    };
    var us = new UserSettingsButtonViewModel() {
        CanvasTop = mode ? 102 : double.NaN,
        CanvasLeft = mode ? -1 : double.NaN
    };
}

Но при клике ничего не происходит. Я знаю, что делаю что-то не так, но не знаю, что.

// Также я хочу использовать команду вместо Click, но давайте решим одну задачу на вопрос.

Вы пытались использовать точку останова? Он попал в changeButton_Click?

bazsisz 08.06.2023 15:58

Да, это было. Было даже в OnPropertyChanged (в обоих случаях), но кнопки не двигались.

dragondangun 08.06.2023 16:10

Вы ничего не делаете с двумя новыми экземплярами модели представления.

Andy 08.06.2023 19:27

@Энди и я что должны сделать? Хм, может быть, мне нужно создать два свойства в MainWindow.xaml.cs и привязать к ним?

dragondangun 08.06.2023 19:42

Обычно для каждого представления используется одна модель представления. Если вы хотите что-то изменить, установите для этого свойства. Привязать представление к свойствам модели представления. Inotifypropertychanged указывает представлению перечитать значение.

Andy 08.06.2023 20:37
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
5
57
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Непонятно, почему у вас две модели просмотра.

В любом случае, вы должны установить DataContext окна на экземпляр одной модели представления. В этот раз вы можете инициализировать модели дочернего представления:

public class MainWindowVieModel
{
    public HomeButtonViewModel HomeButtonViewModel { get; } 
        = new HomeButtonViewModel();

    public UserSettingsButtonViewModel UserSettingsButtonViewModel { get; } 
        = new UserSettingsButtonViewModel();
}

Затем вы можете привязаться к дочерним моделям представления через модель основного представления/DataContext в представлении, используя следующий синтаксис:

<Button x:Name = "homeButton" 
                Height = "55" 
                Width = "75" 
                Background = "#FF334166" 
                Foreground = "{x:Null}" 
                FontSize = "24" 
                BorderBrush = "{x:Null}" 
                FontFamily = "Tahoma"
                Canvas.Top = "{Binding HomeButtonViewModel.CanvasTop}"
                >
    <TextBlock Text = "Home" Foreground = "White"/>
</Button>

<Button x:Name = "userSetingsButton" 
                Height = "55"
                Width = "75"
                Background = "#FF334166"
                Foreground = "{x:Null}"
                FontSize = "20"
                BorderBrush = "{x:Null}"
                FontFamily = "Tahoma"
                Canvas.Top = "{Binding UserSettingsButtonViewModel.CanvasTop}"
                Canvas.Left = "{Binding UserSettingsButtonViewModel.CanvasLeft}"
                >
    <TextBlock Text = "User set" Foreground = "White"/>
</Button>

Наконец, вам нужно установить свойства экземпляра (ов) модели представления, к которым вы фактически привязываетесь, в коде программной части окна (где вы также должны установить DataContext окна):

public partial class MainWindow : Window
{
    private readonly MainWindowVieModel _viewModel = new MainWindowVieModel();

    public MainWindow()
    {
        InitializeComponent();
        DataContext = _viewModel;
    }

    private void changeButton_Click(object sender, RoutedEventArgs e)
    {
        bool mode = !(bool)Settings.Default["mode7"];
        Settings.Default["mode7"] = mode;
        Settings.Default.Save();

        _viewModel.HomeButtonViewModel.CanvasTop = mode ? 1 : 0;

        _viewModel.UserSettingsButtonViewModel.CanvasTop = mode ? 102 : double.NaN;
        _viewModel.UserSettingsButtonViewModel.CanvasLeft = mode ? -1 : double.NaN;
    }
}

Другие вопросы по теме