英文:
How to access ObservableCollection from another ViewModel?
问题
I have a BaseViewModel where I define my ObservableCollection and then I populate it. In another ViewModel I have a Grid and I bind it to my collection, everything works.
public partial class BaseViewModel : ObservableObject
{
    [ObservableProperty]
    private ObservableCollection<Patient> patientInfo;
    public BaseViewModel()
    {
        PopulateObservableCollection();
    }
    public void PopulateObservableCollection()
    {
        PatientInfo = new ObservableCollection<Patient>();
        try
        {
            string query = "select * from " + App.userDetails.DbTable;
            SqlDataAdapter sqlDataAdapter = new SqlDataAdapter(query, conn);
            using (sqlDataAdapter)
            {
                System.Data.DataTable dt = new System.Data.DataTable();
                sqlDataAdapter.Fill(dt);
                foreach (System.Data.DataRow row in dt.Rows)
                    PatientInfo.Add(new Patient(Convert.ToInt32(row[0]), row[1].ToString(), row[2].ToString(), row[3].ToString(), row[4].ToString(), row[5].ToString(), row[6].ToString(), row[7].ToString(), row[8].ToString()));
            }
        }
        catch (Exception)
        { }
    }
}
Now I want to change my ObservableCollection from the other ViewModels and be able to access the memory from the BaseViewModel.
Next a ViewModel where I try to change the ObservableCollection, but I'm not accessing the objected created in the BaseViewModel:
[QueryProperty(nameof(Patient), nameof(Patient))]
public partial class EditingViewModel : BaseViewModel
{
    [ObservableProperty] private Patient patient;
    
