Я работаю над небольшим дополнением к проекту, используя шаблон MVVM.
Проект немного больше, но я сократил его до небольшого примера, чтобы упростить задачу.
using CommunityToolkit.Mvvm.ComponentModel;
namespace MauiApp6.ViewModels
{
public partial class BusyViewModel : ObservableObject
{
[ObservableProperty]
private bool isBusy = false;
}
}
using CommunityToolkit.Mvvm.Input;
namespace MauiApp6.ViewModels
{
public partial class CommandViewModel
{
private BusyViewModel busyViewModel;
public CommandViewModel()
{
// Initialize BusyViewModel
busyViewModel = new BusyViewModel();
}
[RelayCommand()]
internal void Click()
{
// Set IsBusy to true
busyViewModel.IsBusy = true;
}
}
}
<?xml version = "1.0" encoding = "utf-8" ?>
<ContentPage xmlns = "http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x = "http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:vm = "clr-namespace:MauiApp6.ViewModels"
x:Class = "MauiApp6.MainPage" >
<StackLayout>
<Button Text = "Click Me"
BindingContext = "{Binding Source = {vm:CommandViewModel}}"
Command = "{Binding ClickCommand}"/>
<Label BindingContext = "{Binding Source = {vm:BusyViewModel}}"
Text = "{Binding IsBusy}"/>
<ActivityIndicator BindingContext = "{Binding Source = {vm:BusyViewModel}}"
IsRunning = "{Binding IsBusy}"/>
</StackLayout>
</ContentPage>
Другими словами, являются ли BusyViewModel CommandViewModel и BindingContext = "{Binding Source = {vm:BusyViewModel}
одним и тем же экземпляром BusyViewModel
?
Аннотируйте «частную BusyViewModel busyViewModel;» с [ObservableProperty]. Привяжите свойство BindingViewModel с помощью {Binding BindingViewModel.IsBusy}. stackoverflow.com/questions/76094858/… Здесь, в вопросе, вы можете увидеть, как настроена структура.
@LiyunZhang-MSFT вы имеете в виду что-то вроде CominedViewModel, который содержит экземпляр BusyVM и CommandVM?
У вас есть три экземпляра виртуальной машины — Command, Busy и экземпляр Busy, встроенный в команду. Изменения в одном экземпляре Busy не влияют на другой экземпляр Busy.
Я опубликовал ответ, чтобы объяснить ваш код и решение. Кроме того, вы можете прочитать официальный документ о привязке данных, чтобы узнать.
Вы неправильно используете BindingContext
, вам нужно следующее:
Во-первых, вам нужно назначить BindingContext
вашей странице. Как только вы это сделаете, вы назначите его как тип данных, чтобы он был скомпилирован и привязан к вашему XAML, все остальное сделает само.
public MainPage()
{
InitializeComponent();
BindingContext= new CommandViewModel();
}
В вашей главной странице XAML
<?xml version = "1.0" encoding = "utf-8" ?>
<ContentPage xmlns = "http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x = "http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:vm = "clr-namespace:MauiApp6.ViewModels"
x:DataType = "vm:CommandViewModel"
x:Class = "MauiApp6.MainPage" >
<StackLayout>
<Button Text = "Click Me"
Command = "{Binding ClickCommand}"/>
<Label Text = "{Binding IsBusy}"/>
<ActivityIndicator IsRunning = "{Binding IsBusy}"/>
</StackLayout>
</ContentPage>
Кроме того, я не понимаю, в чем цель BusyViewModel
.
Он использует вложенные наблюдаемые объекты. Его внешний наблюдаемый объект — CommandViewModel — имеет вложенное поле наблюдаемого объекта BusyViewModel, и это поле имеет собственное поле IsBusy. Он очень хорошо знает, как привязываться к внешним ViewModels, как вы можете прочитать из его вопроса - привязка команды РАБОТАЕТ. Если вы собираетесь написать ответ, уважайте его организацию классов или направьте его к одному из существующих ответов для вложенных моделей представления.
@Х.А.Х. Я хочу сказать, что поле не может быть привязано, поэтому я не понимаю цели BusyViewModel: либо это должно быть свойство, на которое можно ссылаться, либо базовый класс...
@FreakyAli Насколько мне известно, [ObservableProperty] private bool isBusy = false;
равно ``` Private bool isBus = false; public bool IsBusy {get => isBusy; set => SetProperty(ref isBusy, value); } ``` Итак, isBusy — это поле, а IsBusy — свойство. Я ошибаюсь?
@Evolyzer, конечно, можно привязать. Но в каком контексте? Ваш BindingContext — CommandViewModel. Если вы видите этот ответ, Фрики ставит "x:DataType = "vm:CommandViewModel". Это очень важно. По нескольким причинам. Тогда он может просто выполнить {Binding ClickCommand}. И intellisense сообщит ему, если в этой модели представления нет ClickCommand. Если вы не знаете свой тип BindingContext до запуска программы, это к вам не относится, но вам все равно нужно использовать вложенное свойство для доступа к вашим вложенным моделям представления. дайте ссылку на комментарии Прочтите.
@Evolyzer Нет, я об этом private BusyViewModel busyViewModel;
Прежде всего, код, который вы использовали:
<Button Text = "Click Me"
BindingContext = "{Binding Source = {vm:CommandViewModel}}"
Command = "{Binding ClickCommand}"/>
<Label BindingContext = "{Binding Source = {vm:BusyViewModel}}"
Text = "{Binding IsBusy}"/>
<ActivityIndicator BindingContext = "{Binding Source = {vm:BusyViewModel}}"
IsRunning = "{Binding IsBusy}"/>
Каждый из BindingContext = "{Binding Source = {vm:xxx}
в xaml создаст новый экземпляр класса xxx. Таким образом, контекст привязки ActivityIndicator и Label относятся к типу BusyViewModel, но являются разными экземплярами.
Способ, который всегда используется для установки контекста привязки, — это объявление экземпляра модели представления и установка его в коде.
Кроме того, вам не нужно задавать контекст привязки для каждого элемента управления на странице. Вы можете просто установить BindingContext = "{Binding Source = {vm:xxx}
для ContentPage. И тогда все дочерние элементы управления на странице содержимого будут иметь класс привязкиcontext: xxx.
Вы можете прочитать официальный документ о Наследование контекста привязки.
И в вашем случае вы можете просто установить BindingContext MainPage как CommandViewModel.
public partial class CommandViewModel
{
[ObservableProperty]
private BusyViewModel busyVM;
public CommandViewModel()
{
// Initialize BusyViewModel
busyVM = new BusyViewModel();
}
[RelayCommand()]
internal void Click()
{
// Set IsBusy to true
busyVM.IsBusy = true;
}
}
И в xaml:
<ContentPage xmlns = "http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x = "http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:vm = "clr-namespace:MauiApp6.ViewModels"
BindingContext = "{Binding Source = {vm:CommandViewModel}}"
x:Class = "MauiApp6.MainPage" >
<StackLayout>
<Button Text = "Click Me"
Command = "{Binding ClickCommand}"/>
<Label Text = "{Binding BusyVM.IsBusy}"/>
<ActivityIndicator IsRunning = "{Binding BusyVM.IsBusy}"/>
</StackLayout>
</ContentPage>
Проблема в том, что у вас есть два экземпляра BusyViewModel
: модель представления, созданная вами для метки, и модель представления, встроенная в CommandViewModel
.
ClickCommand
изменяется в модели, встроенной в CommandViewModel, и не меняется в BusyViewModel метки, поэтому видимых изменений не произойдет.
решение такое, как предложил @Liyun Zang - MSFT, вам следует создать один экземпляр CommandViewModel
и установить команду кнопки для свойства ClickCommand
этого экземпляра. и установите метку BindingContext для Label
и ActivityIndicator
для этого свойства экземпляра BusyViewModel`
public MainPage()
{
InitializeComponent();
// instance will be set to all the elements inside MainPage
BindingContext= new CommandViewModel();
}
<ContentPage xmlns = "http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x = "http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:vm = "clr-namespace:MauiApp6.ViewModels"
x:Class = "MauiApp6.MainPage" >
<StackLayout>
<!--because BindingContext is set for the MainPage no need set it here again and setting it again here will lead to unexpected behaviour-->
<Button Text = "Click Me"
Command = "{Binding ClickCommand}"/>
<!--here BindingContext will be set to CommandViewModel.BusyViewModel property-->
<Label BindingContext = "{Binding BusyViewModel}"
Text = "{Binding IsBusy}"/>
<!--same thing-->
<ActivityIndicator BindingContext = "{Binding Source = {vm:BusyViewModel}}"
IsRunning = "{Binding IsBusy}"/>
</StackLayout>
</ContentPage>
Команда
busyViewModel.IsBusy = true;
изменяет значение busyViewModelCommandViewModel
. Привязали ли вы busyViewModelCommandViewModel
к метке и ActivityIndicator?