第二十章:异步和文件I/O.(二十一)

简介:

因此,MandelbrotViewModel类具有许多属性,但如果您不考虑用户界面,则可能与您定义的属性不同。 CurrentCenter属性是程序当前显示的图像中心的复数,CurrentMagnification也适用于该图像。 但TargetMagnification绑定到Stepper的当前设置,它将应用于下一个计算的图像。 RealOffset和ImaginaryOffset属性绑定到两个Slider元素,范围从0到1.从CurrentCenter,CurrentMagnification,RealOffset和ImaginaryOffset属性,ViewModel可以计算TargetCenter属性。 这是下一个计算图像的中心。 如您所见,该TargetCenter属性用于显示两个滑块下方的复数:

namespace MandelbrotXF
{
    class MandelbrotViewModel : ViewModelBase
    {
        // Set via constructor arguments.
        readonly double baseWidth;
        readonly double baseHeight;
        // Backing fields for properties.
        Complex currentCenter, targetCenter;
        int pixelWidth, pixelHeight;
        double currentMagnification, targetMagnification;
        int iterations;
        double realOffset, imaginaryOffset;
        bool isBusy;
        double progress;
        BitmapInfo bitmapInfo;
        public MandelbrotViewModel(double baseWidth, double baseHeight)
        {
            this.baseWidth = baseWidth;
            this.baseHeight = baseHeight;
            // Create MandelbrotModel object.
            MandelbrotModel model = new MandelbrotModel();
            // Progress reporter
            Progress<double> progressReporter = new Progress<double>((double progress) =>
            {
                Progress = progress;
            });
            CancellationTokenSource cancelTokenSource = null;
            // Define CalculateCommand and CancelCommand.
            CalculateCommand = new Command(
                execute: async () =>
                    {
                        // Disable this button and enable Cancel button.
                        IsBusy = true;
                        ((Command)CalculateCommand).ChangeCanExecute();
                        ((Command)CancelCommand).ChangeCanExecute();
                        // Create CancellationToken.
                        cancelTokenSource = new CancellationTokenSource();
                        CancellationToken cancelToken = cancelTokenSource.Token;
                        try
                        {
                            // Perform the calculation.
                            BitmapInfo = await model.CalculateAsync(TargetCenter, 
                                                            baseWidth / TargetMagnification, 
                                                            baseHeight / TargetMagnification,
                                                                PixelWidth, PixelHeight, 
                                                                Iterations,
                                                                progressReporter, 
                                                                cancelToken);
                            // Processing only for a successful completion.
                            CurrentCenter = TargetCenter;
                            CurrentMagnification = TargetMagnification;
                            RealOffset = 0.5;
                            ImaginaryOffset = 0.5;
                        }
                        catch (OperationCanceledException)
                        {
                            // Operation cancelled!
                        }
                        catch
                        {
                            // Another type of exception? This should not occur.
                        }
                        // Processing regardless of success or cancellation.
                        Progress = 0;
                        IsBusy = false;
                        // Disable Cancel button and enable this button.
                        ((Command)CalculateCommand).ChangeCanExecute();
                        ((Command)CancelCommand).ChangeCanExecute();
                    }, 
                canExecute: () =>
                    {
                        return !IsBusy;
                    });
            CancelCommand = new Command(
                execute: () =>
                    {
                        cancelTokenSource.Cancel();
                    },
                canExecute: () =>
                    {
                        return IsBusy;
                    });
         }
        public int PixelWidth
        {
            set { SetProperty(ref pixelWidth, value); }
            get { return pixelWidth; }
        }
        public int PixelHeight
        {
            set { SetProperty(ref pixelHeight, value); }
            get { return pixelHeight; }
        }
        public Complex CurrentCenter
        {
            set
            { 
                if (SetProperty(ref currentCenter, value))
                    CalculateTargetCenter();
            }
            get { return currentCenter; }
        }
        public Complex TargetCenter
        {
            private set { SetProperty(ref targetCenter, value); }
            get { return targetCenter; }
        }
        public double CurrentMagnification
        {
            set { SetProperty(ref currentMagnification, value); }
            get { return currentMagnification; }
        }
        public double TargetMagnification
        {
            set { SetProperty(ref targetMagnification, value); }
            get { return targetMagnification; }
        }
        public int Iterations
        {
            set { SetProperty(ref iterations, value); }
            get { return iterations; }
        }
        // These two properties range from 0 to 1.
        // They indicate a new center relative to the 
        // current width and height, which is the baseWidth
        // and baseHeight divided by CurrentMagnification.
        public double RealOffset
        {
            set
            {
                if (SetProperty(ref realOffset, value))
                    CalculateTargetCenter();
            }
            get { return realOffset; }
        }
        public double ImaginaryOffset
        {
            set
            {
                if (SetProperty(ref imaginaryOffset, value))
                   CalculateTargetCenter();
            }
            get { return imaginaryOffset; }
        }
        void CalculateTargetCenter()
        {
            double width = baseWidth / CurrentMagnification;
            double height = baseHeight / CurrentMagnification;
            TargetCenter = new Complex(CurrentCenter.Real + (RealOffset - 0.5) * width,
                                       CurrentCenter.Imaginary + (ImaginaryOffset - 0.5) *
                                       height);
        }
        public bool IsBusy 
        {
            private set { SetProperty(ref isBusy, value); }
            get { return isBusy; }
        }
        public double Progress
        {
            private set { SetProperty(ref progress, value); }
            get { return progress; }
        }
        public BitmapInfo BitmapInfo
        {
            private set { SetProperty(ref bitmapInfo, value); }
            get { return bitmapInfo; }
        }
        public ICommand CalculateCommand { private set; get; }
        public ICommand CancelCommand { private set; get; }
    }
}

