사진 브라우저 WPF 앱-성능 향상

Aug 18 2020

저는 개인 프로젝트로 WPF 사진 브라우저 앱에서 작업하고 있습니다. 나는 매번 전체 이미지를 읽을 때 썸네일 처리에 대한 현재 전략이 좋지 않다는 것을 알고 있습니다. 이것은 몇 개 이상의 이미지가있는 폴더를 탐색 할 때 나타납니다. 누군가가 코드의이 측면을 개선 할 수있는 변경 사항을 제안 할 수 있다면 매우 유용 할 것입니다.

코드의 슬림화 된 버전은 다음과 같습니다. 이것은 폴더를 가져와 그 안에있는 이미지의 썸네일과 이름을 표시합니다. (전체 앱에는 이미지에 대한 다양한 작업을 수행하는 버튼도 있습니다.)

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

답변

1 RickDavin Aug 19 2020 at 20:23

일부 속성 설정자가 너무 많은 일을하고 있다고 생각합니다. 예를 들어 MainWindowFolder속성 setter는 폴더 이름을 설정하지 않습니다뿐만 아니라 해당 폴더 내의 모든 파일을 열거하고 수집하는 시간이 걸립니다. 이것은 단일 책임 원칙 과 아마도 최소 경악 의 원칙에 위배 됩니다. 나는 그런 응용 프로그램을 사용했고 내가 원하는 것은 폴더로 이동하는 것이었지만 각 클릭은 폴더에서 강제로 읽는 것처럼 보입니다. 요컨대, 폴더 설정은 폴더 만 설정해야하며 해당 폴더의 파일을 열거하는 것은 별도의 작업이어야합니다.

일부 비동기 호출을 구현하고 스트리밍을 유지하면 성능이 향상 될 수 있습니다. 함께 스트리밍 EnumerateFiles하지만 목록에 추가합니다. 이로 인해 성능이 저하 될 수 있습니다. 파일을 읽어야한다고 생각하는 정확한 순간을 조사하고 그 순간까지 파일 읽기를 지연합니다.

와 관련된 모든 비동기 메서드를 읽어야합니다 BitMap. 이 게시물 은 C #을 사용하여 비트 맵을 비동기 적으로로드하는 방법을 찾습니다 . 백그라운드 작업을 사용하여 파일을 열거하거나 축소판을로드하는 것을 고려할 수 있습니다.