ObservableCollection在WPF中没有自动通知UI

qvtsj1bj  于 2023-10-22  发布在  其他
关注(0)|答案(1)|浏览(123)

我正在尝试构建我的第一个基于MVVM模式的应用程序。
我仍然没有隔离按钮点击动作的视图,因为我在这个问题上卡住了ObservableCollection
这些是类:
模型

public class Anagrafica
{   
    public int Cod {  get; set; }
    public string RSoc { get; set; }
    public string Via { get; set; }
    public string Cap { get; set; }
    public string Cit {  get; set; }
    public string Pro { get; set; }
    public string Naz {  get; set; }
    public string Piva { get; set; }
    public string Cfis { get; set; }
    public string NTel { get; set; }
    public string NFax { get; set; }
    public string Email { get; set; }
    public string Ntel { get; set; }
    public string Web { get; set; }
    public string CPag { get; set; }
    public string IBAN { get; set; }
    public string Note { get; set; }
}

ModelService

public interface IAnagraficheSVC
{
    ObservableCollection<Anagrafica> Anagrafiche {  get; }

    void CercaAna(int cod);
}
public class AnagraficheSVC:IAnagraficheSVC
{
    private ObservableCollection<Anagrafica> _anagrafiche = new ObservableCollection<Anagrafica>();

    public ObservableCollection<Anagrafica> Anagrafiche => _anagrafiche;
    public void CercaAna(int cod)
    {
        using (OleDbConnection conn = new OleDbConnection($"PROVIDER=Microsoft.Ace.OLEDB.12.0;Data Source= { Properties.Settings.Default.dbdir }")) 
        {
            if (conn == null)
            {
                throw new Exception("Connection String is Null.");
            }

            OleDbCommand query = new OleDbCommand($"SELECT * from Cli WHERE Cod={cod}", conn);
            OleDbDataAdapter anaDA = new OleDbDataAdapter(query);
            DataTable anaDT = new DataTable();
            anaDA.Fill(anaDT);

            foreach (DataRow row in anaDT.Rows)
            {
                Anagrafica a = new Anagrafica();
                a.Cod = (int)row["Cod"];
                a.RSoc = row["Rsoc"].ToString();
                a.Via = row["IVia"].ToString();
                a.Cap= row["ICap"].ToString();
                a.Cit= row["ICit"].ToString();
                a.Pro= row["IPro"].ToString();
                a.Naz= row["KInt"].ToString();
                a.Piva= row["PIva"].ToString();
                a.Cfis= row["CFis"].ToString();
                a.Email = row["Emai"].ToString();
                a.NTel= row["NTel"].ToString();
                a.Web= row["KUrl"].ToString();
                a.CPag= row["CPag"].ToString();
                a.IBAN= row["NBan"].ToString();
                a.Note = row["No01"].ToString();

                _anagrafiche.Add(a);
            }

        }
    }
}

ViewModel

public class AnagraficaVM
{
    private IAnagraficheSVC _anagraficheSVC = null;

    public ObservableCollection<Anagrafica> Anagrafiche => _anagraficheSVC.Anagrafiche;

    public AnagraficaVM(IAnagraficheSVC anagraficheSVC)
    {
        _anagraficheSVC=anagraficheSVC;
    }

    public void cercaAna(int cod)
    {
        _anagraficheSVC.CercaAna(cod);
    }
}

View.xaml.cs

public partial class MainWindow : Window
{
    public MainWindow(AnagraficaVM AnaVM)
    {
        InitializeComponent();
        AnaTab.DataContext = AnaVM;
    }

    private void Cercabtn_Click(object sender, RoutedEventArgs e)
    {
        (AnaTab.DataContext as AnagraficaVM).cercaAna(int.Parse(txtCod.Text));
    }
}

视图(部分)

<TabItem Header="Anagrafica" Name="AnaTab">
    <Border Name="pagborder" Padding="20">
        <Grid Name="AnaGrid" DataContext="{Binding Anagrafiche}">
            <Grid.RowDefinitions>
                <RowDefinition Height="*"></RowDefinition>
                <RowDefinition Height="*"></RowDefinition>
                <RowDefinition Height="*"></RowDefinition>
                <RowDefinition Height="25"></RowDefinition>
                <RowDefinition Height="25"></RowDefinition>
                <RowDefinition Height="*"></RowDefinition>
                <RowDefinition Height="*"></RowDefinition>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Label Name="lblCod" Style="{StaticResource cod}" FontSize="16" Grid.Column="0" HorizontalAlignment="Left" VerticalAlignment="Center"></Label>
            <TextBox Name="txtCod"  HorizontalAlignment="Left" VerticalAlignment="Center" Width="50" Margin="130,0,0,0" Height="18" Text="{Binding Cod, UpdateSourceTrigger=Explicit}"/>
            </ToggleButton>
            <Rectangle Grid.Row="1" Grid.ColumnSpan="4" Fill="Transparent" Height="10"/>
            <Label Grid.Row="2" Grid.Column="0" FontSize="14" Background="#576CBC">Dati Anagrafici</Label >
            <Label Grid.Row="3" Grid.Column="0">Ragione Sociale</Label>
            <Label Grid.Row="3" Grid.Column="1">Indirizzo</Label>
            <TextBox Grid.Row="4" Grid.Column="0" Name="RagSoc" Text="{Binding RSoc,UpdateSourceTrigger=Explicit}"/>
            <TextBox Grid.Row="4" Grid.Column="1" Name="Indirizzo" Width="210"  Text="{Binding Via}"/>

App.xaml.cs

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        AnagraficheSVC anagraficheSVC = new AnagraficheSVC();
        AnagraficaVM anaVM = new AnagraficaVM(anagraficheSVC);
        Views.MainWindow mainWindow = new Views.MainWindow(anaVM);
        mainWindow.Show();
    }
}

如果我理解正确的话,ObservableCollection应该在一个项目被添加、删除、替换时自动通知UI。
在我的案例中没有发生的事情:我绑定到ViewModel的ObservableCollection的文本框没有被数据填充(而调试数据在ObservableCollection内部)。
如果我在_anagraficheSVC.CercaAna(cod);之前添加_anagraficheSVC.Anagrafiche.Clear();,我插入索引的文本框txtCod被清除(所以我猜.Clear()得到通知?).
我想我错过了一些东西,因为它应该工作。
既然我已经占用了你的时间,你认为这是MVVM模式的一个很好的实现吗?具体来说,方法调用_anagraficheSVC.CercaAna(cod);来填充ObservableCollection:合法吗?
非常感谢您的宝贵时间!
编辑:
如果我在文本框(AnaGrid)的同一个容器中添加一个测试ListBox,并将ItemsSource绑定到Anagrafiche,它可以工作:每次我点击按钮,我看到一个新的Models.Anagrafica项目添加到列表中。
DataContext是否监听CollectionChanged
更新和解决方案:
我就知道会是很蠢的事。
通过输入{Binding Anagrafiche},我将Grid绑定到ObservableCollection对象,该对象当然没有属性,只有Anagrafica项。
通过输入{Binding Anagrafiche[0]},我得到了第一个项目与他的属性,一切正常。我甚至不需要任何PropertyChanged事件来添加或删除列表中的项目。
谢谢大家,特别是HeldHasp,谢谢你们的努力和帮助。

lpwwtiir

lpwwtiir1#

型号public class Anagrafica
这不是来自“MV* Patterns”组的模型。在这些模式中,模型是包含所有业务(领域)逻辑的应用程序的层。类Anagrafica是一个实体。可能是DTO。这些类在ADO中称为模型。但ADO是Repository层,它是应用程序模型实现的一部分。
ModelService
该服务实际上是MVVM中的一个模型。考虑到您没有收到_anagrafiche集合可能发生更改的通知,我建议您更改实现以避免任何随机错误。

public class AnagraficheSVC:IAnagraficheSVC
{
    // private ObservableCollection<Anagrafica> _anagrafiche = new ObservableCollection<Anagrafica>();

    public ObservableCollection<Anagrafica> Anagrafiche {get;} = new();

视图(部分)
老实说,我不能说我完全理解你想在GUI中显示什么。我假设您希望为Anagrafiche集合的每个元素创建一个选项卡,并在选项卡中显示元素的详细信息。
如果我没猜错,应该是这样的:

<TabControl ItemsSource="{Binding Anagrafiche}"
            DisplayMemberPath="Some Property Anagrafica">
    <TabControl.ContentTemplate>
        <DataTemplate DataType="{x:Type model:Anagrafica}">
            <Border Name="pagborder" Padding="20">
                <Grid Name="AnaGrid">
                    <Grid.RowDefinitions>
             <!-- Continion XAML code to represent one Anagrafica object -->
    </TabControl.ContentTemplate>
             <!-- Continuation of the XAML code -->
</TabControl>

这就是github.com/Tvuce/WPFpj.git
下面是一个工作实现:

public class User
    {
        public string Name { get; set; } = string.Empty;
        public string Surn { get; set; } = string.Empty;
        public int Age { get; set; }
    }
public class UsersViewModel
    {
        public ObservableCollection<User> Users { get; } = new();

        private RelayCommand? _addUser;
        public RelayCommand AddUser => _addUser ??= new(() => Users.Add(new User() { Name = "cane: " + Users.Count, Surn = "lupo", Age = 15 }));
    }
<Window x:Class="testbinding.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:testbinding"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800" Background="DarkBlue">
    <Window.DataContext>
        <local:UsersViewModel/>
    </Window.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <TabControl ItemsSource="{Binding Users}" DisplayMemberPath="Name">
            <TabControl.ContentTemplate>
                <DataTemplate DataType="{x:Type local:User}">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition ></ColumnDefinition>
                            <ColumnDefinition ></ColumnDefinition>
                        </Grid.ColumnDefinitions>
                        <Grid.RowDefinitions>
                            <RowDefinition></RowDefinition>
                            <RowDefinition></RowDefinition>
                            <RowDefinition></RowDefinition>
                            <RowDefinition></RowDefinition>
                        </Grid.RowDefinitions>
                        <TextBox Grid.Row="0" Grid.Column="0"
                                 Height="20" Width="100"
                                 Text="{Binding Name}"/>
                        <TextBox Grid.Row="0" Grid.Column="1"
                                 Height="20" Width="100"
                                 Text="{Binding Surn}"/>
                        <TextBox Grid.Row="1" Grid.Column="0"
                                 Height="20" Width="100"
                                 Text="{Binding Age}"/>
                    </Grid>
                </DataTemplate>
            </TabControl.ContentTemplate>
        </TabControl>
        <Button Grid.Column="0" Grid.Row="1"
                Command="{Binding AddUser}"
                Background="LightGray" Width="100" Height="60">Add User</Button>
    </Grid>
</Window>
public partial class MainWindow : Window
    {
        // Getting ViewModel in Code Behind from DataContext.
        //In general, it is better not to do this. And leave Code Behind empty.
        private readonly UsersViewModel viewModel;
        public MainWindow()
        {
            InitializeComponent();

            viewModel = (UsersViewModel)DataContext;
        }
    }

.我读文本框只绑定到PropertyChanged事件.
如果只需要显示一个元素(对象),那么最好使用INotifyPropertyChanged.PropertyChanged。但是为了举例,为了学习和提高理解,我将向您展示如何使用ObservableCollection来完成这一点。

public partial class MainWindow : Window
    {
        public ObservableCollection<User> o;
        public MainWindow()
        {
            InitializeComponent();
            o = new ObservableCollection<User>();
            DataContext = o;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            User p = new User() { name = "cane", surn = "lupo", age = 15 };
            if (o.Count == 0)
                o.Add(p);
            else
                o[0] = p;
        }
<Window x:Class="testbinding.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:testbinding"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800" Background="DarkBlue">
    <Grid DataContext="{Binding [0]}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition ></ColumnDefinition>
            <ColumnDefinition ></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <TextBox Grid.Row="0" Grid.Column="0" Name="nome" Height="20" Width="100" Text="{Binding name}"></TextBox>
        <TextBox Grid.Row="0" Grid.Column="1" Name="cogn" Height="20" Width="100" Text="{Binding surn}"></TextBox>
        <TextBox Grid.Row="1" Grid.Column="0" Name="eta" Height="20" Width="100" Text="{Binding age}"></TextBox>
        <Button Grid.Column="0" Grid.Row="2" Click="Button_Click" Background="LightGray" Width="100" Height="60">Press</Button>
    </Grid>
</Window>

相关问题