MandelbrotViewModel还为Calculate和Cancel按钮,Progress属性和IsBusy属性定义了ICommand类型的两个属性。 正如您将看到的,IsBusy属性用于显示这两个按钮中的一个并隐藏另一个按钮,并在计算过程中禁用其余的用户界面。 这两个ICommand属性是在类的构造函数中使用lambda函数实现的。
XAML文件中的数据绑定到MandelbrotViewModel中的属性需要Xamarin.FormsBook.Toolkit库中的两个新绑定转换器。 第一个简单地否定了bool值:

namespace Xamarin.FormsBook.Toolkit
{
    public class BooleanNegationConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, 
                              object parameter, CultureInfo culture)
        {
            return !(bool)value;
        }
        public object ConvertBack(object value, Type targetType, 
                                  object parameter, CultureInfo culture)
        {
            return !(bool)value;
        }
    }
}

它与ViewModel的IsBusy属性一起使用。 当IsBusy为true时,需要将几个元素的IsEnabled属性和Go按钮的IsVisible属性设置为false。
两个Stepper元素实际上控制ViewModel中值的指数。 例如,Stepper值为8,对应于Iterations或TargetMagnification值256.该转换需要base-2对数转换器:

namespace Xamarin.FormsBook.Toolkit
{
    public class BaseTwoLogConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, 
                              object parameter, CultureInfo culture)
        {
            if (value is int)
            {
                return Math.Log((int)value) / Math.Log(2);
            }
            return Math.Log((double)value) / Math.Log(2);
        }
        public object ConvertBack(object value, Type targetType, 
                                  object parameter, CultureInfo culture)
        {
            double returnValue = Math.Pow(2, (double)value);
            if (targetType == typeof(int))
            {
                return (int) returnValue;
            }
            return returnValue;
        }
    }
}

这是XAML文件,绑定到ViewModel的Progress,RealOffset,ImaginaryOffset,TargetCenter,TargetMagnification,Iterations,IsBusy,CalculateCommand和CancelCommand属性:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:toolkit=
                 "clr-namespace:Xamarin.FormsBook.Toolkit;assembly=Xamarin.FormsBook.Toolkit"
             x:Class="MandelbrotXF.MandelbrotXFPage"
             SizeChanged="OnPageSizeChanged">
    <ContentPage.Padding>
        <OnPlatform x:TypeArguments="Thickness"
                    iOS="0, 20, 0, 0" />
    </ContentPage.Padding>
    <ContentPage.Resources>
        <ResourceDictionary>
            <toolkit:BooleanNegationConverter x:Key="negate" />
            <toolkit:BaseTwoLogConverter x:Key="base2log" />
        </ResourceDictionary>
    </ContentPage.Resources>
    <Grid x:Name="mainGrid">
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="0" />
        </Grid.ColumnDefinitions>
 
        <!-- Image for determining pixels per unit. -->
        <Image x:Name="testImage"
               Grid.Row="0" Grid.Column="0"
               Opacity="0"
               HorizontalOptions="Center"
                VerticalOptions="Center" />
        <!-- Image for Mandelbrot Set. -->
        <Image x:Name="image"
               Grid.Row="0" Grid.Column="0"
               HorizontalOptions="FillAndExpand"
               VerticalOptions="FillAndExpand"
               SizeChanged="OnImageSizeChanged" />

        <AbsoluteLayout x:Name="crossHairLayout"
                        Grid.Row="0" Grid.Column="0"
                        HorizontalOptions="Center"
                        VerticalOptions="Center"
                        SizeChanged="OnCrossHairLayoutSizeChanged">
            <AbsoluteLayout.Resources>
                <ResourceDictionary>
                    <Style TargetType="BoxView">
                        <Setter Property="Color" Value="White" />
                        <Setter Property="AbsoluteLayout.LayoutBounds" Value="0,0,0,0" />
                    </Style>
                </ResourceDictionary>
            </AbsoluteLayout.Resources>
 
            <BoxView x:Name="realCrossHair" />
            <BoxView x:Name="imagCrossHair" />
            <BoxView x:Name="topBox" />
            <BoxView x:Name="bottomBox" />
            <BoxView x:Name="leftBox" />
            <BoxView x:Name="rightBox" />
        </AbsoluteLayout>
        <StackLayout x:Name="controlPanelStack"
                     Grid.Row="1" Grid.Column="0"
                     Padding="10">
            <ProgressBar Progress="{Binding Progress}"
                         VerticalOptions="CenterAndExpand" />
            <StackLayout VerticalOptions="CenterAndExpand">
                <Slider Value="{Binding RealOffset, Mode=TwoWay}"
                        IsEnabled="{Binding IsBusy, Converter={StaticResource negate}}" />
                <Slider Value="{Binding ImaginaryOffset, Mode=TwoWay}"
                        IsEnabled="{Binding IsBusy, Converter={StaticResource negate}}" />
                <Label Text="{Binding TargetCenter, StringFormat='{0}'}"
                       FontSize="Small"
                       HorizontalTextAlignment="Center" />
            </StackLayout>
            <Grid VerticalOptions="CenterAndExpand">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>

                <!-- Magnification factor stepper and display. -->
                <Stepper x:Name="magnificationStepper"
                         Grid.Row="0" Grid.Column="0"
                         Value="{Binding TargetMagnification, 
                         Converter={StaticResource base2log}}"
                         IsEnabled="{Binding IsBusy, Converter={StaticResource negate}}"
                         VerticalOptions="Center" />
                <StackLayout Grid.Row="0" Grid.Column="1"
                             Orientation="Horizontal"
                             Spacing="0"
                             VerticalOptions="Start">
                    <Label Text="zoom 2"
                           FontSize="Medium" />
                    <Label Text="{Binding Source={x:Reference magnificationStepper},
                           Path=Value,
                           StringFormat='{0}'}"
                           FontSize="Micro" />
                </StackLayout>
                <!-- Iterations factor stepper and display. -->
                <Stepper x:Name="iterationsStepper"
                         Grid.Row="1" Grid.Column="0"
                         Value="{Binding Iterations, Converter={StaticResource base2log}}"
                         IsEnabled="{Binding IsBusy, Converter={StaticResource negate}}"
                         VerticalOptions="Center" />
                <StackLayout Grid.Row="1" Grid.Column="1"
                             Orientation="Horizontal"
                             Spacing="0"
                             VerticalOptions="End">
                    <Label Text="loop 2"
                           FontSize="Medium" />
                    <Label Text="{Binding Source={x:Reference iterationsStepper},
                           Path=Value,
                           StringFormat='{0}'}"
                           FontSize="Micro" />
                </StackLayout>
                <!-- Go / Cancel buttons. -->
                <Grid Grid.Row="0" Grid.Column="1" Grid.RowSpan="2"
                      HorizontalOptions="End"
                      VerticalOptions="Center">
 
                    <Button Text="Go"
                            Command="{Binding CalculateCommand}"
                            IsVisible="{Binding IsBusy, Converter={StaticResource negate}}" />
                    <Button Text="Cancel"
                            Command="{Binding CancelCommand}"
                            IsVisible="{Binding IsBusy}" />
                </Grid>
            </Grid>
        </StackLayout>
    </Grid>
</ContentPage>

此XAML文件仅安装三个事件处理程序,它们都是SizeChanged处理程序。

目录
相关文章
|
存储 JavaScript Android开发
|
Web App开发 Android开发
第二十章:异步和文件I/O.(二十三)
回到网上在本章之前,本书中唯一的异步代码涉及使用可移植类库WebRequest中唯一可用于此目的的合理类进行Web访问。 WebRequest类使用称为异步编程模型或APM的旧异步协议。 APM涉及两种方法,在WebRequest的情况下,这些方法称为BeginGetResponse和EndGetResponse。
733 0