แอพ Photo browser WPF - การปรับปรุงประสิทธิภาพ

Aug 18 2020

ฉันกำลังทำงานกับแอพ WPF photo broswer เป็นโปรเจ็กต์ส่วนตัว ฉันตระหนักดีว่ากลยุทธ์ในปัจจุบันของฉันสำหรับการจัดการภาพขนาดย่อนั้นไม่ดีเนื่องจากฉันอ่านภาพเต็มทุกครั้งซึ่งจะแสดงเมื่อเรียกดูโฟลเดอร์ที่มีรูปภาพมากกว่าสองสามภาพ หากใครสามารถแนะนำการเปลี่ยนแปลงที่อาจช่วยปรับปรุงด้านนี้ของโค้ดหรืออย่างอื่นได้นั่นจะเป็นประโยชน์มาก

โค้ดรุ่นบางลงอยู่ด้านล่าง การดำเนินการนี้จะใช้โฟลเดอร์และแสดงรูปย่อและชื่อของรูปภาพภายในโฟลเดอร์ (แอพตัวเต็มยังมีปุ่มสำหรับดำเนินการต่างๆกับรูปภาพ)

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

ฉันคิดว่าผู้ตั้งค่าทรัพย์สินของคุณบางคนทำมากเกินไป ตัวอย่างเช่นในMainWindowตัวFolderตั้งค่าคุณสมบัติไม่เพียงแค่ตั้งชื่อโฟลเดอร์ แต่ยังต้องใช้เวลาในการแจกแจงและรวบรวมไฟล์ทั้งหมดภายในโฟลเดอร์นั้นด้วย นี้ไปกับSingle รับผิดชอบหลักการและอาจจะเป็นหลักการน้อยมหัศจรรย์ ฉันใช้แอพพลิเคชั่นแบบนั้นและรู้สึกหงุดหงิดที่เมื่อสิ่งที่ฉันอยากทำคือไปที่โฟลเดอร์ แต่ดูเหมือนว่าแต่ละคลิกจะบังคับให้อ่านจากโฟลเดอร์ ในระยะสั้นการตั้งค่าโฟลเดอร์ควรตั้งค่าโฟลเดอร์เท่านั้นและการระบุไฟล์ในโฟลเดอร์นั้นควรเป็นการดำเนินการแยกกัน

ประสิทธิภาพอาจได้รับประโยชน์จากการใช้การโทรแบบ async และสตรีมมิ่งบางครั้ง คุณสตรีมด้วยEnumerateFilesแต่คุณเพิ่มลงในรายการ ซึ่งอาจทำให้ได้รับผลกระทบด้านประสิทธิภาพ ฉันจะตรวจสอบทันทีที่คุณคิดว่าคุณต้องอ่านไฟล์และชะลอการอ่านจนกว่าจะถึงเวลานั้น

คุณควรอ่านข้อมูลเกี่ยวกับวิธีการ async ใด ๆ BitMapที่เกี่ยวข้องกับ ฉันพบโพสต์นี้ว่าจะโหลดบิตแมปแบบอะซิงโครนัสกับ C # ได้อย่างไร และคุณอาจพิจารณาใช้งานพื้นหลังเพื่อแจกแจงไฟล์หรือโหลดภาพขนาดย่อ