티스토리 뷰
Windows 8 RP 버전부터 적용된 BackgroundDownloader를 이용한 다운로드 셈플이다. 기존에 sample 프로젝트를 제공하고 있는데, 실제로 사용하기에는 좀 부족해서 몇가지 기능을 수정, 보완을 했다. 맨 아래 소스를 참고하기 바란다.
1. 참고 포스트
BackgroundDownloader class
Background Transfer sample
http://code.msdn.microsoft.com/windowsapps/Background-Transfer-Sample-d7833f61
A StringFormat converter for Windows 8 Metro
http://blogs.u2u.be/diederik/post/2012/03/19/A-StringFormat-converter-for-Windows-8-Metro.aspx
2. 결과 화면
시작화면
2개의 다운로드를 동시에 진행
첫번째 아이템 선택 후 Pause를 눌러서 일시 정지
두번째 아이템 선택 후 일시 정지 후 첫번째 아이템 Resume 버튼을 눌러서 다시 실행
두번째 아이템은 취소
다운로드된 아이템은 앱의 템프 폴더에 저장
3. MainPage.xaml
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:BackgroundUpDownSample"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Common="using:BackgroundUpDownSample.Common"
x:Class="BackgroundUpDownSample.MainPage"
IsTabStop="false"
mc:Ignorable="d">
<Page.Resources>
<Common:StringFormatConverter x:Key="StringFormatConverter"/>
<DataTemplate x:Key="DataTemplate1">
<Grid d:DesignWidth="322" d:DesignHeight="53">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock TextWrapping="Wrap" Text="{Binding ResultFileName, Mode=OneWay}"/>
<ProgressBar Grid.Row="1" Value="{Binding DownloadProgressPercent}" HorizontalContentAlignment="Stretch"/>
<StackPanel Grid.Row="2" Orientation="Horizontal">
<TextBlock Text="{Binding BytesReceived, Converter={StaticResource StringFormatConverter}, ConverterParameter='{}{0:N0}'}"/>
<TextBlock Text="/"/>
<TextBlock Text="{Binding TotalBytesToReceive, Converter={StaticResource StringFormatConverter}, ConverterParameter='{}{0:N0}'}"/>
<TextBlock Text="KB"/>
<TextBlock Text="{Binding State}" Margin="20,0,0,0" />
</StackPanel>
</Grid>
</DataTemplate>
</Page.Resources>
<d:DataContext>
<local:BackgroundViewModel/>
</d:DataContext>
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5*"/>
<ColumnDefinition Width="0.5*"/>
</Grid.ColumnDefinitions>
<StackPanel Margin="30,10,10,30">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Download Uri : " VerticalAlignment="Center" Width="100" />
<TextBox Margin="0,2,0,2" VerticalAlignment="Center" MinWidth="500" TextWrapping="NoWrap" Text="{Binding UriString, Mode=TwoWay}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Local File Name : " VerticalAlignment="Center" Width="100"/>
<TextBox Margin="0,0,0,0" VerticalAlignment="Center" MinWidth="500" TextWrapping="NoWrap" Text="{Binding FileName, Mode=TwoWay}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,10,0,0">
<Button Content="Start" Width="80" Command="{Binding StartCommand, Mode=OneWay}"/>
</StackPanel>
<ListBox x:Name="lbActiveDownloads" ItemsSource="{Binding ActiveDownloads}" Margin="0,10,0,0" ItemTemplate="{StaticResource DataTemplate1}"/>
<StackPanel Orientation="Horizontal" Margin="0,10,0,0">
<Button Content="Pause" Width="90" Command="{Binding PauseCommand, Mode=OneWay}"/>
<Button Content="Resume" Width="90" Command="{Binding ResumeCommand, Mode=OneWay}"/>
<Button Content="Cancel" Width="90" Command="{Binding CancelCommand, Mode=OneWay}"/>
</StackPanel>
</StackPanel>
</Grid>
</Page>
4. MainPage.xaml.cs
public sealed partial class MainPage : Page
{
//뷰 모델
public BackgroundViewModel ViewModel
{
get { return this.DataContext as BackgroundViewModel; }
set
{
this.DataContext = value;
}
}
public MainPage()
{
this.InitializeComponent();
//뷰모델 생성
ViewModel = new BackgroundViewModel();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
//이벤트 연결
lbActiveDownloads.SelectionChanged += ViewModel.SelectionChanged;
//백그라운드 다운로드 아이템 복구
ViewModel.DiscoverActiveDownloadsAsync();
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
lbActiveDownloads.SelectionChanged -= ViewModel.SelectionChanged;
}
}
5. BackgroundViewModel.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;
using BackgroundUpDownSample.Common;
using Windows.Networking.BackgroundTransfer;
using Windows.Storage;
using Windows.UI.Xaml.Controls;
namespace BackgroundUpDownSample
{
public class DownloadOprationItem : BindableBase
{
/// <summary>
/// 취소 토큰
/// </summary>
public CancellationTokenSource CancelToken { get; set; }
/// <summary>
/// 생성자
/// </summary>
public DownloadOprationItem()
{
CancelToken = new CancellationTokenSource();
}
/// <summary>
/// 취소 메소드
/// </summary>
public void Cancel()
{
CancelToken.Cancel();
CancelToken.Dispose();
}
private DownloadOperation downloadOp;
/// <summary>
/// 다운로드 오퍼레이션
/// </summary>
public DownloadOperation DownloadOp
{
get { return downloadOp; }
set
{
downloadOp = value;
OnPropertyChanged();
}
}
/// <summary>
/// 다운로드 Uri
/// </summary>
public string RequestUri
{
get { return DownloadOp.RequestedUri.ToString(); }
}
/// <summary>
/// 로컬 파일명
/// </summary>
public string ResultFileName
{
get { return DownloadOp.ResultFile.Name; }
}
private double downloadProgressPercent;
/// <summary>
/// 다운로드 퍼센트
/// </summary>
public double DownloadProgressPercent
{
get { return downloadProgressPercent; }
set
{
downloadProgressPercent = value;
OnPropertyChanged();
}
}
private ulong totalBytesToReceive;
/// <summary>
/// 총크기
/// </summary>
public ulong TotalBytesToReceive
{
get { return totalBytesToReceive; }
set
{
totalBytesToReceive = value;
OnPropertyChanged();
}
}
private ulong bytesReceived;
/// <summary>
/// 수신크기
/// </summary>
public ulong BytesReceived
{
get { return bytesReceived; }
set
{
bytesReceived = value;
OnPropertyChanged();
}
}
private string state;
/// <summary>
/// 상태 메시지
/// </summary>
public string State
{
get { return state; }
set
{
state = value;
OnPropertyChanged();
}
}
}
public class BackgroundViewModel : BindableBase
{
private ObservableCollection<DownloadOprationItem> activeDownloads;
/// <summary>
/// 현재 백그라운드 다운로드 오퍼레이션들
/// </summary>
public ObservableCollection<DownloadOprationItem> ActiveDownloads
{
get { return activeDownloads; }
set
{
activeDownloads = value;
OnPropertyChanged();
}
}
private DownloadOprationItem currentDownloadOperation;
/// <summary>
/// 현재 다운로드 오퍼레이션
/// </summary>
public DownloadOprationItem CurrentDownloadOperation
{
get { return currentDownloadOperation; }
set
{
currentDownloadOperation = value;
OnPropertyChanged();
}
}
private string uriString;
/// <summary>
/// 다운로드 Uri
/// </summary>
public string UriString
{
get { return uriString; }
set
{
uriString = value;
OnPropertyChanged();
}
}
private string fileName;
/// <summary>
/// 파일 이름
/// </summary>
public string FileName
{
get { return fileName; }
set
{
fileName = value;
OnPropertyChanged();
}
}
/// <summary>
/// 생성자
/// </summary>
public BackgroundViewModel()
{
ActiveDownloads = new ObservableCollection<DownloadOprationItem>();
}
/// <summary>
/// 백그라운드 다운로드 복구
/// </summary>
/// <returns></returns>
public async Task DiscoverActiveDownloadsAsync()
{
try
{
//백그라운드 다운로더에서 현재 다운로드 정보를 반환 받아
IReadOnlyList<DownloadOperation> downloads = await BackgroundDownloader.GetCurrentDownloadsAsync();
//다운로드 카운트가 있으면
if (downloads.Count > 0)
{
//테스크 리스트를 하나 만들고
List<Task> tasks = new List<Task>();
foreach (DownloadOperation download in downloads)
{
//다운로드를 테스크에 추가하고
tasks.Add(HandleDownloadAsync(download, false));
}
//모든 테스크가 완료되면 리턴 한다.
await Task.WhenAll(tasks);
}
}
catch (Exception ex)
{
}
}
// 다운로드 시작
public async void StartDownload(Uri source, string destination)
{
try
{
if (destination == null || destination == "")
{
//파일명이 없으면 원본 파일 명을 사용한다.
var lastSegment = source.Segments.LastOrDefault();
destination = Uri.UnescapeDataString(lastSegment);
}
//템프 폴더에 다운로드
var path = Windows.Storage.ApplicationData.Current.TemporaryFolder.Path;
StorageFile destTempFile = await Windows.Storage.ApplicationData.Current.TemporaryFolder.CreateFileAsync(destination, CreationCollisionOption.GenerateUniqueName);
BackgroundDownloader downloader = new BackgroundDownloader();
DownloadOperation download = downloader.CreateDownload(source, destTempFile);
// Attach progress and completion handlers.
await HandleDownloadAsync(download, true);
}
catch (Exception ex)
{
}
}
/// <summary>
/// 다운로드 관리
/// </summary>
private async Task HandleDownloadAsync(DownloadOperation download, bool start)
{
var downloadItem = new DownloadOprationItem { DownloadOp = download };
try
{
// 액티브 다운로드 리스트에 추가
ActiveDownloads.Add(downloadItem);
// 프로그래스 콜백 하나 만들고
Progress<DownloadOperation> progressCallback = new Progress<DownloadOperation>(DownloadProgress);
if (start)
{
//CurrentDownloadOperation = downloadItem;
downloadItem.State = downloadItem.DownloadOp.Progress.Status.ToString();
// true이면 바로 시작
await download.StartAsync().AsTask(downloadItem.CancelToken.Token, progressCallback);
}
else
{
downloadItem.State = downloadItem.DownloadOp.Progress.Status.ToString();
// false이면 캔슬토큰과 프로그래스만 붙여 놓고
await download.AttachAsync().AsTask(downloadItem.CancelToken.Token, progressCallback);
}
ResponseInformation response = download.GetResponseInformation();
}
catch (TaskCanceledException)
{
downloadItem.State = downloadItem.DownloadOp.Progress.Status.ToString();
}
catch (Exception ex)
{
downloadItem.State = "error";
}
finally
{
//ActiveDownloads.Remove(downloadItem);
downloadItem.State = downloadItem.DownloadOp.Progress.Status.ToString();
}
}
// 다운로드 프로그래스 처리
private void DownloadProgress(DownloadOperation download)
{
double percent = 100;
//선택된 다운로드 아이템을 찾고
var di = ActiveDownloads.FirstOrDefault(p => p.DownloadOp.Guid == download.Guid);
if (download.Progress.TotalBytesToReceive > 0)
{
percent = download.Progress.BytesReceived * 100 / download.Progress.TotalBytesToReceive;
if (di != null)
{
//다운로드 아이템의 상태, 다운로드 퍼센트, 수신 사이즈, 총 사이즈 입력
di.State = download.Progress.Status.ToString();
di.DownloadProgressPercent = percent;
di.BytesReceived = download.Progress.BytesReceived / 1024;
di.TotalBytesToReceive = download.Progress.TotalBytesToReceive / 1024;
}
}
if (download.Progress.HasRestarted && di != null)
{
di.State = download.Progress.Status.ToString();
}
if (download.Progress.HasResponseChanged && di != null)
{
di.State = download.Progress.Status.ToString();
//di.State = "response updated";
// We've received new response headers from the server.
//MarshalLog(" - Response updated; Header count: " + download.GetResponseInformation().Headers.Count);
// If you want to stream the response data this is a good time to start.
// download.GetResultStreamAt(0);
}
}
private ICommand startCommand;
/// <summary>
/// 다운로드 스타트
/// </summary>
public ICommand StartCommand
{
get
{
if (startCommand == null)
{
startCommand = new DelegateCommand(
_ =>
{
StartDownload(new Uri(UriString), FileName);
});
}
return startCommand;
}
}
/// <summary>
/// 셀렉트 체인지 이벤트 처리
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void SelectionChanged(object sender, SelectionChangedEventArgs e)
{
CurrentDownloadOperation = e.AddedItems.FirstOrDefault() as DownloadOprationItem;
CheckCanExecuteChanged();
}
/// <summary>
/// 커맨드 사용가능 여부 체크
/// </summary>
private void CheckCanExecuteChanged()
{
(PauseCommand as DelegateCommand).RaiseCanExecuteChanged();
(ResumeCommand as DelegateCommand).RaiseCanExecuteChanged();
(CancelCommand as DelegateCommand).RaiseCanExecuteChanged();
}
private ICommand pauseCommand;
/// <summary>
/// 일시 정지
/// </summary>
public ICommand PauseCommand
{
get
{
if (pauseCommand == null)
{
pauseCommand = new DelegateCommand(
_ =>
{
CurrentDownloadOperation.DownloadOp.Pause();
CheckCanExecuteChanged();
},
_ => IsCurrentDownloadOperation());
}
return pauseCommand;
}
}
private ICommand resumeCommand;
/// <summary>
/// 다시 시작
/// </summary>
public ICommand ResumeCommand
{
get
{
if (resumeCommand == null)
{
resumeCommand = new DelegateCommand(
_ =>
{
CurrentDownloadOperation.DownloadOp.Resume();
CheckCanExecuteChanged();
},
_ => IsCurrentDownloadOperation());
}
return resumeCommand;
}
}
private ICommand cancelCommand;
/// <summary>
/// 취소
/// </summary>
public ICommand CancelCommand
{
get
{
if (cancelCommand == null)
{
cancelCommand = new DelegateCommand(
_ =>
{
CurrentDownloadOperation.Cancel();
CheckCanExecuteChanged();
CurrentDownloadOperation.DownloadProgressPercent = 0;
},
_ => IsCurrentDownloadOperation());
}
return cancelCommand;
}
}
/// <summary>
/// 버튼 사용 가능여부 반환 함수
/// </summary>
/// <returns></returns>
private bool IsCurrentDownloadOperation()
{
bool returnValue = false;
if (CurrentDownloadOperation == null)
{
returnValue = false;
}
else
{
returnValue = true;
}
return returnValue;
}
}
}
6. 추가적인 설명을 하면 좋을 것 같기는 한데..
소스의 주석을 참고하여, 분석하고 생각해서 본인의 것으로 만들어 보기를 바란다.
7. 소스
'Previous Platforms > Samples' 카테고리의 다른 글
ISupportIncrementalLoading Sample (1) | 2012.10.07 |
---|---|
Create multi language support app (Multilingual App Toolkit) - Windows 8 RP (2) | 2012.08.02 |
Using SQLite in Windows 8 RP Metro style app Part 2 (11) | 2012.07.14 |
Using SQLite in Windows 8 RP Metro style app Part 1 (6) | 2012.07.09 |
Grid App analyze Part 2 - Windows 8 Release Preview (0) | 2012.06.09 |
- Total
- Today
- Yesterday
- Build 2016
- #uwp
- Windows 10
- XAML
- visual studio 2019
- .net
- kiosk
- #prism
- MVVM
- Cross-platform
- uno-platform
- UWP
- WPF
- Visual Studio 2022
- Behavior
- #Windows Template Studio
- #MVVM
- LINQ
- Microsoft
- ComboBox
- dotNETconf
- Always Encrypted
- .net 5.0
- IOT
- Bot Framework
- PRISM
- ef core
- C#
- uno platform
- windows 11
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |