Какая привязка будет лучше для изображения, будь то привязка Byte [] или строка для хорошей производительности - PullRequest
0 голосов
/ 04 августа 2020

Я работаю над проектом WPF, в котором окно содержит список пользователей и изображения их профилей. Ниже представлена ​​модель User.

public class UserModel
    {
        public Guid? Id { get; set; }

        public string Name { get; set; }

        public string ProfileImage { get; set; }
    }

И вот как я привязываю UserImage

<Rectangle Grid.Column="0" RadiusX="20" RadiusY="20" Margin="0,1,0,0" Width="35" Height="35" HorizontalAlignment="Center" VerticalAlignment="Center">
                                    <Rectangle.Fill>
                                        <ImageBrush  ImageSource="{Binding ProfileImage}" Stretch="UniformToFill"/>
                                    </Rectangle.Fill>
                                </Rectangle>

Но здесь мне просто нужно пояснение, чтобы не создавать проблем с производительностью при загрузке эти изображения. Мне было предложено использовать byte [] для изображений вместо строки, чтобы избежать проблем с производительностью.

Кто-нибудь, пожалуйста, поясните мне, что будет лучшей привязкой для отображения изображений, чтобы уменьшить проблемы с производительностью.

Ответы [ 2 ]

1 голос
/ 04 августа 2020

Если вы можете получить данные изображения в массиве байтов, то вы можете напрямую привязать свойство Source элемента управления Image к этому свойству без необходимости в каком-либо преобразователе.

public class UserModel
{
    public Guid? Id { get; set; }
    public string Name { get; set; }
    public byte[] ProfileImage { get; set; }
}


<ListBox ItemsSource="{Binding UserModelCollection}">
    <ListBox.ItemTemplate>
        <DataTemplate DataType="{x:Type local:UserModel}">
            <Grid
                Height="32"
                Margin="4"
                Focusable="False">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="16" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>

                <Image
                    Grid.Column="0"
                    VerticalAlignment="Center"
                    Source="{Binding ProfileImage}"
                    Stretch="Uniform" />

                <TextBlock
                    Grid.Column="2"
                    VerticalAlignment="Center"
                    Text="{Binding Name}" />
            </Grid>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Чтобы избежать блокировки Поток пользовательского интерфейса, особенно если у вас большой список пользователей и вы хотите получать изображения только по запросу, когда этот элемент отображается, я предлагаю использовать асинхронное свойство.

/// <summary>
/// A read only property where the value is fetched once when required, in an async manner, and then cached for future use.
/// </summary>
/// <remarks>
/// in Xaml, bind to perAsyncPropertyInstance.Value
/// </remarks>
/// <typeparam name="T"></typeparam>
public class perAsyncProperty<T> : perViewModelBase where T : class
{
    private readonly Func<Task<T>> _fetchValue;

    public perAsyncProperty(Func<Task<T>> fetchValue)
    {
        _fetchValue = fetchValue;
    }

    private bool _fetchingValue;

    private T _value;

    /// <summary>
    /// The property's value - use this as the source of data binding.
    /// </summary>
    public T Value
    {
        get
        {
            if (_value != null || _fetchingValue)
            {
                return _value;
            }

            _fetchingValue = true;

            // can't use await inside a property getter, so use a continuation instead
            _fetchValue()
                .EvaluateFunctionWithTimeoutAsync(FetchValueTimeOut)
                .ContinueWith(FetchValueContinuation);

            // Local function to refresh Value once the data fetch task has completed
            async void FetchValueContinuation(Task<perAsyncFunctionResponse<T>> task)
            {
                var taskResult = await task.ConfigureAwait(false);

                if (taskResult.IsCompletedOk)
                {
                    Value = taskResult.Data;
                }
                else if (taskResult.IsTimedOut)
                {
                    OnTimeOutAction?.Invoke(taskResult);
                }
                else if (taskResult.IsError)
                {
                    OnErrorAction?.Invoke(taskResult);
                }

                _fetchingValue = false;
            }

            return _value;
        }
        private set => Set(nameof(Value), ref _value, value);
    }

    /// <summary>
    /// Timeout value for FetchValue invocation
    /// </summary>
    public TimeSpan FetchValueTimeOut { get; set; } = perTimeSpanHelper.Forever;

    /// <summary>
    /// Optional action to perform if FetchValue generates an error.
    /// </summary>
    public Action<perAsyncFunctionResponse<T>> OnErrorAction { get; set; }

    /// <summary>
    /// Optional action to perform if FetchValue times out.
    /// </summary>
    public Action<perAsyncFunctionResponse<T>> OnTimeOutAction { get; set; }

    /// <summary>
    /// Clear Value and force it to be re-fetched then next time it is read.
    /// </summary>
    public void ResetValue()
    {
        _fetchingValue = false;
        Value = null;
    }
}


public class perBytesFromFileAsyncProperty : perAsyncProperty<byte[]>
{
    public perBytesFromFileAsyncProperty(string filePath) : base(() => FetchData(filePath))
    {
    }

    private static Task<byte[]> FetchData(string filePath)
    {
        Debug.WriteLine("loading file - " + filePath);
        return perIOAsync.ReadAllBytesFromFileRawAsync(filePath);
    }
}


public class perBytesFromUrlAsyncProperty : perAsyncProperty<byte[]>
{
    public perBytesFromUrlAsyncProperty(string url): base(() => FetchData(url))
    {
    }

    private static Task<byte[]> FetchData(string url)
    {
        Debug.WriteLine("downloading Url - " + url);
        return perIOAsync.ReadAllBytesFromUrlRawAsync(url);
    }
}

Используйте соответствующий версия в зависимости от того, является ли ваша строка для источника изображения путем к файлу или веб-URL.

Теперь UserModel и привязка представления могут быть закодированы как

public class UserModel
{
    public UserModel (Guid id, string name, string imageFilePath)
    {
        Id = id;
        Name = name;
        ProfileImage = new perBytesFromFileAsyncProperty(imageFilePath);
    }

    public Guid? Id { get; set; }
    public string Name { get; set; }
    public perBytesFromFileAsyncProperty ProfileImage { get; set; }
}


<ListBox ItemsSource="{Binding UserModelCollection}">
    <ListBox.ItemTemplate>
        <DataTemplate DataType="{x:Type local:UserModel}">
            <Grid
                Height="32"
                Margin="4"
                Focusable="False">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="16" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>

                <Image
                    Grid.Column="0"
                    VerticalAlignment="Center"
                    Source="{Binding ProfileImage.Value}"
                    Stretch="Uniform" />

                <TextBlock
                    Grid.Column="2"
                    VerticalAlignment="Center"
                    Text="{Binding Name}" />
            </Grid>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Подробнее о моем сообщение в блоге .

1 голос
/ 04 августа 2020

Строка не может быть рисунком. Строка содержит путь (Uri) изображения. Привязка автоматически проверяет наличие изображения по указанному Uri, и, если оно есть, преобразует его в ImageSource и передаст его свойству.

Но массив byte [] напрямую содержит данные изображения. Привязка не может автоматически преобразовать этот массив в тип ImageSource. Следовательно, вам нужно получить массив byte [] самостоятельно, преобразовать его в один из типов ImageSource и только потом предоставить его в свойстве ProfileImage, откуда привязка сможет его забрать.

Первый вариант (со строкой) проще, но при больших размерах изображений, медленном доступе к ним, их большом количестве могут возникнуть проблемы с лагами GUI. Поскольку получение картинки по привязке будет происходить в основном потоке GUI (Диспетчере).

Во втором случае большая часть работы может выполняться асинхронно.

...