使用NotifyPropertyChange异步设置Image.Source

时间:2017-01-19 16:19:22

标签: c# wpf asynchronous inotifypropertychanged

我有一个通过一些滑块修改的图像,其值被发送到一个程序(重新)生成一个BitmapSource的方法。

目前事情很麻烦,因为滑块“运动滞后。我认为这是由于Update方法的阻塞性质。

我试图让事情更加异步"但显然没有成功,因为滑块移动仍然是滞后的。

我在下面发布我的代码,问题是:我做错了什么,以及在WPF中异步更新程序生成的图像的正确方法是什么?

需要注意的一点是,IDE抱怨没有等待Update()次呼叫,并且#34;在呼叫完成之前继续执行",但我不确定这意味着什么,或者我该怎么办呢。

MainWindow.xaml

<Window x:Class="Miotec.FranjasSenoidais.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:Miotec.FranjasSenoidais"
        xmlns:toolkit="http://schemas.xceed.com/wpf/xaml/toolkit"
        mc:Ignorable="d"
        Title="MainWindow">
    <Grid Margin="10">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>

        <Grid x:Name="Imagem" Grid.Row="0" Grid.Column="0">
            <Image x:Name="franjas" Width="800" Height="600"
                   Source="{Binding ImageSource}"/>
        </Grid>

        <Grid x:Name="SliderLateral" Grid.Row="0" Grid.Column="1">
            <toolkit:RangeSlider Orientation="Vertical"
                                 Maximum="1" Minimum="0" Step="0.01"
                                 HigherValue="{Binding Maximo}" 
                                 LowerValue="{Binding Minimo}"/>
        </Grid>

        <Grid x:Name="SliderEspessuraFranja" Grid.Row="1" Grid.Column="0">
            <Slider Orientation="Horizontal" Value="{Binding Espessura}"
                    Minimum="4" Maximum="16"/>
        </Grid>
    </Grid>
</Window>

MainWindow.xaml.cs(为简洁起见,代码中的DataContext)

using System;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media.Imaging;

namespace Miotec.FranjasSenoidais
{

    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public double Maximo
        {
            get { return _maximo; }
            set
            {
                _maximo = value;
                RaisePropertyChanged("Maximo");
                Update();
            }
        }

        double _maximo = 1;

        public double Minimo
        {
            get { return _minimo; }
            set
            {
                _minimo = value;
                RaisePropertyChanged("Minimo");
                Update();
            }
        }
        double _minimo = 0;

        public int Espessura
        {
            get { return _espessura; }
            set
            {
                _espessura = value;
                RaisePropertyChanged("Espessura");
                Update();
            }
        }
        int _espessura = 10;


        public BitmapSource ImageSource { get { return _imageSource; } }
        BitmapSource _imageSource;


        private async Task Update()
        {
            await Dispatcher.BeginInvoke(new Action(() =>
            {
                // somewhat lengthy operation
                _imageSource = FranjasSenoidais.Criar((int)franjas.Width,
                                       (int)franjas.Height,
                                       Maximo, Minimo,
                                       Espessura);

            }));

            RaisePropertyChanged("ImageSource");
        }

        public MainWindow()
        {
            InitializeComponent();

            DataContext = this;
        }




        public event PropertyChangedEventHandler PropertyChanged;
        void RaisePropertyChanged(string name)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
    }
}

FranjasSenoidais.cs

public static class FranjasSenoidais
{
    public static BitmapSource Criar(int largura,
                                     int altura,
                                     double maximo,
                                     double minimo,
                                     int espessura)
    {
        if (largura < 2 || altura < 2)
            return null;

        var targetBitmap = new RenderTargetBitmap(largura, altura,
                                                96, 96,
                                                PixelFormats.Pbgra32);

        var visual = new DrawingVisual();

        using (var dc = visual.RenderOpen())
        {
            double xcenter = largura * 0.5;
            double ycenter = altura * 0.5;

            dc.DrawEllipse(Brushes.Black, null, new Point(xcenter, ycenter), espessura, espessura);
        }

        targetBitmap.Render(visual);
        targetBitmap.Freeze();

        return targetBitmap;
    }
}

1 个答案:

答案 0 :(得分:1)

我认为你只需要做一些改变。 @酷蓝评论是对的。只需将调度移动到INotify实现中,然后在任务上排队cpu绑定进程。请确保从该线程更改属性,请参阅以下内容:

    private void Update() {
        Task.Run(new Action(() => {
            // somewhat lengthy operation
            _imageSource = FranjasSenoidais.Criar((int)franjas.Width,
                                   (int)franjas.Height,
                                   Maximo, Minimo,
                                   Espessura);
            RaisePropertyChanged("ImageSource");
        }));
    }

    public event PropertyChangedEventHandler PropertyChanged;
    void RaisePropertyChanged(string name) {
        Application.Current.Dispatcher.Invoke(() => {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }, System.Windows.Threading.DispatcherPriority.Background);
    }