简体   繁体   English

C# WPF MVVM - 当项目添加到 VM 实例中的 ObservableCollection 时视图不会改变

[英]C# WPF MVVM - View doesn't change when Item Added to ObservableCollection in VM Instance

Description描述

I am currently building an app where if the user triggers an event on a combobox found on the Main View, it will take the selecteditem and add it to a ObservableCollection on a secondary view, when the user triggers the event to add the item, it successfully adds it to the Collection but does not update the UI in the ItemsControl that the ObservableCollection is a binding of.我目前正在构建一个应用程序,如果用户在主视图上找到的 combobox 上触发事件,它将获取所选项目并将其添加到辅助视图上的 ObservableCollection 中,当用户触发事件以添加项目时,它成功将其添加到集合中,但不会更新 ObservableCollection 绑定的 ItemsControl 中的 UI。

Problem问题

UI isn't updating when ObservableCollection has an item added to it in another ViewModel.当 ObservableCollection 在另一个 ViewModel 中添加了一个项目时,UI 不会更新。

JobList工作清单

JobList is derived from the ObservableCollection<Job> type allowing for the loading of later stored data using xml on loading the application, it has a limit of 6 items. JobList 派生自ObservableCollection<Job>类型,允许在加载应用程序时使用 xml 加载以后存储的数据,它有 6 个项目的限制。

class JobList : ObservableCollection<Job>
{
    public JobList () : base ()
    {
        if (JobListExists)
            LoadExistingJobList();
    }

    private bool JobListExists
    {
        get {

            bool result = false;

            if (File.Exists(recentsPath + @"\RecentItems.xml"))
            {
                result = true;
            }

            return result;
        }
    }

    private void LoadExistingJobList ()
    {
        using (var reader = new StreamReader(recentsPath + @"\RecentItems.xml"))
        {
            XmlSerializer deserializer = new XmlSerializer(typeof(List<Job>),
                new XmlRootAttribute("Jobs"));
            List<Job> jobs = (List<Job>)deserializer.Deserialize(reader);

            foreach (Job job in jobs)
            {
                Add(job);
            }
        }
    }

    public void AddToCollection (Job job)
    {
        Insert(0, job);

        if (Count == 7)
            RemoveAt(6);
    }
}

MainView主视图

The MainView is being used for the MainWindow and stores the SearchBox (To be used in multiple places) and a ContentControl that stores the CurrentView. MainView 用于 MainWindow 并存储 SearchBox(在多个地方使用)和存储 CurrentView 的 ContentControl。 When an event occurs from here, no subsequent changes happen to the view bound to the ContentControl.当从这里发生事件时,绑定到 ContentControl 的视图不会发生后续更改。

<Window.DataContext>
   <viewModel:MainViewModel/>
</Window.DataContext>

    <Border Background="#272537"
            CornerRadius="15">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="50"/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            
            <Grid.RowDefinitions>
                <RowDefinition Height="50"/>
                <RowDefinition/>
            </Grid.RowDefinitions>

            <Image Source="/Images/LegIcon.png" Width="30" Height="30" />

            <StackPanel Grid.Row="1">
                <RadioButton Height="40" Margin="5" Content="/Images/Home.png" Style="{StaticResource MenuButtonTheme}" IsChecked="True" Command="{Binding HomeViewCommand}"/>
                <RadioButton Height="40" Margin="5" Content="/Images/Globals.png" Style="{StaticResource MenuButtonTheme}" Command="{Binding GlobalViewCommand}"/>
            </StackPanel>
            <RadioButton Grid.Row="1" Height="40" Margin="5" Content="/Images/Settings.png" VerticalAlignment="Bottom" Style="{StaticResource MenuButtonTheme}"/>

            <StackPanel Grid.Column="1" Orientation="Horizontal">
                <ComboBox Width="190"
                     Height="40"
                     VerticalAlignment="Center"
                     HorizontalAlignment="Left"
                     Margin="5"
                     Grid.Column="1"
                     Style="{StaticResource SearchBoxTheme}"
                     ItemsSource="{Binding CurrentDropdownData}"
                        IsEditable="true"
                     DisplayMemberPath="Name"
                     IsSynchronizedWithCurrentItem="True">
                    <ComboBox.InputBindings>
                        <KeyBinding Gesture="Enter"
                        Command="{Binding SearchBoxCommand}"
                        CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type ComboBox}}}"/>
                    </ComboBox.InputBindings>
                </ComboBox>

                <Button Name="MinimizeButton" Style="{StaticResource MinimizeButton}" Margin="5" Width="40" Click="MinimizeButton_Click" />
            </StackPanel>
            <ContentControl Grid.Row="1"
                            Grid.Column="1"
                            Content="{Binding CurrentView}"/>
        </Grid>
    </Border>

MainViewModel主视图模型

class MainViewModel : ObservableObject
{
    public RelayCommand HomeViewCommand { get; set; }
    public RelayCommand GlobalViewCommand { get; set; }
    public RelayCommand SearchBoxCommand { get; set; }

    public HomeViewModel HomeVM { get; set; }
    public GlobalViewModel GlobalVM { get; set; }

    private object _currentView;

    public object CurrentView
    {
        get { return _currentView; }
        set
        {
            _currentView = value;
            OnPropertyChanged();
        }
    }

    private ICollectionView _currentDropdownData;

    public ICollectionView CurrentDropdownData
    { 
        get { return _currentDropdownData; }
        set
        {
            _currentDropdownData = value;
            OnPropertyChanged("CurrentDropdownData");
        }
    }

    public MainViewModel()
    {
        CurrentDropdownData = CollectionViewSource.GetDefaultView(DataProvider.GetProjectItems());
        HomeVM = new HomeViewModel();
        GlobalVM = new GlobalViewModel();
        CurrentView = HomeVM;

        HomeViewCommand = new RelayCommand(o =>
        {
            CurrentDropdownData = CollectionViewSource.GetDefaultView(DataProvider.GetProjectItems());
            CurrentView = HomeVM;
            SearchBoxCommand = new RelayCommand(e =>
            {
                ((HomeViewModel)CurrentView).Collection.AddToCollection((Job)CurrentDropdownData.CurrentItem);
            });
        });

        GlobalViewCommand = new RelayCommand(o =>
        {
            CurrentDropdownData = CollectionViewSource.GetDefaultView(DataProvider.GetGlobalItems());
            CurrentView = GlobalVM;
            SearchBoxCommand = new RelayCommand(e =>
            {
            });
        });

        SearchBoxCommand = new RelayCommand(e =>
        {
            ((HomeViewModel)CurrentView).Collection.AddToCollection((Job)CurrentDropdownData.CurrentItem);
        });
    }
}

HomeView主页查看

The Home View solely uses a ItemsControl that refers to the "Collections" JobList as a DataBinding to display the currently selected Jobs Home View 仅使用 ItemsControl 引用“Collections”JobList 作为 DataBinding 来显示当前选择的 Jobs

<UserControl.DataContext>
        <viewModel:HomeViewModel/>
    </UserControl.DataContext>
    <ItemsControl x:Name="TilePanel" ItemsSource="{Binding Collection, Mode=TwoWay}" xmlns:model="clr-namespace:JobMate_2.MVVM.Model">
        <ItemsControl.ItemTemplate>
            <DataTemplate DataType="{x:Type model:Job}">
                <Border Background="#353340"
                        CornerRadius="5" 
                        Margin="5"
                        Height="50">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition/>
                            <ColumnDefinition Width="25"/>
                        </Grid.ColumnDefinitions>

                        <Grid.RowDefinitions>
                            <RowDefinition/>
                            <RowDefinition/>
                        </Grid.RowDefinitions>

                        <TextBlock Text="{Binding Name, Mode=OneWay}"
                                   Foreground="White"
                                   Margin="5"
                                   FontFamily="/Fonts/#Poppins"
                                   x:Name="JobTitle"/>

                        <Button Grid.Column="1"
                                Content="."
                                HorizontalAlignment="Stretch"
                                VerticalAlignment="Stretch"
                                Margin="2.5"
                                Visibility="Hidden"/>

                        <UniformGrid Columns="3" Grid.Row="1">
                            <Button Content="Shared" Margin="2.5" Style="{StaticResource FileLocationButton}"/>
                            <Button Content="Job Data" Margin="2.5" Style="{StaticResource FileLocationButton}"/>
                            <Button Content="Proc. Data" Margin="2.5" Style="{StaticResource FileLocationButton}"/>
                        </UniformGrid>
                    </Grid>
                </Border>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>

HomeViewModel主页查看模型

The HomeViewModel very simply stores the Collection that is being used in the View for databinding. HomeViewModel 非常简单地存储了在视图中用于数据绑定的集合。

class HomeViewModel : ObservableObject
{
    private JobList _collection;

    public JobList Collection
    {
        get { return _collection; }
        set
        {
            _collection = value;
            OnPropertyChanged("Collection");
        }
    }

    public HomeViewModel ()
    {
        Collection = new JobList()
        {
            new Job () { Name = "Test" },
            new Job () { Name = "Test 2" },
            new Job () { Name = "Test 3" }
        };
    }
}

There can be only one reason.可能只有一个原因。
You have different instances of HomeViewModel in Window and UserControl.您在 Window 和 UserControl 中有不同的HomeViewModel实例。
The UserControl does not have to create its own Data Context. UserControl 不必创建自己的数据上下文。
He should get it from the Window by binding to the CurrentView.他应该通过绑定到 CurrentView 从 Window 获取它。

If you create an instance of a UserControl in a Window with the MainViewModel in its Data Context, then you need a binding like this:如果您在 Window 中创建 UserControl 的实例,并在其数据上下文中使用 MainViewModel,那么您需要这样的绑定:

<UserControl *****
    ***********
    DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.CurrentView}">
    <d:UserControl.DataContext>
        <viewModel:HomeViewModel/>
    </d:UserControl.DataContext>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 WPF - MVVM - 更改 VM 实例的属性时视图不会更新 - WPF - MVVM - View doesn't update when changing properties of a VM instance C# WPF MVVM 文本框值不变 - C# WPF MVVM TextBox value doesn't change WPF / C#将ObservableCollection中项的属性更改更新为ListBox - WPF / C# Updating property change of an item in an ObservableCollection to the ListBox C# WPF ListView MVVM ObservableCollection 在 ObservableCollection 内部 - C# WPF ListView MVVM ObservableCollection inside of an ObservableCollection C#-ObservableCollection似乎没有保存添加的项目 - c# - ObservableCollection doesn't seem save added items WPF ItemsControl元素不会更改ObservableCollection值更改MVVM的可见性 - Wpf ItemsControl element doesn't change visibility on ObservableCollection value change MVVM 当ObservableCollection中的项目更新时,ItemsControl视图不会更新 - ItemsControl view doesn't updated when an item in ObservableCollection is updated C# WPF 带有字典的 ObservableCollection 到 MVVM Datagrid - C# WPF ObservableCollection with dictionary inside to MVVM Datagrid 如何在 MVVM WPF C# 的 ComboBox 中显示来自 ObservaBleCollection 的值 - How to Display Values from ObservaBleCollection in ComboBox In MVVM WPF C# 刷新ObservableCollection <T> 当T中的属性更改时-WPF MVVM - Refresh ObservableCollection<T> when a property in T changes - WPF MVVM
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM