Aplikasi WPF browser foto - peningkatan kinerja

Aug 18 2020

Saya sedang mengerjakan aplikasi broswer foto WPF sebagai proyek pribadi. Saya menyadari strategi saya saat ini untuk penanganan thumbnail buruk karena saya membaca dalam gambar penuh setiap kali - ini terlihat saat menjelajahi folder dengan lebih dari beberapa gambar. Jika ada yang bisa menyarankan perubahan yang mungkin memperbaiki aspek kode ini, atau yang lainnya, itu akan sangat berguna.

Versi kode yang lebih ramping ada di bawah. Ini mengambil folder dan menampilkan thumbnail dan nama gambar di dalamnya. (Aplikasi lengkap juga memiliki tombol untuk melakukan berbagai operasi pada gambar.)

MainWindow.xaml.cs

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Runtime.CompilerServices;
using System.Windows;

namespace PhotoBrowser
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = new MainWindowViewModel();
        }
    }

    class MainWindowViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged([CallerMemberName] string name = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }

        private string _folder;
        public string Folder
        {
            get => _folder;
            set
            {
                _folder = value;
                if (Directory.Exists(_folder))
                {
                    var filesInFolder = Directory.EnumerateFiles(_folder);
                    var files_ = new ObservableCollection<FileItem>();
                    foreach (string file in filesInFolder)
                    {
                        files_.Add(new FileItem(file, false));
                    }
                    Files = files_;
                }
                else
                {
                    Files = new ObservableCollection<FileItem>();
                }
                OnPropertyChanged();
            }
        }

        private ObservableCollection<FileItem> _files;
        public ObservableCollection<FileItem> Files
        {
            get => _files;
            set
            {
                _files = value;
                OnPropertyChanged();
            }
        }

        public MainWindowViewModel() { }
    }
}

MainWindow.xaml

<Window x:Class="PhotoBrowser.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:PhotoBrowser"
        mc:Ignorable="d"
        Title="Photo Browser" Height="800" Width="800" MinHeight="300" MinWidth="400">
    <Grid HorizontalAlignment="Stretch" Height="Auto" Margin="10,10,10,10" VerticalAlignment="Stretch" Width="Auto" ShowGridLines="False">
        <Grid.RowDefinitions>
            <RowDefinition Height="35"/>
            <RowDefinition Height="1*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="35"/>
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <TextBlock Text="Folder"
                       Grid.Column="0" HorizontalAlignment="Stretch" VerticalAlignment="Center"/>
        <TextBox x:Name="Folder" 
                    Text="{Binding Folder, UpdateSourceTrigger=PropertyChanged}" 
                    Grid.Column="1"
                    HorizontalAlignment="Stretch" Height="25" TextWrapping="NoWrap" Margin="5,5,5,5"
                    VerticalAlignment="Center"/>
        <ListBox x:Name="FilesInCurrentFolder" 
                Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="1" HorizontalAlignment="Left" VerticalAlignment="Stretch" 
                BorderBrush="Black" Height="Auto" Width="772" SelectionMode="Extended"
                ItemsSource="{Binding Files, UpdateSourceTrigger=PropertyChanged}" >
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <Image Source="{Binding Thumbnail}" Width="100" Height="100"/>
                        <TextBlock Text="{Binding Name}" VerticalAlignment="Center" />
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
            <ListBox.Resources>
                <Style TargetType="ListBoxItem">
                    <Setter Property="IsSelected" Value="{Binding Mode=TwoWay, Path=IsSelected, UpdateSourceTrigger=PropertyChanged}" />
                </Style>
            </ListBox.Resources>
        </ListBox>
    </Grid>
</Window>

FileItem.cs

using System;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Runtime.CompilerServices;
using System.Windows.Media.Imaging;

namespace PhotoBrowser
{
    public class FileItem : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName] string name = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }

        private string _fileName;
        public string FileName
        {
            get => _fileName;
            set
            {
                _fileName = value;
                OnPropertyChanged();
                OnPropertyChanged("Name");
            }
        }

        private string _name;
        public string Name
        {
            get => Path.GetFileName(_fileName);
            set
            {
                _name = value;
                _fileName = Path.Combine(Path.GetDirectoryName(_fileName), value);
                OnPropertyChanged();
                OnPropertyChanged("FileName");
            }
        }

        private bool _isSelected;
        public bool IsSelected
        {
            get => _isSelected;
            set
            {
                _isSelected = value;
                OnPropertyChanged();
            }
        }

        private BitmapSource _thumbnail;
        public BitmapSource Thumbnail
        {
            get => _thumbnail;
            set
            {
                _thumbnail = value;
                OnPropertyChanged();
            }
        }

        public FileItem(string fileName)
        {
            this.FileName = fileName;
            GetThumbnail();
        }

        public FileItem(string fileName, bool isSelected) : this(fileName)
        {
            this.IsSelected = isSelected;
        }

        public void GetThumbnail()
        {
            Image image = new Bitmap(FileName);
            image = image.GetThumbnailImage(100, 100, () => false, IntPtr.Zero);
            Thumbnail = new BitmapImage(new Uri(FileName));
        }
    }
}

Jawaban

1 RickDavin Aug 19 2020 at 20:23

Saya rasa beberapa pembuat properti Anda melakukan terlalu banyak hal. Misalnya MainWindow, Folderpenyetel properti tidak hanya menyetel nama folder tetapi juga membutuhkan waktu untuk menghitung dan mengumpulkan semua file di dalam folder itu. Ini bertentangan dengan Prinsip Tanggung Jawab Tunggal dan mungkin Prinsip Keterkejutan . Saya telah menggunakan aplikasi seperti itu dan menjadi frustrasi ketika yang ingin saya lakukan hanyalah menavigasi ke folder tetapi setiap klik tampaknya memaksa membaca dari folder. Singkatnya, mengatur folder seharusnya hanya mengatur folder, dan menghitung file dalam folder itu harus menjadi tindakan terpisah.

Performa bisa mendapatkan keuntungan dari mengimplementasikan beberapa panggilan asinkron dan membuat sesuatu tetap streaming. Anda melakukan streaming EnumerateFilestetapi Anda menambahkannya ke daftar. Ini bisa membuat kinerja terpukul. Saya akan menyelidiki persis saat Anda merasa perlu membaca file, dan menunda membacanya sampai saat itu juga.

Anda harus membaca metode asinkron apa pun yang terkait dengan BitMap. Saya menemukan posting ini bagaimana cara memuat bitmap secara asinkron dengan C #. Dan Anda mungkin mempertimbangkan untuk menggunakan Tugas latar belakang untuk menghitung file atau memuat thumbnail.