Skip to content

Instantly share code, notes, and snippets.

@Nia-TN1012
Last active April 13, 2016 21:24
Show Gist options
  • Save Nia-TN1012/0265cafca6dc50a211a3 to your computer and use it in GitHub Desktop.
Save Nia-TN1012/0265cafca6dc50a211a3 to your computer and use it in GitHub Desktop.
C# 6.0 / WPFでRSSフィードを取得し、表示するプログラムです。(For Qiita : [C# / WPF] 最新のC# 6.0でMVVMパターンを実装する)

このGistはQiitaの記事「最新のC# 6.0でMVVMパターンを実装する」( http://qiita.com/nia_tn1012/items/de5c8f83f9a638f6e44e )で扱っているプログラムです。

◆ ファイルの内容

  • RSSModel.cs     : Model

  • RSSViewModel.cs   : ViewModel

  • MainWindow.xaml   : View

  • MainWindow.xaml.cs  : View(分離コード)

  • Readme.md : このファイル

<Window x:Class="RSSMVVM_CS6.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:RSSMVVM_CS6"
Title="RSS Reader ( C# 6.0版 )" Height="480" Width="640">
<Window.DataContext>
<local:RSSViewModel x:Name="rssViewModel"
Url="http://chronoir.net/feed/"
GetRSSCompleted="rssViewModel_GetRSSCompleted"/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- RSSフィードの配信元情報を表示します -->
<StackPanel Grid.Row="0" Background="Black">
<TextBlock Text="{Binding Title, TargetNullValue=No RSS Feed}"
FontSize="14" Foreground="Lime" Padding="10, 5"/>
<TextBlock Text="{Binding Description}"
Foreground="White" Padding="10, 5"/>
<TextBlock Text="{Binding LastUpdatedTime, StringFormat=最終更新日 : yyyy年MM月dd日 HH:mm:dd, ConverterCulture=ja-JP}"
Foreground="Yellow" Padding="10, 5"/>
</StackPanel>
<!-- RSSフィードのコンテンツを表示します -->
<ListBox x:Name="RSSListBox"
Grid.Row="1"
ItemsSource="{Binding Items}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
Background="Black"
MouseDoubleClick="RSSListBox_MouseDoubleClick">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Title}" FontSize="14"
TextWrapping="Wrap" Foreground="Lime" Padding="5,3,5,1"/>
<TextBlock Text="{Binding PubDate, StringFormat=投稿日 : yyyy年MM月dd日 HH:mm:dd, ConverterCulture=ja-JP}"
TextWrapping="Wrap" Foreground="Yellow" Padding="5,1,5,2"/>
<TextBlock Text="{Binding Summary}"
TextWrapping="Wrap" Foreground="White" Padding="5,1,5,3"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!-- URL入力ボックスと「更新」ボタンです -->
<TextBox Grid.Column="0" Text="{Binding Url}"
Background="Black" Foreground="White"/>
<Button Grid.Column="1" HorizontalAlignment="Right"
Content="更新" Command="{Binding GetRSS}"/>
</Grid>
</Grid>
</Window>
using System.Diagnostics;
using System.Windows;
using System.Windows.Input;
namespace RSSMVVM_CS6 {
// RSSリーダーのViewです。
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
// RSSフィードを取得するコマンドを実行します。
rssViewModel.GetRSS.Execute( null );
}
// RSSフィード取得後のイベントです。( ViewModel側のハンドラに設定します )
private void rssViewModel_GetRSSCompleted( object sender, TaskResultEventArgs e ) {
if( e.Result == TaskResult.Failed )
MessageBox.Show( "RSSフィードの取得中にエラーが発生しました。", "Error", MessageBoxButton.OK, MessageBoxImage.Exclamation );
}
// リストボックス内の項目をダブルクリックした時のイベントです。
private void RSSListBox_MouseDoubleClick( object sender, MouseButtonEventArgs e ) {
// リストボックスが空( RSSフィールドを取得していない時 )
if( RSSListBox.Items.Count == 0 ) {
MessageBox.Show( "RSSフィードのコンテンツが空です。", "Error", MessageBoxButton.OK, MessageBoxImage.Exclamation );
return;
}
try {
// リンク先を既定のブラウザで開きます。
Process.Start( rssViewModel.Items[RSSListBox.SelectedIndex].Link );
}
catch {
MessageBox.Show( "コンテンツのページを開く時にエラーが発生しました。", "Error", MessageBoxButton.OK, MessageBoxImage.Exclamation );
}
}
}
}
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.ServiceModel.Syndication;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Data;
using System.Xml;
namespace RSSMVVM_CS6 {
// 任意のタスクの成功・失敗を表す列挙体です。
enum TaskResult {
// Succeeded : 成功 / Failed : 失敗
Succeeded, Failed
}
// 任意のタスクの成功・失敗のフラグを扱うイベント引数です。
class TaskResultEventArgs : EventArgs {
public TaskResult Result { get; set; }
public TaskResultEventArgs( TaskResult r ) {
Result = r;
}
}
// 任意のタスクの成功・失敗のフラグを扱うイベント用デリゲートです。
delegate void TaskResultEventHandler( object sender, TaskResultEventArgs e );
// RSSのコンテンツです。
class RSSContent {
// タイトル
public string Title { get; set; }
// 概要
public string Summary { get; set; }
// 投稿日時
public DateTime PubDate { get; set; }
// リンク
public string Link { get; set; }
}
// RSSフィードの配信元情報です。
class RSSProviderInfo {
// タイトル
public string Title { get; set; }
// 概要
public string Description { get; set; }
// 最終更新日時
public DateTime LastUpdatedTime { get; set; }
}
// RSSリーダーのModelです。
class RSSModel : INotifyPropertyChanged {
// HTMLタグを取り除くための文字列です。
private const string patternStr = @"<.*?>";
// コンストラクター
public RSSModel() {
BindingOperations.EnableCollectionSynchronization( Items, new object() );
}
#region 「2.4. 自動実装プロパティで初期値を設定する」の対象
// RSSフィードのコンテンツを格納するコレクションです。
public ObservableCollection<RSSContent> Items { get; private set; } = new ObservableCollection<RSSContent>();
// RSSフィードの配信元情報です。
public RSSProviderInfo RSSProvider { get; private set; } = new RSSProviderInfo();
// RSSフィードのURL( ViewModel、Viewから設定できるようにします。 )
public string Url { get; set; } = "";
#endregion *******************************
// RSSフィードを取得します。( 非同期メソッド )
public Task<TaskResult> GetRSS() {
TaskResult result = TaskResult.Succeeded;
RSSProvider = new RSSProviderInfo();
Items.Clear();
try {
using( XmlReader rd = XmlReader.Create( Url ) ) {
SyndicationFeed feed = SyndicationFeed.Load( rd );
// RSSフィードの配信元情報を取得します。
RSSProvider = new RSSProviderInfo {
Title = feed.Title.Text,
Description = Regex.Replace( feed.Description.Text, patternStr, string.Empty, RegexOptions.Singleline ),
LastUpdatedTime = feed.LastUpdatedTime.DateTime,
};
// RSSフィードのコンテンツを取得します。
foreach( SyndicationItem item in feed.Items ) {
Items.Add( new RSSContent {
Title = item.Title.Text,
Summary = Regex.Replace( item.Summary.Text, patternStr, string.Empty, RegexOptions.Singleline ),
PubDate = item.PublishDate.DateTime,
Link = item.Id
} );
}
}
}
catch {
// RSSフィードの取得中に例外が発生したら、失敗フラグを立てます。
result = TaskResult.Failed;
}
return Task.FromResult( result );
}
// RSSの取得完了後に発生させるイベントハンドラです。
public event TaskResultEventHandler GetRSSCompleted;
// プロパティ変更後に発生させるイベントハンドラです。
public event PropertyChangedEventHandler PropertyChanged;
#region 「2.1. null条件演算子で変更通知処理をコンパクトに」の対象
// プロパティ変更を通知します。
private void NotifyPropertyChanged( [CallerMemberName]string propertyName = null ) {
PropertyChanged?.Invoke( this, new PropertyChangedEventArgs( propertyName ) );
}
#endregion *******************************
#region << リファクタリング ポイント Model-2 >>
// RSSフィードを取得します。
public async Task GetRSSAsync() {
// RSSフィードを非同期で取得します。
TaskResult result = await Task.Run( () => GetRSS() );
// RSSフィードの取得が完了したことをViewModel側に通知します。
GetRSSCompleted?.Invoke( this, new TaskResultEventArgs( result ) );
}
#endregion *******************************
}
}
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
namespace RSSMVVM_CS6 {
// RSSリーダーのViewModelです。
class RSSViewModel : INotifyPropertyChanged {
// Model
RSSModel rssModel = new RSSModel();
#region 「2.1. null条件演算子で変更通知処理をコンパクトに」の対象
// コンストラクター
public RSSViewModel() {
rssModel.PropertyChanged += ( sender, e ) =>
PropertyChanged?.Invoke( this, new PropertyChangedEventArgs( e.PropertyName ) );
// RSSフィードの取得が完了したことをView側に通知します。
rssModel.GetRSSCompleted += ( sender, e ) => {
// RSSフィード取得中のフラグをオフにします。
IsProgress = false;
// RSSフィード取得完了したことをView側に通知します。
GetRSSCompleted?.Invoke( this, e );
};
}
#endregion *******************************
#region 「2.3. プロパティ / メソッドの本体をラムダ式で簡潔に」の対象
// RSSフィードのタイトル
public string Title => IsProgress ? "RSSフィールドを取得中 ..." : rssModel.RSSProvider.Title;
// RSSフィードの説明
public string Description => IsProgress ? "Recieving ..." : rssModel.RSSProvider.Description;
// RSSフィードの最終更新日
public DateTime LastUpdatedTime => IsProgress ? DateTime.MinValue : rssModel.RSSProvider.LastUpdatedTime;
// RSSフィードのコンテンツ
public ObservableCollection<RSSContent> Items => rssModel.Items;
#endregion *******************************
// RSSフィードのURL
public string Url {
get { return rssModel.Url; }
set { rssModel.Url = value; }
}
#region 「2.2. nameof演算子でリファクタリングが捗る!?」の対象
// RSSフィード取得中のフラグ
private bool isProgress = false;
public bool IsProgress {
get { return isProgress; }
set {
isProgress = value;
NotifyPropertyChanged();
NotifyPropertyChanged( nameof( Title ) );
NotifyPropertyChanged( nameof( Description ) );
NotifyPropertyChanged( nameof( LastUpdatedTime ) );
}
}
#endregion *******************************
// RSSの取得完了後に発生させるイベントハンドラです。
public event TaskResultEventHandler GetRSSCompleted;
// プロパティ変更後に発生させるイベントハンドラです。
public event PropertyChangedEventHandler PropertyChanged;
#region 「2.1. null条件演算子で変更通知処理をコンパクトに」の対象
// プロパティ変更を通知します。
private void NotifyPropertyChanged( [CallerMemberName]string propertyName = null ) {
PropertyChanged?.Invoke( this, new PropertyChangedEventArgs( propertyName ) );
}
#endregion *******************************
#region 「2.3. プロパティ / メソッドの本体をラムダ式で簡潔に」の対象
// RSSフィード取得のコマンド
private ICommand getRSS;
public ICommand GetRSS => getRSS ?? ( getRSS = new GetRSSCommand( this ) );
#endregion *******************************
// RSSフィードを取得するコマンドです。
private class GetRSSCommand : ICommand {
// ViewModel
private RSSViewModel rssViewModel;
#region 「2.1. null条件演算子で変更通知処理をコンパクトに」の対象
// コンストラクタ
public GetRSSCommand( RSSViewModel viewModel ) {
rssViewModel = viewModel;
// コマンド実行の可否の変更を通知します。
rssViewModel.PropertyChanged += ( sender, e ) =>
CanExecuteChanged?.Invoke( sender, e );
}
#endregion *******************************
#region 「2.3. プロパティ / メソッドの本体をラムダ式で簡潔に」の対象
// コマンドを実行できるかどうかを取得します。
public bool CanExecute( object parameter ) => !rssViewModel.IsProgress;
#endregion *******************************
// コマンド実行の可否の変更した時のイベントハンドラです。
public event EventHandler CanExecuteChanged;
// コマンドを実行し、RSSフィードを取得します。
public void Execute( object parameter ) {
// RSSフィード取得中のフラグをオンにします。
rssViewModel.IsProgress = true;
// RSSフィード取得します。
rssViewModel.rssModel.GetRSSAsync();
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment