簡體   English   中英

將異步PointCollection更改傳播到UI

[英]Propagate async PointCollection change to UI

我無法將異步方法的結果傳播到UI。

XAML

<Window x:Class="COVMin.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:COVMin"        
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:MainViewModel/>
    </Window.DataContext>
    <Grid Margin="0,0,0,0">
        <Border BorderThickness="1" BorderBrush="Black" Background="White" Margin="4" VerticalAlignment="Top" Height="170">
        <Polygon Points="{Binding Points}" Stretch="Fill" Fill="Black" Opacity="0.8" />
    </Border>
    <Button x:Name="button" Content="Button" HorizontalAlignment="Left" Height="24" Marin="0,0,0,10" VerticalAlignment="Bottom" Width="75" Command="{Binding DrawPointsCommand}"/>
    </Grid>

視圖模型

class MainViewModel : ViewModelBase
{        
    private PointCollection points { get; set; }

    public PointCollection Points
    {
        get { return this.points; }
        set
        {                
            this.points = value;
            OnPropertyChanged("Points");
        }
    }

    public ICommand DrawPointsCommand { get; private set; }

    /// <summary>
    /// Simplified, in real it´s long time operation causing UI to freeze.
    /// </summary>        
    private Task<PointCollection> ConvertToPointCollection()
    {
        return Task.Run<PointCollection>(() =>
        {
            PointCollection points = new PointCollection();
            points.Add(new System.Windows.Point(0, 6236832));                
            points.Add(new System.Windows.Point(255, 6236832));

            return points;
        });
    }

    /// <summary>
    /// 
    /// </summary>
    private async Task<PointCollection> Process()
    {            
        this.Points = await ConvertToPointCollection();
        return this.Points;
    }

    /// <summary>
    /// Method calling long-time operation bound to button as a Command.
    /// </summary>
    private async void GetValues()
    {
        this.Points = await Process();            
    }

    /// <summary>
    /// Constructor.
    /// </summary>
    public MainViewModel()
    {
        this.DrawPointsCommand = new DelegateCommand(GetValues);
    }
}

ViewModelBase

    /// <summary>
    /// Base class for PropertyChanged event handling.
    /// </summary>        
    class ViewModelBase : INotifyPropertyChanged
    {  
        public event PropertyChangedEventHandler PropertyChanged;

        public void OnPropertyChanged(string name)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(name));
            }
        }
    }

DelegateCommand類

public class DelegateCommand : ICommand
{        
    private readonly Action _action;

    public DelegateCommand(Action action)
    {
        _action = action;
    }

    public void Execute(object parameter)
    {
        _action();
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public event EventHandler CanExecuteChanged;        
}

問題出在OnPropertyChange上,這導致System.ArgumentException告訴我必須在與DependencyObject相同的線程上創建DependencySource。 我花了相當多的時間嘗試調度員和什么不是但仍然沒有好處。

主要問題是PointCollection是一個DependencyObject ,因此由創建它的線程擁有。 通常,您不能在其他線程中使用此類對象。 使用Dispatcher (在代碼示例中顯式或隱式地await )在這里沒有用,因為它不是擁有該對象的UI線程。 實際上,它試圖在導致問題的UI線程上使用該對象。

對於也是Freezable對象的DependencyObjects來說,對於“不跨線程共享”規則有一個重要的例外,就像PointCollection 如果在其他線程嘗試訪問該對象之前將其凍結在擁有的線程中,則可以安全地在其他線程中使用該對象。

因此,您可以將ConvertToPointCollection()方法更改為如下所示:

private Task<PointCollection> ConvertToPointCollection()
{
    return Task.Run<PointCollection>(() =>
    {
        PointCollection points = new PointCollection();
        points.Add(new System.Windows.Point(0, 6236832));                
        points.Add(new System.Windows.Point(255, 6236832));

        points.Freeze();

        return points;
    });
}

當然,這也將防止以后修改對象。 如果您需要能夠修改集合,那么您將不得不采取不同的方法。 例如,在UI線程中創建PointCollection ,然后使用中間類型(如List<Point> )將新點從背景線程傳遞到UI線程,然后UI線程可在其中將這些點復制到PointCollection

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM