Verfasst von: bletra | 5. Oktober 2012

Multithreading in WPF (TPL, MVVM): Fertige Demo-Applikation (Teil 5 von 5)

In diesem letzten Artikel der Reihe werde ich die besprochenen Codefragmente zu einer kleinen Demo-Applikation zusammenführen. Ich beginne mit dem ViewModel, das BaseViewModel, wie ich es in „WPF: Von Spaghetticode zu MVVM – Teil 6 von 9“ besprochen habe, erweitert. Das ViewModel verwendet die Klasse ExplicitlyObservableList, wie in Artikel „Multithreading in WPF (TPL, MVVM): Bindbare Collections (Teil 4 von 5)“ eingeführt.

using System;
using UBT.NetLecture.WPF.MVVMBase;
using System.Windows.Input;
using System.Threading.Tasks;

namespace DemoMVVMAsync
{
  public class ViewModel : BaseViewModel
  {
    private bool isRunning = false;
    private string folder = @"d:\tmp\images"; //eigentlich auch als Property für GUI
    private DelegateCommand cancelCmd;
    private ICommand doManyTasks;
    private ExplicitlyObservableList<ImageInformation> items;
    System.Threading.CancellationTokenSource cancelTokenSource;
    public ViewModel()
    {
      items = new ExplicitlyObservableList<ImageInformation>();
      doManyTasks = new DelegateCommand((param) => DoManyTasks(), (param) => !IsRunning);
      cancelCmd = new DelegateCommand((param) => CancelWork(), (param) => IsRunning);
    }
    public bool IsRunning
    {
      get { return isRunning; }
      set { isRunning = value; NotifyPropertyChanged("IsRunning"); }
    }
    public ICommand GetImagesCommand
    {
      get { return doManyTasks; }
    }
    public ICommand CancelCommand
    {
      get { return cancelCmd; }
    }
    public ExplicitlyObservableList<ImageInformation> Images
    {
      get { return items; }
    }
    private void CancelWork()
    {
      if (cancelTokenSource != null)
        cancelTokenSource.Cancel();
    }
    private void DoManyTasks()
    {
      //UI-Thread
      cancelTokenSource = new System.Threading.CancellationTokenSource();
      System.Threading.CancellationToken cancelToken = cancelTokenSource.Token;
      IsRunning = true;
      items.Clear();
      var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
      //start Hintergrund-Thread
      Task.Factory.StartNew(() =>
      {
        string[] imagePaths = System.IO.Directory.GetFiles(folder, "*.jpg");
        for (int i=0; i< imagePaths.Length; ++i)
        {
          if (cancelTokenSource.Token.IsCancellationRequested)
            return;
          Task.Factory.StartNew(() => { items.Add(new ImageInformation(imagePaths[i])); }, System.Threading.CancellationToken.None, TaskCreationOptions.None, uiScheduler);
          if (i % 5 == 0)//nicht auf Basis von images.Count, da diese eben im ui-thread hinzugefügt werden und nicht hier
          {
            Task.Factory.StartNew(() => { items.Notify(); }, System.Threading.CancellationToken.None, TaskCreationOptions.None, uiScheduler);
            System.Threading.Thread.Sleep(500);//GUI etwas Zeit lassen, sich anzuzeige
          }
        };
      }, cancelTokenSource.Token).ContinueWith(
         (task) => {
           items.Notify(); //letze Änderungen mitteilen
           IsRunning = false;
         }, uiScheduler //in UI-Thread
      );
    }
  }
}

Die Oberfläche ist relativ einfach gehalten:

<Window x:Class="DemoMVVMAsync.MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="MainWindow"
  Width="200"
>
<Window.Resources>
  <DataTemplate x:Key="ImageInformationTemplate">
    <Border BorderBrush="DarkBlue" BorderThickness="2" CornerRadius="3" Margin="5">
      <StackPanel Orientation="Vertical" Margin="3">
        <Image Source="{Binding PreviewImage}" />
        <TextBlock Text="{Binding Path=Title}" />
      </StackPanel>
    </Border>
  </DataTemplate>
</Window.Resources>
<DockPanel LastChildFill="True" >
  <StackPanel Width="150" DockPanel.Dock="Top" Orientation="Vertical" HorizontalAlignment="Left">
    <Button Content="start" Command="{Binding Path=GetImagesCommand}" Name="btnStart" IsDefault="True"/>
    <Button Content="cancel" Command="{Binding Path=CancelCommand}" Name="btnCancel"  IsCancel="True" />
  </StackPanel>
  <ListBox DockPanel.Dock="Bottom" ScrollViewer.HorizontalScrollBarVisibility="Disabled" Name="lbImages" Background="Transparent" IsEnabled="True"  BorderThickness="1" ItemTemplate="{StaticResource ImageInformationTemplate}" ItemsSource="{Binding Path=Images, Mode=OneWay}" />
</DockPanel>
</Window>

Im Code-Behind-Teil passiert wie gehabt nicht viel:

namespace DemoMVVMAsync
{
  public partial class MainWindow : Window
  {
    public MainWindow()
    {
      InitializeComponent();
      ViewModel vm = new ViewModel();
      this.DataContext = vm;
    }
  }
}
Advertisements

Responses

  1. […] Collection, WPF « Multithreading in WPF (TPL, MVVM): Cancel (Teil 3 von 5) Multithreading in WPF (TPL, MVVM): Fertige Demo-Applikation (Teil 5 von 5) […]

  2. […] 5: Fertige Demo-Applikation Bewerten:Gefällt mir:Gefällt mirSei der Erste dem dies […]

  3. […] Multithreading in WPF (TPL, MVVM): Fertige Demo-Applikation (Teil 5 von 5) […]

  4. […] 5: Fertige Demo-Applikation Bewerten:Gefällt mir:Gefällt mirSei der Erste dem dies […]


Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s

Kategorien

%d Bloggern gefällt das: