【问题标题】:C# WPF MVVM - View doesn't change when Item Added to ObservableCollection in VM InstanceC# WPF MVVM - 将项目添加到 VM 实例中的 ObservableCollection 时视图不会更改
【发布时间】:2021-07-21 17:27:26
【问题描述】:

说明

我目前正在构建一个应用程序,如果用户在主视图上找到的组合框上触发事件,当用户触发事件以添加项,它成功地将其添加到集合中,但不会更新 ObservableCollection 绑定的 ItemsControl 中的 UI。

问题

当 ObservableCollection 在另一个 ViewModel 中添加项目时,UI 不会更新。

工作清单

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 用于 MainWindow 并存储 SearchBox(将在多个地方使用)和存储 CurrentView 的 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);
        });
    }
}

主视图

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

HomeViewModel 非常简单地存储了在 View 中用于数据绑定的 Collection。

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" }
        };
    }
}

【问题讨论】:

    标签: c# wpf xaml mvvm observablecollection


    【解决方案1】:

    可能只有一个原因。
    您在 Window 和 UserControl 中有不同的 HomeViewModel 实例。
    UserControl 不必创建自己的数据上下文。
    他应该通过绑定到 CurrentView 从 Window 中获取它。

    【讨论】:

    • 抱歉这个愚蠢的问题,我对 MVVM 模型有点新,我认为这意味着从“HomeView”脚本中删除 &lt;UserControl.DataContext&gt; &lt;viewModel:HomeViewModel/&gt; &lt;/UserControl.DataContext&gt;,但是我如何从主视图模型?
    • 显示使用此 UserControl 的布局 (XAML)。
    • 如果布局是指我在其中布局 UserControl 的视图,它是原始问题中的 HomeView 部分,它使用 ItemsControl 来布局 StackPanel 及其 ItemTemplate。如果您的意思是在 MainView 的哪个位置使用它,它可以在原始问题的 MainView 部分的 ContentControl 下找到。
    • 从头开始,没有注意到我需要在 ItemsControl 上设置{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.HomeVM.Collection} 以获取 Windows 数据上下文。感谢您的帮助!
    【解决方案2】:

    如果您在数据上下文中使用 MainViewModel 在窗口中创建 UserControl 的实例,那么您需要这样的绑定:

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

    【讨论】:

      猜你喜欢
      • 2021-06-09
      • 2023-03-08
      • 1970-01-01
      • 1970-01-01
      • 2011-08-25
      • 2014-03-21
      • 1970-01-01
      • 1970-01-01
      • 2016-01-19
      相关资源
      最近更新 更多