WPF将ObservableCollection绑定到ItemsControl-UI不会更新

时间:2018-10-15 15:30:20

标签: c# wpf binding observablecollection itemscontrol

这里相当新手在寻找帮助。

我正在尝试制作一种类似于“ Steam启动器”的东西,但是像当Steam一样,但是当我向文件中添加新游戏时(当前只是为了方便起见,将其存储在文本文件中。

>

添加游戏后,除非将DataContext切换到其他视图然后再返回,否则UI不会刷新。

这是主要的3页代码(没有发布完整的内容,请参阅GitHub):((如果出现严重错误,请原谅我)

ListView.xaml

<UserControl
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:local="clr-namespace:Game_Launcher.Views"
         xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
         xmlns:ViewModels="clr-namespace:Game_Launcher.ViewModels"
         x:Class="Game_Launcher.Views.ListView"
         d:DataContext="{d:DesignInstance Type=ViewModels:ListViewModel}"
         mc:Ignorable="d"
         d:DesignHeight="300" d:DesignWidth="300">
<Grid RenderTransformOrigin="0.693,0.49">

    <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
    </Grid.RowDefinitions>

    <!-- This section displays a card per game -->
    <ScrollViewer VerticalScrollBarVisibility="Auto" Grid.ColumnSpan="3" Grid.RowSpan="7">
        <ItemsControl x:Name="gameListView" Grid.ColumnSpan="3" Margin="10,10,0,10" Grid.RowSpan="7" ItemsSource="{Binding Path=games, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Vertical">
                        <materialDesign:Card Margin="10 5 10 0" Grid.ColumnSpan="3" Grid.RowSpan="5" Height="80">
                            <Grid>
                                <Image x:Name="GameIcon" Width="60" HorizontalAlignment="Left" Source="{Binding Icon, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" Grid.RowSpan="2" />
                                <Label x:Name="GameTitle" Content="{Binding Title, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" Margin="65,1,0,45" Width="175" Height="29" FontSize="20" HorizontalAlignment="Left" />
                                <Label x:Name="GameGenre" Content="{Binding Genres, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="65,35,0,0" RenderTransformOrigin="0.5,0.469" />
                                <TextBlock HorizontalAlignment="Left" VerticalAlignment="Top" Margin="72,53,10,0" RenderTransformOrigin="0.5,0.469" FontSize="9">
                                <Hyperlink NavigateUri="{Binding Link, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" RequestNavigate="Hyperlink_Link">
                                    <TextBlock x:Name="GameLink" Text="{Binding Link, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
                                </Hyperlink>
                            </TextBlock>
                                <Button x:Name="GameLauncher" Click="LaunchButton_OnClick" Tag="{Binding Path, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,16,6,19" Height="40" Width="40" Style="{StaticResource MaterialDesignFloatingActionButton}">
                                    <materialDesign:PackIcon Kind="Gamepad" />
                                </Button>
                            </Grid>
                        </materialDesign:Card>
                    </StackPanel>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </ScrollViewer>
</Grid>

ListView.xaml.cs

using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using System.Collections.ObjectModel;
using System.IO;
using System.Diagnostics;
using Game_Launcher.ViewModels;

namespace Game_Launcher.Views
{
    /// <summary>
    /// Interaction logic for ListView.xaml
    /// </summary>
    public partial class ListView : UserControl
    {
        private ListViewModel lvm = new ListViewModel();

        public ListView()
        {
            InitializeComponent();
            lvm.refreshGames();
            gameListView.ItemsSource = lvm.games;
        }

        //Open web browser when link clicked
        private void Hyperlink_Link(object sender, RequestNavigateEventArgs e)
        {
            Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri));
        }

        //Launch game path when clicked
        private void LaunchButton_OnClick(object sender, RoutedEventArgs e)
        {
            object link = ((Button)sender).Tag;
            string linkString = link.ToString().Trim();
            Process.Start(new ProcessStartInfo(linkString));
        }
    }
}

ListViewModel.cs

using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;

namespace Game_Launcher.ViewModels
{
    public class ListViewModel : INotifyPropertyChanged
    {
        public ObservableCollection<Game> games { get; set; }

        public void refreshGames()
        {
            games = new ObservableCollection<Game>();
            if (File.Exists("./Resources/GamesList.txt"))
            {
                //Read file to gameFile
                string gameFile = "./Resources/GamesList.txt";
                StreamReader sr = new StreamReader(gameFile, true);
                //games is array containing all game info per item
                string[] gamesArr = File.ReadAllLines(gameFile);
                int numberOfGames = 0;
                string[] columns = new string[0];
                //columns is array containing each element of the game
                foreach (var item in gamesArr)
                {
                    columns = gamesArr[numberOfGames].Split('|');
                    Game g = new Game
                    {
                        Title = columns[0],
                        Genres = columns[1],
                        Path = columns[2],
                        Link = columns[3],
                        Icon = columns[4],
                        Poster = columns[5],
                        Banner = columns[6],
                    };

                    games.Add(new Game
                    {
                        Title = g.Title,
                        Genres = g.Genres,
                        Path = g.Path,
                        Link = g.Link,
                        Icon = g.Icon,
                        Poster = g.Poster,
                        Banner = g.Banner,
                    });
                    numberOfGames++;
                }
                sr.Close();
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public class Game : INotifyPropertyChanged
    {
        private string _title;
        private string _genres;
        private string _path;
        private string _link;
        private string _icon;
        private string _poster;
        private string _banner;

        public string Title { get { return _title; } set { _title = value; OnPropertyChanged(Title); } }
        public string Genres { get { return _genres; } set { _genres = value; OnPropertyChanged(Genres); } }
        public string Path { get { return _path; } set { _path = value; OnPropertyChanged(Path); } }
        public string Link { get { return _link; } set { _link = value; OnPropertyChanged(Link); } }
        public string Icon { get { return _icon; } set { _icon = value; OnPropertyChanged(Icon); } }
        public string Poster { get { return _poster; } set { _poster = value; OnPropertyChanged(Poster); } }
        public string Banner { get { return _banner; } set { _banner = value; OnPropertyChanged(Banner); } }

        #region INotifyPropertyChanged

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        #endregion INotifyPropertyChanged
    }
}

2 个答案:

答案 0 :(得分:1)

不要在每次填充新ObservableCollection实例时创建它。只需Clear()当前实例中的现有项目,然后Add()将您的新项目集添加到其中。

public ListViewModel()
{
    Games = new ObservableCollection<Game>();
}

...

public ObservableCollection<Game> Games { get; }

public void refreshGames()
{
    Games.Clear()

    ...

    // add items to Games collection here
}

答案 1 :(得分:-1)

您的问题是您要使用两个单独的视图模型。删除处理ListViewModel的这些位:

 private ListViewModel lvm = new ListViewModel();

    public ListView()
    {
        InitializeComponent();
        lvm.refreshGames();
        gameListView.ItemsSource = lvm.games;
    }

您应该在XAML中创建它的实例:

<UserControl>
    <UserControl.DataContext>
        <ViewModels:ListViewModel />
    </UserControl.DataContext>
</UserControl>