    [RelayCommand]
    private async Task Delete()
    {
        try
        {
            string query = "DELETE from " + App.userDetails.DbTable + " WHERE id = @patientId";
            sqlConnection = new SqlConnection(conn);
            SqlCommand sqlCommand = new SqlCommand(query, sqlConnection);
            SqlDataAdapter sqlDataAdapter = new SqlDataAdapter(sqlCommand);
            using (sqlDataAdapter)
            {
                sqlConnection.Open();
                sqlCommand.Parameters.AddWithValue("@patientId", Patient.Id);
                sqlCommand.ExecuteScalar();
            }
            //Removing object from observable collection
            PatientInfo.Remove(PatientInfo.SingleOrDefault(x => x.Id == Patient.Id));
        }
        catch (Exception ex)
        {
            await Shell.Current.DisplayAlert("Aplicação", ex.ToString(), "Sair");
        }
        finally
        {
            sqlConnection.Close();
        }
    }
}
My "DashboardView" where the grid is defined:
<?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:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
             x:Class="PatientAppMultiPlatf.Views.DashboardPage"
             xmlns:viewModels="clr-namespace:PatientAppMultiPlatf.ViewModels"
             xmlns:syncfusion="clr-namespace:Syncfusion.Maui.DataGrid;assembly=Syncfusion.Maui.DataGrid"
             Title="Lista de Pacientes">
    <VerticalStackLayout Padding="10">
        <HorizontalStackLayout Padding="20" >
            <Button 
                   Text="Exportar" Command="{Binding PDFCommand}" CornerRadius="20" WidthRequest="100" HorizontalOptions="End">
            </Button>
            <Button
                Text="Filtrar" Command="{Binding FilterCommand}" WidthRequest="100" CornerRadius="20" HorizontalOptions="Center">
            </Button>
            <Frame HeightRequest="45" Padding="5" HasShadow="True" BorderColor="White">
                <Entry Text="{Binding Date}" VerticalOptions="Center" Placeholder="Data"/>
            </Frame>
            <Frame HeightRequest="45" Padding="5" HasShadow="True" BorderColor="White">
                <Entry Text="{Binding Name}" VerticalOptions="Center" Placeholder="Nome"/>
            </Frame>
            <Frame HeightRequest="45" Padding="5" HasShadow="True" BorderColor="White">
                <Entry Text="{Binding Age}" VerticalOptions="Center" Placeholder="Idade"/>
            </Frame>
            <Frame HeightRequest="45" Padding="5" HasShadow="True" BorderColor="White">
                <Entry Text="{Binding Gender}" VerticalOptions="Center" Placeholder="Genero"/>
            </Frame>
            <Frame HeightRequest="45" Padding="5" HasShadow="True" BorderColor="White">
                <Entry Text="{Binding Process}" VerticalOptions="Center" Placeholder="Processo"/>
            </Frame>
            <Frame HeightRequest="45" Padding="5" HasShadow="True" BorderColor="White">
                <Entry Text="{Binding Diagnostic}" VerticalOptions="Center" Placeholder="Diagnostico"/>
            </Frame>
            <Frame HeightRequest="45" Padding="5" HasShadow="True" BorderColor="White">
                <Entry Text="{Binding Discharge}" VerticalOptions="Center" Placeholder="Alta"/>
            </Frame>
        </HorizontalStackLayout>
        <syncfusion:SfDataGrid x:Name="dataGrid" DefaultColumnWidth="90"
            ItemsSource="{Binding PatientInfo}" SortingMode="Single" SelectedRow="{Binding SelectedPatient}" SelectionMode="Single"
                               HeightRequest="600"  VerticalOptions="CenterAndExpand" HorizontalOptions="Center" AutoGenerateColumnsMode="None">
            <syncfusion:SfDataGrid.Behaviors>
                <toolkit:EventToCommandBehavior
                    EventName="CellDoubleTapped"
                    Command="{Binding CellDoubleTappedCommand}">
                </toolkit:EventToCommandBehavior>
            </syncfusion:SfDataGrid.Behaviors>
            <syncfusion:SfDataGrid.Columns VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand">
                <syncfusion:DataGridTextColumn  HeaderText="Data" MappingName="Date"/>
                <syncfusion:DataGridTextColumn  HeaderText="Nome" MappingName="Name" ColumnWidthMode="Fill"/>
                <syncfusion:DataGridTextColumn  HeaderText="Idade" MappingName="Age"/>
                <syncfusion:DataGridTextColumn  HeaderText="Genero" MappingName="Gender"/>
                <syncfusion:DataGridTextColumn  HeaderText="Processo" MappingName="Process"/>
                <syncfusion:DataGridTextColumn  HeaderText="Diagnostico" MappingName="Diagnostic" ColumnWidthMode="Fill"/>
                <syncfusion:DataGridTextColumn  HeaderText="Alta" MappingName="Discharge" ColumnWidthMode="LastColumnFill"/>
            </syncfusion:SfDataGrid.Columns>
                        <syncfusion:SfDataGrid.DefaultStyle>
                <syncfusion:DataGridStyle HeaderRowBackground="#b2cbc8" HeaderRowTextColor="Black" />
            </syncfusion:SfDataGrid.DefaultStyle>
        </syncfusion:SfDataGrid>
    </VerticalStackLayout>
</ContentPage>
Each view has a xaml.cs where I bind the page to the view model:
namespace PatientAppMultiPlatf.Views;
public partial class DashboardPage : ContentPage
{
    public DashboardPage(DashboardViewModel viewModel)
    {
        InitializeComponent();
        BindingContext = viewModel;
    }
}
I'm using MVVM architecture. What is the best method to access the ObservableCollection that is being
英文:
I have a BaseViewModel where I define my ObservableCollection and then I populate it. In another ViewModel I have a Grid and I bind it to my collection, everything works.
public partial class BaseViewModel : ObservableObject
{
    [ObservableProperty]
    private ObservableCollection<Patient> patientInfo;
    public BaseViewModel()
    {
        PopulateObservableCollection();
    }
    public void PopulateObservableCollection()
    {
        PatientInfo = new ObservableCollection<Patient>();
        try
        {
            string query = "select * from " + App.userDetails.DbTable;
            SqlDataAdapter sqlDataAdapter = new SqlDataAdapter(query, conn);
            using (sqlDataAdapter)
            {
                System.Data.DataTable dt = new System.Data.DataTable();
                sqlDataAdapter.Fill(dt);
                foreach (System.Data.DataRow row in dt.Rows)
                    PatientInfo.Add(new Patient(Convert.ToInt32(row[0]), row[1].ToString(), row[2].ToString(), row[3].ToString(), row[4].ToString(), row[5].ToString(), row[6].ToString(), row[7].ToString(), row[8].ToString()));
            }
        }
        catch (Exception)
        { }
    }
}
Now I want to change my ObservableCollection from the other ViewModels and be able to access the memory from the BaseViewModel.
Next a ViewModel where I try to change the ObservableCollection, but I'm not accessing the objected created in the BaseViewModel:
[QueryProperty(nameof(Patient), nameof(Patient))]
public partial class EditingViewModel : BaseViewModel
{
    [ObservableProperty] private Patient patient;
    
    [RelayCommand]
    private async Task Delete()
    {
        try
        {
            string query = "DELETE from " + App.userDetails.DbTable + " WHERE id = @patientId";
            sqlConnection = new SqlConnection(conn);
            SqlCommand sqlCommand = new SqlCommand(query, sqlConnection);
            SqlDataAdapter sqlDataAdapter = new SqlDataAdapter(sqlCommand);
            using (sqlDataAdapter)
            {
                sqlConnection.Open();
                sqlCommand.Parameters.AddWithValue("@patientId", Patient.Id);
                sqlCommand.ExecuteScalar();
            }
            //Removing object from observable collection
            PatientInfo.Remove(PatientInfo.SingleOrDefault(x => x.Id == Patient.Id));
        }
        catch (Exception ex)
        {
            await Shell.Current.DisplayAlert("Aplicaçao", ex.ToString(), "Sair");
        }
        finally
        {
            sqlConnection.Close();
        }
    }
}
My "DashboardView" where the grid is defined:
<?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:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
             x:Class="PatientAppMultiPlatf.Views.DashboardPage"
             xmlns:viewModels="clr-namespace:PatientAppMultiPlatf.ViewModels"
             xmlns:syncfusion="clr-namespace:Syncfusion.Maui.DataGrid;assembly=Syncfusion.Maui.DataGrid"
             Title="Lista de Pacientes">
    <VerticalStackLayout Padding="10">
        <HorizontalStackLayout Padding="20" >
            <Button 
                   Text="Exportar" Command="{Binding PDFCommand}" CornerRadius="20" WidthRequest="100" HorizontalOptions="End">
            </Button>
            <Button
                Text="Filtrar" Command="{Binding FilterCommand}" WidthRequest="100" CornerRadius="20" HorizontalOptions="Center">
            </Button>
            <Frame HeightRequest="45" Padding="5" HasShadow="True" BorderColor="White">
                <Entry Text="{Binding Date}" VerticalOptions="Center" Placeholder="Data"/>
            </Frame>
            <Frame HeightRequest="45" Padding="5" HasShadow="True" BorderColor="White">
                <Entry Text="{Binding Name}" VerticalOptions="Center" Placeholder="Nome"/>
            </Frame>
            <Frame HeightRequest="45" Padding="5" HasShadow="True" BorderColor="White">
                <Entry Text="{Binding Age}" VerticalOptions="Center" Placeholder="Idade"/>
            </Frame>
            <Frame HeightRequest="45" Padding="5" HasShadow="True" BorderColor="White">
                <Entry Text="{Binding Gender}" VerticalOptions="Center" Placeholder="Genero"/>
            </Frame>
            <Frame HeightRequest="45" Padding="5" HasShadow="True" BorderColor="White">
                <Entry Text="{Binding Process}" VerticalOptions="Center" Placeholder="Processo"/>
            </Frame>
            <Frame HeightRequest="45" Padding="5" HasShadow="True" BorderColor="White">
                <Entry Text="{Binding Diagnostic}" VerticalOptions="Center" Placeholder="Diagnostico"/>
            </Frame>
            <Frame HeightRequest="45" Padding="5" HasShadow="True" BorderColor="White">
                <Entry Text="{Binding Discharge}" VerticalOptions="Center" Placeholder="Alta"/>
            </Frame>
        </HorizontalStackLayout>
        <syncfusion:SfDataGrid x:Name="dataGrid" DefaultColumnWidth="90"
            ItemsSource="{Binding PatientInfo}" SortingMode="Single" SelectedRow="{Binding SelectedPatient}" SelectionMode="Single"
                               HeightRequest="600"  VerticalOptions="CenterAndExpand" HorizontalOptions="Center" AutoGenerateColumnsMode="None">
            <syncfusion:SfDataGrid.Behaviors>
                <toolkit:EventToCommandBehavior
                    EventName="CellDoubleTapped"
                    Command="{Binding CellDoubleTappedCommand}">
                </toolkit:EventToCommandBehavior>
            </syncfusion:SfDataGrid.Behaviors>
            <syncfusion:SfDataGrid.Columns VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand">
                <syncfusion:DataGridTextColumn  HeaderText="Data" MappingName="Date"/>
                <syncfusion:DataGridTextColumn  HeaderText="Nome" MappingName="Name" ColumnWidthMode="Fill"/>
                <syncfusion:DataGridTextColumn  HeaderText="Idade" MappingName="Age"/>
                <syncfusion:DataGridTextColumn  HeaderText="Genero" MappingName="Gender"/>
                <syncfusion:DataGridTextColumn  HeaderText="Processo" MappingName="Process"/>
                <syncfusion:DataGridTextColumn  HeaderText="Diagnostico" MappingName="Diagnostic" ColumnWidthMode="Fill"/>
                <syncfusion:DataGridTextColumn  HeaderText="Alta" MappingName="Discharge" ColumnWidthMode="LastColumnFill"/>
            </syncfusion:SfDataGrid.Columns>
                        <syncfusion:SfDataGrid.DefaultStyle>
                <syncfusion:DataGridStyle HeaderRowBackground="#b2cbc8" HeaderRowTextColor="Black" />
            </syncfusion:SfDataGrid.DefaultStyle>
        </syncfusion:SfDataGrid>
    </VerticalStackLayout>
