はじめに
この記事は、MVVMとWPFの操作に特化しています。 Twitterクライアントの開発プロセスについて説明します。 開発プロセスはステップに分けられます。 各ステップの最後に、並行してアプリケーションを作成するリーダーには、動作するアプリケーションが必要です。 後続の各ステップは、前のステップで作成された機能にいくつかの機能を追加します。 サードパーティのライブラリTweetSharpを使用しました 。 私が英語で書いた元の記事と同様に、ソースコードへのリンクはここにあります 。
この記事は、WPF開発の初心者を対象としています。 ただし、読者はWPF、特にマスタリングされたデータバインディングの最初の経験を持っていると想定されています。
MVVMを使用する必要がある理由を説明しません。これは、Josh Smithの記事「モデルビュープレゼンテーションモデルを使用したWPFアプリケーション」によく書かれていると思います。 この記事を読みたくない場合-信じてください-WPFの場合のGUIの誤った設計は大きな頭痛の種になります。
顧客のツイッター予備分析
クライアントの設計の設計に移る前に、既存のクライアントのGUIを見て、ユーザーに提供する機能を理解しましょう。
ユーザーは、ページの上部でいくつかのタブを使用します-最近、返信、ユーザーなど。 選択したブックマークに応じて、メインウィンドウのスペースに異なる情報が表示されます。 たとえば、[最近]をクリックすると、サブスクリプションの最新のツイートが表示され、メインウィンドウで[メッセージ]をクリックすると、ダイレクトメッセージが表示されます。
もう少し考えて、TabControlを使用するのが妥当であることを理解します。
MVVMについて簡単に
例から始めましょう。 ツイートを表示するUserControlを記述する必要があるとします。 [ユーザーコントロールの追加]をクリックし、必要なコントロールをxamlにスローします。 次に、コードビハインドでツイートを保存するプロパティを作成します。 次に、同じコードビハインドでイベントハンドラーを追加します。たとえば、ツイートのUserPictureをクリックします。 一般に、すべては、WinFormsなどの前世代のテクノロジーを使用してこの制御を行っている場合と同じです。 このコントロールを詳しく見てみましょう-ユーザーに見える部分の説明があります-それらのxaml。 また、背後にあるコードもあります。
ここで、最初の機能要求を取得したとします-ユーザーは他のツイートを表示したいが、まったく同じコントロールでした。 コードビハインドで適切なフラグを立てることができます。または、ソフトウェア設計がより高度な場合は、GoFの戦略を使用します。 しばらくすると、2番目の機能要求が届きます-各ツイートにはユーザーにとって不必要な情報が多すぎて、コントロールを新しく見直す必要がありますが、同時に古いユーザーを怒らせ、古い視覚表現で作業する機会を残したくありません。 など-要件は新しくなりつつあり、さまざまなトリックと独創的なソリューションを使用してそれらを実装します。
やめて! 時間をさかのぼってみましょう-コントロールが1つしかない状況に戻りましょう。 コードビハインドとコントロールクラス自体を広めるとどうなりますか? それらは本質を2つに分けます。 ビューと呼ばれるもの-これはユーザーに表示されるもので、UserControlから継承されたクラスです。 2番目の名前はViewModelです。これらは、Viewを通じてユーザーに表示されるものに直接関連するデータであり、アバターをクリックするなどのイベントハンドラー関数です。 その後、最初の機能要求を簡単に解決できます。ViewModelを記述し、古いビューをそれにマップするだけです。 最初の機能リクエストも簡単に解決できます。異なるビューを1つのViewModelにマップできます。 いい考えだと思います。
しかし、ViewとViewModelを接続する方法は? このために、データバインディング-WPFの主要ツールが使用されます。 使用するDataContextを何らかの方法でViewに伝え、データをどこから取得するかを伝えれば、コードビハインドの場合とほぼ同じようにデータにバインドします。 ここでは、技術的な詳細を意図的に省略します-詳細は後で説明します。
これは、MVVMを使用して記述されたアプリケーションの典型的なクラス図です。
Viewクラスがあり、ViewModelのデータを使用しますが、モデルについては何も知りません(この場合、モデルはTweetSharpライブラリに実装されます)。 一方、ViewModelは、どのViewがそれらを使用するかを知りませんが、Modelから情報を取得できます。
通常、残りのViewとViewModelを集約するメインのViewとViewModelもあります。
ステップ1.アプリケーションフレームワーク
最初に、1つのTabItem-Recentのみを含むTabControlでアプリケーションを作成します。 MVVMに従って行動します。
新しいWPFアプリケーションを作成しましょう。 MainWindowウィンドウで、TabControlを追加します。
<TabControl Height="400"
HorizontalAlignment="Left"
Name="Tabs"
VerticalAlignment="Top" Width="300">
/>
このウィンドウは、ビューのメイン要素になります。 他のすべての要素(tabItems)が表示されます。 対応するViewModelクラスに対応する必要があります。これは、そのフィールドのメインになります。 MainWindowのコードビハインドではなく、その中にデータを保存し、イベントハンドラー関数を記述します。 新しいパブリッククラスSimpleTwitterClientViewModelを作成します。 とりあえず空にします。 次に、コードクラスではなくSimpleTwitterClientViewModelでデータを検索するようにウィンドウクラスに指示する必要があります。 DataContextを設定する必要があるもの。 これを行うには、ViewModelフィールドをコードビハインドに追加し、nameプロパティをウィンドウに追加して、DataContextを定義します。
<Window x:Class="SimpleTwitterClient.MainWindow"
x:Name="MainWindowInstance"
xmlns:view="clr-namespace:SimpleTwitterClient.View"
xmlns:viewModel="clr-namespace:SimpleTwitterClient.ViewModel"
DataContext="{BindingViewModel,ElementName=MainWindowInstance}">
public partial class MainWindow : Window
{
private SimpleTwitterClientViewModel _viewModel;
public SimpleTwitterClientViewModel ViewModel
{
get { return _viewModel; }
set { _viewModel = value; }
}
public MainWindow()
{
_viewModel = new SimpleTwitterClientViewModel();
InitializeComponent();
}
}
次に、RecentのViewとViewModelを作成します。 RecentView-空のコードビハインドを持つUserControlのみになります。 コントロールを追加して、現在の空のTabItemと区別します。 RecentViewModelを空にします。
SimpleTwitterClientViewModelは、TabItemのViewModelを格納するクラスになります。 これまでのところ、最近のみがあります。 それでは、このコードを書きましょう。
public class SimpleTwitterClientViewModel
{
RecentViewModel _recentPage = new RecentViewModel();
public RecentViewModel RecentPage
{
get { return _recentPage; }
set { _recentPage = value; }
}
}
ここでアプリケーションを起動すると、これまでのところ何も変わっていないことがわかります。 また、TabItemにRecentViewを表示するように指示する必要があります。 どうやってやるの? これは微妙ですが重要なポイントです-最初に、ContentをRecentPageに設定する必要があります。
/>
次に、ViewModelからViewへのビューを作成する必要があります。 TabItemのコンテンツがRecentViewModelの場合、RecentViewを表示する必要があることを確認するコード。 このコードは次のとおりです。
<Window.Resources>
<DataTemplateDataType="{x:TypeviewModel:RecentViewModel}">
<view:RecentView />
</Window.Resources>
これらすべてのアクションの結果として、次のようなアプリケーションがあります。
なぜそのような困難なのですか? これはほんの始まりに過ぎず、アプリケーションがより複雑になると、MVVMの利点を理解できます。
ステップ2.最初のツイート
RecentPageにデータ、最新のツイートを入力します。 このために、TweetSharpライブラリーを使用します。 アセンブリのリストに追加します。
Twitter APIでアプリケーションを使用するには、一連の詐欺を行う必要があります。
1. ConsumerKeyおよびConsumerSecret。 リンクhttp://dev.twitter.com/に従って、「登録」をクリックします。 その後、アプリケーションに関する情報を含むページが表示され、そこからConsumerKeyとConsumerSecretを取得して、設定に保存します。
2. PIN(oauth_verifier)。 アプリケーションを初めて起動すると、ConsumerSecretとConsumerKeyを含むリクエストをサーバーに送信する必要があります。
FluentTwitter.SetClientInfo(
new TwitterClientInfo
{
ConsumerKey = Settings.Default.ConsumerKey,
ConsumerSecret = Settings.Default.ConsumerSecret
});
var twit = FluentTwitter.CreateRequest().Authentication.GetRequestToken();
var response = twit.Request();
var RequestToken = response.AsToken();
twit = twit.Authentication.AuthorizeDesktop(RequestToken.Token);
最後の行はデフォルトでブラウザを開きます。 アプリケーションによるサービスの使用を許可するかどうかについて質問があります。[許可]をクリックし、アプリケーションのダイアログボックスにPINを入力します。 getPinFromUserメソッドでこのダイアログを呼び出します。
string verifier = getPinFromUser();
3. AccesToken。 次に、consumerKey、consumerSecret、Pinを含むリクエストを送信します。 サービスは、将来使用されるAccessTokenを返します。
twit.Authentication.GetAccessToken(RequestToken.Token, verifier);
var response2 = twit.Request();
4. AccessTokenを更新します。 しばらくすると、サービスはAccessTokenの更新を要求する場合があります。
OAuthHandlerクラスをModelフォルダーに追加します。 そこにUserControl AskPinFromUserDialogを追加します。 そして、アプリケーションのリソースに、アプリケーションを登録したときにTwitterから受け取ったConsumerKey、ConsumerSecretを追加します。 OauthInfoFileNameを追加して、アプリケーションの構成ファイルへのパスを書き込みます。 ツイッターから受け取ったキーはそこに保存されます。 非常に安全ではありませんが、簡単です。 最後に、このクラスのオブジェクトをメインのViewModelに追加します。
Model.OAuthHandler _oauthHandler = newModel.OAuthHandler();
これでRecentPageを使い始めることができます。 開始するには、ツイートストレージコンテナをRecentPageクラスに追加します。
public ObservableCollection Tweets
{
get; set;
}
ObservableCollection, - List View, .
. - _oauthHandler ViewModel. RecentViewModel, _oauthHandler .
, _oauthHandler , .
. LoadTweets RecentViewModel:
public void LoadTweets()
{
TwitterResult response = FluentTwitter
.CreateRequest()
.AuthenticateWith(
Settings.Default.ConsumerKey,
Settings.Default.ConsumerSecret,
Model.OAuthHandler.Token,
Model.OAuthHandler.TokenSecret)
.Statuses()
.OnHomeTimeline().AsJson().Request());
var statuses = response.AsStatuses();
foreach (TwitterStatus status in statuses)
{
Tweets.Add(status);
}
}
RecentViewModel:
public RecentViewModel(Model.OAuthHandleroauthHandler)
{
_oauthHandler = oauthHandler;
Tweets = newObservableCollection();
LoadTweets();
}
ListBox :
<ListBoxx:Name="RecentTweetList"
ItemsSource="{Binding Path=Tweets}"
IsSynchronizedWithCurrentItem="True"/>
, TweetStatus.ToString(). , ListBox , TweetStatus. , , DataTemplate UserControl.Resources. DataTemplate , TweetStatus. , TweetStatus Text, Binding Path=Text.
DataTemplate:
<DataTemplate x:Key="TweetItemTemplate">
<Grid x:Name="TTGrid">
<Grid.RowDefinitions>
/>
/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
/>
/>
/>
</Grid.ColumnDefinitions>
<Image Source="{Binding Path=User.ProfileImageUrl}"
Name="UserAvatarImage" />
<TextBlock
Name="ScreenNameTextBlock"
Text="{Binding Path=User.ScreenName}"
Grid.Row="1"Grid.ColumnSpan="2"/>
<TextBlock
Text="{Binding Path=Text}"
TextWrapping="Wrap"Grid.Column="1"Grid.ColumnSpan="2" />
次に、宣言されたDataTemplateを使用するようListBoxに指示します。
ItemTemplate="{StaticResourceTweetItemTemplate}"
アプリケーションは次のようになります。
ステップ3.新しいページ
[最近]タブには、フォローしているユーザーが最後に投稿したツイートと、自分のツイートが表示されます。 TweetSharpに関しては、これはHomeTimelineです。 しかし、これらはあなたの興味を引くかもしれないすべてのツイートではありません。 OnHomeTimelineメソッドが宣言されているTwitterStatusesExtensionsクラスにアクセスすると、OnFriendsTimeline、OnListTimeline、ByByMeなどのさまざまな基準でグループ化されたツイートのリストを返す他のメソッドが表示されます。
フォローしているユーザーのリツイートを含むページを作成します。 Recentの作成と同様に、RetweetsViewModelを作成します。 コードを詳細に説明することはせず、読者に演習を残します。 ビューも書くと思いますか? うーん、最近とリツイートに表示される情報は構造的に同じです。 MVVMとWPFはコピーアンドペーストを最小限に抑えます-この場合、RetweetsViewについてはまだ考えていません。 ステップ3の最後に、これが不要であることがわかります。
次に、RetweetsViewModelクラスのオブジェクトをメインのViewModelに追加する必要があります。 ページが2つしかない場合、これは通常の解決策です。 しかし、私たちは多くのページを作りたいです。 したがって、このソリューションは最適ではありません。 メインViewModelにさまざまなViewModelを持つコンテナを保存することをお勧めします。 RetweetsViewModelはRecentViewModelと非常によく似ており、クラスからExtract Interfaceを簡単に使用できることがわかります。 また、OauthHandlerをプロパティにし、コンストラクターで初期化しませんが、基本クラスで初期化します。 IPageBaseインターフェイスを呼び出します。
public interface IPageBase
{
void LoadTweets();
ObservableCollection Tweets { get; set; }
}
ViewModel:
ObservableCollection _pages;
public ObservableCollection Pages
{
get { return _pages; }
set { _pages = value; }
}
public SimpleTwitterClientViewModel()
{
_pages = new ObservableCollection();
_pages.Add(new RecentViewModel());
_pages.Add(new RetweetsViewModel());
foreach (var page in _pages)
{
page.LoadTweets();
}
}
View DataTemplate RetweetsPage. TabItem – tabItem ViewModel RecentPage:
/>
TabItems Pages. TabControl ItemsSource. Pages TabItems – Pages. – TabItems Header. header TabItems? Name IPageBase. -. , TabItem Header:
<TabControlName="Tabs"
ItemsSource="{Binding Pages}">
<TabControl.ItemContainerStyle>
</TabControl.ItemContainerStyle>
– RetweetsViewModel View. RetweetsView. RecentView! MVVM!
<DataTemplateDataType="{x:TypeviewModel:RetweetsViewModel}">
<view:RecentView />
. :
DataTemplate – ViewModel .
– follower & following.
4. ICommand
, . MVVM – OnMouseClick. . , . TextBox , :
code behind – EnterTweetTextBox.Text. MVVM . ViewModel , Send. , .
, EnterTweetTextBox . SimpleTwitterClientViewModel – Message. Text:
<TextBoxName="EnterTweetTextBox"
Text="{BindingMessage}"/>
SendTweet SimpleTwitterClientViewModel:
private void SendTweet()
{
var twitter = FluentTwitter.CreateRequest();
twitter.AuthenticateWith(
Settings.Default.ConsumerKey,
Settings.Default.ConsumerSecret,
OAuthHandler.Token,
OAuthHandler.TokenSecret);
twitter.Statuses().Update(Message);
var response = twitter.Request();
//you can verify the response here
}
– ? – Button Command, . ICommand. SendMessage, , . ? – -adapter ICommand. Josh Smith:
internal class RelayCommand : ICommand
{
#region Fields
readonly Action _execute;
readonly Func _canExecute;
#endregion
#region Constructors
public RelayCommand(Action execute)
: this(execute, null)
{
}
public RelayCommand(Action execute, Func canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute();
}
public event EventHandler CanExecuteChanged
{
add
{
if (_canExecute != null)
CommandManager.RequerySuggested += value;
}
remove
{
if (_canExecute != null)
CommandManager.RequerySuggested -= value;
}
}
public void Execute(object parameter)
{
_execute();
}
#endregion // ICommand Members
}
RelayCoommand ICommand. , SendTweet ICommand:
RelayCommand _sendCommand;
public ICommand SendCommand
{
get
{
if (_sendCommand == null)
{
_sendCommand = new RelayCommand(() => this.SendTweet());
}
return _sendCommand;
}
}
, SendButton , SendCommand:
<Button Name="SendTweetButton"
Command="{Binding SendCommand}"/>
, ? :
, – .
, feature: userpicture, TabItem . TabItem , .
public interface IPageBase
{
void LoadTweets();
ObservableCollection Tweets { get; set; }
}
ViewModel:
ObservableCollection _pages;
public ObservableCollection Pages
{
get { return _pages; }
set { _pages = value; }
}
public SimpleTwitterClientViewModel()
{
_pages = new ObservableCollection();
_pages.Add(new RecentViewModel());
_pages.Add(new RetweetsViewModel());
foreach (var page in _pages)
{
page.LoadTweets();
}
}
View DataTemplate RetweetsPage. TabItem – tabItem ViewModel RecentPage:
/>
TabItems Pages. TabControl ItemsSource. Pages TabItems – Pages. – TabItems Header. header TabItems? Name IPageBase. -. , TabItem Header:
<TabControlName="Tabs"
ItemsSource="{Binding Pages}">
<TabControl.ItemContainerStyle>
</TabControl.ItemContainerStyle>
– RetweetsViewModel View. RetweetsView. RecentView! MVVM!
<DataTemplateDataType="{x:TypeviewModel:RetweetsViewModel}">
<view:RecentView />
. :
DataTemplate – ViewModel .
– follower & following.
4. ICommand
, . MVVM – OnMouseClick. . , . TextBox , :
code behind – EnterTweetTextBox.Text. MVVM . ViewModel , Send. , .
, EnterTweetTextBox . SimpleTwitterClientViewModel – Message. Text:
<TextBoxName="EnterTweetTextBox"
Text="{BindingMessage}"/>
SendTweet SimpleTwitterClientViewModel:
private void SendTweet()
{
var twitter = FluentTwitter.CreateRequest();
twitter.AuthenticateWith(
Settings.Default.ConsumerKey,
Settings.Default.ConsumerSecret,
OAuthHandler.Token,
OAuthHandler.TokenSecret);
twitter.Statuses().Update(Message);
var response = twitter.Request();
//you can verify the response here
}
– ? – Button Command, . ICommand. SendMessage, , . ? – -adapter ICommand. Josh Smith:
internal class RelayCommand : ICommand
{
#region Fields
readonly Action _execute;
readonly Func _canExecute;
#endregion
#region Constructors
public RelayCommand(Action execute)
: this(execute, null)
{
}
public RelayCommand(Action execute, Func canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute();
}
public event EventHandler CanExecuteChanged
{
add
{
if (_canExecute != null)
CommandManager.RequerySuggested += value;
}
remove
{
if (_canExecute != null)
CommandManager.RequerySuggested -= value;
}
}
public void Execute(object parameter)
{
_execute();
}
#endregion // ICommand Members
}
RelayCoommand ICommand. , SendTweet ICommand:
RelayCommand _sendCommand;
public ICommand SendCommand
{
get
{
if (_sendCommand == null)
{
_sendCommand = new RelayCommand(() => this.SendTweet());
}
return _sendCommand;
}
}
, SendButton , SendCommand:
<Button Name="SendTweetButton"
Command="{Binding SendCommand}"/>
, ? :
, – .
, feature: userpicture, TabItem . TabItem , .
public interface IPageBase
{
void LoadTweets();
ObservableCollection Tweets { get; set; }
}
ViewModel:
ObservableCollection _pages;
public ObservableCollection Pages
{
get { return _pages; }
set { _pages = value; }
}
public SimpleTwitterClientViewModel()
{
_pages = new ObservableCollection();
_pages.Add(new RecentViewModel());
_pages.Add(new RetweetsViewModel());
foreach (var page in _pages)
{
page.LoadTweets();
}
}
View DataTemplate RetweetsPage. TabItem – tabItem ViewModel RecentPage:
/>
TabItems Pages. TabControl ItemsSource. Pages TabItems – Pages. – TabItems Header. header TabItems? Name IPageBase. -. , TabItem Header:
<TabControlName="Tabs"
ItemsSource="{Binding Pages}">
<TabControl.ItemContainerStyle>
</TabControl.ItemContainerStyle>
– RetweetsViewModel View. RetweetsView. RecentView! MVVM!
<DataTemplateDataType="{x:TypeviewModel:RetweetsViewModel}">
<view:RecentView />
. :
DataTemplate – ViewModel .
– follower & following.
4. ICommand
, . MVVM – OnMouseClick. . , . TextBox , :
code behind – EnterTweetTextBox.Text. MVVM . ViewModel , Send. , .
, EnterTweetTextBox . SimpleTwitterClientViewModel – Message. Text:
<TextBoxName="EnterTweetTextBox"
Text="{BindingMessage}"/>
SendTweet SimpleTwitterClientViewModel:
private void SendTweet()
{
var twitter = FluentTwitter.CreateRequest();
twitter.AuthenticateWith(
Settings.Default.ConsumerKey,
Settings.Default.ConsumerSecret,
OAuthHandler.Token,
OAuthHandler.TokenSecret);
twitter.Statuses().Update(Message);
var response = twitter.Request();
//you can verify the response here
}
– ? – Button Command, . ICommand. SendMessage, , . ? – -adapter ICommand. Josh Smith:
internal class RelayCommand : ICommand
{
#region Fields
readonly Action _execute;
readonly Func _canExecute;
#endregion
#region Constructors
public RelayCommand(Action execute)
: this(execute, null)
{
}
public RelayCommand(Action execute, Func canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute();
}
public event EventHandler CanExecuteChanged
{
add
{
if (_canExecute != null)
CommandManager.RequerySuggested += value;
}
remove
{
if (_canExecute != null)
CommandManager.RequerySuggested -= value;
}
}
public void Execute(object parameter)
{
_execute();
}
#endregion // ICommand Members
}
RelayCoommand ICommand. , SendTweet ICommand:
RelayCommand _sendCommand;
public ICommand SendCommand
{
get
{
if (_sendCommand == null)
{
_sendCommand = new RelayCommand(() => this.SendTweet());
}
return _sendCommand;
}
}
, SendButton , SendCommand:
<Button Name="SendTweetButton"
Command="{Binding SendCommand}"/>
, ? :
, – .
, feature: userpicture, TabItem . TabItem , .
public interface IPageBase
{
void LoadTweets();
ObservableCollection Tweets { get; set; }
}
ViewModel:
ObservableCollection _pages;
public ObservableCollection Pages
{
get { return _pages; }
set { _pages = value; }
}
public SimpleTwitterClientViewModel()
{
_pages = new ObservableCollection();
_pages.Add(new RecentViewModel());
_pages.Add(new RetweetsViewModel());
foreach (var page in _pages)
{
page.LoadTweets();
}
}
View DataTemplate RetweetsPage. TabItem – tabItem ViewModel RecentPage:
/>
TabItems Pages. TabControl ItemsSource. Pages TabItems – Pages. – TabItems Header. header TabItems? Name IPageBase. -. , TabItem Header:
<TabControlName="Tabs"
ItemsSource="{Binding Pages}">
<TabControl.ItemContainerStyle>
</TabControl.ItemContainerStyle>
– RetweetsViewModel View. RetweetsView. RecentView! MVVM!
<DataTemplateDataType="{x:TypeviewModel:RetweetsViewModel}">
<view:RecentView />
. :
DataTemplate – ViewModel .
– follower & following.
4. ICommand
, . MVVM – OnMouseClick. . , . TextBox , :
code behind – EnterTweetTextBox.Text. MVVM . ViewModel , Send. , .
, EnterTweetTextBox . SimpleTwitterClientViewModel – Message. Text:
<TextBoxName="EnterTweetTextBox"
Text="{BindingMessage}"/>
SendTweet SimpleTwitterClientViewModel:
private void SendTweet()
{
var twitter = FluentTwitter.CreateRequest();
twitter.AuthenticateWith(
Settings.Default.ConsumerKey,
Settings.Default.ConsumerSecret,
OAuthHandler.Token,
OAuthHandler.TokenSecret);
twitter.Statuses().Update(Message);
var response = twitter.Request();
//you can verify the response here
}
– ? – Button Command, . ICommand. SendMessage, , . ? – -adapter ICommand. Josh Smith:
internal class RelayCommand : ICommand
{
#region Fields
readonly Action _execute;
readonly Func _canExecute;
#endregion
#region Constructors
public RelayCommand(Action execute)
: this(execute, null)
{
}
public RelayCommand(Action execute, Func canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute();
}
public event EventHandler CanExecuteChanged
{
add
{
if (_canExecute != null)
CommandManager.RequerySuggested += value;
}
remove
{
if (_canExecute != null)
CommandManager.RequerySuggested -= value;
}
}
public void Execute(object parameter)
{
_execute();
}
#endregion // ICommand Members
}
RelayCoommand ICommand. , SendTweet ICommand:
RelayCommand _sendCommand;
public ICommand SendCommand
{
get
{
if (_sendCommand == null)
{
_sendCommand = new RelayCommand(() => this.SendTweet());
}
return _sendCommand;
}
}
, SendButton , SendCommand:
<Button Name="SendTweetButton"
Command="{Binding SendCommand}"/>
, ? :
, – .
, feature: userpicture, TabItem . TabItem , .