</ContentPage>
Each view has a xaml.cs where I bind the page to the view model:
namespace PatientAppMultiPlatf.Views;
public partial class DashboardPage : ContentPage
{
	public DashboardPage(DashboardViewModel viewModel)
	{
		InitializeComponent();
		BindingContext = viewModel;
	}
}
I'm using MVVM architecture. What is the best method to access the ObservableCollection that is being binded?
答案1
得分: 3
在BaseViewModel中,将以下部分进行更改:
private ObservableCollection<Patient> patientInfo;
更改为:
protected ObservableCollection<Patient> patientInfo;
英文:
In the BaseViewModel, change
[ObservableProperty]
private ObservableCollection<Patient> patientInfo;
to
[ObservableProperty]
protected ObservableCollection<Patient> patientInfo;
答案2
得分: 1
ObservableCollections 不是 ObservableProperties。
对于集合本身的更改足以通知任何更改。
如果我担心将其设置为其他内容,通常会这样做:
public ObservableCollection MyProperty {get;} = new();
此外,我没有看到您的 x:DataType 在任何地方。为什么没有设置它,以及为什么您的 VisualElement 有名称?
编辑:我不确定我是否足够强调“PUBLIC”这个词,所以我想再次指出。
英文:
ObservableCollections are not ObservableProperties.
The change to the collection itself is more than enough to notify any changes.
If I am concerned about setting it to something else, I usually do this:
**public** ObservableCollection MyProperty {get;} = new();
Also, I do not see anywhere your x:DataType. Why are you not setting it, and why is your VisualElement named?
Edit: I am not sure I put enough stress on the word PUBLIC, so I want to point it out again here.
答案3
得分: 0
根据您的代码,我创建了一个演示,它可以从继承自父类(BaseViewModel)属性的PatientInfo中移除项目。您可以参考以下代码:
BaseViewModel.cs
public partial class BaseViewModel : ObservableObject
{
    [ObservableProperty]
    private ObservableCollection<Patient> patientInfo;
    public BaseViewModel()
    {
        PopulateObservableCollection();
    }
    public void PopulateObservableCollection()
    {
        PatientInfo = new ObservableCollection<Patient>();
        try
        {
            PatientInfo.Add(new Patient { Id = 01, Name = "test1" });
            PatientInfo.Add(new Patient { Id = 02, Name = "test2" });
            PatientInfo.Add(new Patient { Id = 03, Name = "test3" });
        }
        catch (Exception)
        { }
    }
}
public class Patient
{
    public int Id { get; set; }
    public string Name { get; set; }
}
EditingViewModel.cs
public partial class EditingViewModel : BaseViewModel
{
    [RelayCommand]
    private async Task Delete()
    {
        try
        {
            // 从可观察集合中移除对象
            // 这里我可以从父类`BaseViewModel`的`PatientInfo`中移除项目
            PatientInfo.RemoveAt(0);
        }
        catch (Exception ex)
        {
            await Shell.Current.DisplayAlert("应用", ex.ToString(), "退出");
        }
        finally
        {
        }
    }
}
MainPage.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:MauiMvvmApp="clr-namespace:MauiMvvmApp226"
             x:Class="MauiMvvmApp226.MainPage">
    <ContentPage.BindingContext>
        <MauiMvvmApp:EditingViewModel></MauiMvvmApp:EditingViewModel>
    </ContentPage.BindingContext>
    <VerticalStackLayout
        Spacing="25"
        Padding="30,0"
        VerticalOptions="Start">
        <ListView ItemsSource="{Binding PatientInfo}">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <TextCell Text="{Binding Id}"
                              Detail="{Binding Name}"
                              TextColor="#f35e20"
                              DetailColor="#503026" />
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
        <Button
            Command="{Binding DeleteCommand}"
            x:Name="CounterBtn"
            Text="点击我"
            HorizontalOptions="Center" />
    </VerticalStackLayout>
</ContentPage>
英文:
Based on your code,I created a demo and it could remove the item from PatientInfo which inherits from the properties of the parent class( BaseViewModel)
You can refer to the following code:
BaseViewModel.cs
public partial class BaseViewModel: ObservableObject 
{
[ObservableProperty]
private ObservableCollection<Patient> patientInfo;
public BaseViewModel()
{
PopulateObservableCollection();
}
public void PopulateObservableCollection()
{
PatientInfo = new ObservableCollection<Patient>();
try
{
PatientInfo.Add(new Patient { Id = 01, Name="test1"});
PatientInfo.Add(new Patient { Id = 02, Name = "test2" });
PatientInfo.Add(new Patient { Id = 03, Name = "test3" });
}
catch (Exception)
{ }
}
}
public class Patient 
{
public int Id { get; set; }
public string Name { get; set; }
}
EditingViewModel.cs
  public partial class EditingViewModel : BaseViewModel 
{
//[ObservableProperty] private Patient patient;
[RelayCommand]
private async Task Delete()
{
try
{
//Removing object from observable collection
//here I could remove the item from PatientInfo from parent class `BaseViewModel `
PatientInfo.RemoveAt(0);
}
catch (Exception ex)
{
await Shell.Current.DisplayAlert("Aplicaçao", ex.ToString(), "Sair");
}
finally
{
}
}
}
MainPage.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:MauiMvvmApp="clr-namespace:MauiMvvmApp226"
x:Class="MauiMvvmApp226.MainPage">
<ContentPage.BindingContext>
<MauiMvvmApp:EditingViewModel></MauiMvvmApp:EditingViewModel>
</ContentPage.BindingContext>
<VerticalStackLayout
Spacing="25"
Padding="30,0"
VerticalOptions="Start">
<ListView ItemsSource="{Binding PatientInfo}">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Id}"
Detail="{Binding Name}"
TextColor="#f35e20"
DetailColor="#503026" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button
Command="{ Binding DeleteCommand}"
x:Name="CounterBtn"
Text="Click me"
HorizontalOptions="Center" />
</VerticalStackLayout>
</ContentPage>  
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论