新しいSystem.Reactiveビットを試して、リストビュー内の項目をコンテキストメニューのクリックに応じてアクションを実行することを簡略化するかどうかを考えました。 これまでの設定ビットはやや簡単ですが、私は2つのイベントストリーム(アイテムの選択とメニューのクリック)を正しい方法で組み合わせることに戸惑っています。コンテキストメニューの操作にRxを使用する
これは私がこれまで (bookListViewは、私の本が含まれており、メニューbookListContextMenuを表示)
private void frmBookList_Load(object sender, EventArgs e)
{
//filter click events to right clicks over a ListViewItem containing a actual book
var rightclicks = from click in Observable.FromEventPattern<MouseEventArgs>(bookListView, "MouseClick")
where click.EventArgs.Button == MouseButtons.Right &&
ClickedOnBook((ListView)click.Sender, click.EventArgs) != null
select click;
//subscribe to clicks to display context menu at clicked location
rightclicks.Subscribe(click => bookListContextMenu.Show((Control)click.Sender, click.EventArgs.Location));
//subscribe to clicks again to convert click into clicked book
var rightclickedbook = rightclicks.Select(click => ClickedOnBook((ListView)click.Sender, click.EventArgs));
//capture context menu click, convert to action enum
var clickaction = Observable.FromEventPattern<ToolStripItemClickedEventArgs>(bookListContextMenu, "ItemClicked")
.Select(click => GetAction(click.EventArgs.ClickedItem));
//combine the two event streams to get a pair of Book and Action
//can project to an anonymoue type as it will be consumed within this method
var bookaction = clickaction.CombineLatest(rightclickedbook, (action, book) => new { Action = action, Book = book });
//subscribe to action and branch to action specific method
bookaction.Subscribe(doaction =>
{
switch (doaction.Action)
{
case BookAction.Delete:
DeleteBookCommand(doaction.Book);
break;
case BookAction.Edit:
EditBookCommand(doaction.Book);
break;
}
});
}
public enum BookAction
{
None = 0,
Edit = 1,
Delete = 2
}
private BookAction GetAction(ToolStripItem item)
{
if (item == deleteBookToolStripMenuItem) return BookAction.Delete;
else if (item == editBookToolStripMenuItem) return BookAction.Edit;
else return BookAction.None;
}
private Book ClickedOnBook(ListView lview, MouseEventArgs click)
{
ListViewItem lvitem = lview.GetItemAt(click.X, click.Y);
return lvitem != null ? lvitem.Tag as Book : null;
}
private void DeleteBookCommand(Book selectedbook)
{
//code to delete a book
}
private void EditBookCommand(Book selectedbook)
{
//code to edit a book
}
問題は結合関数である持っているものです。 「CombineLatest」を使用してコンテキストメニューを初めて使用した場合は、その後でそれぞれ を右クリックすると、新しい選択項目で前のアクションが再び呼び出されます。
私は「Zip」を使って本を右クリックしますが、コンテキストメニューをクリックするのではなく、次に右クリックして実際にメニューをクリックすると、アクションが呼び出されます第1の選択であり、第2の選択ではない。
私は、さまざまな形式のタイムバッファとウィンドウと最新のものなどを試しましたが、通常、メニューが表示されるとすぐにブロックして選択できないようにしたり、メニューが表示された場合は空のシーケンスについての例外を受け取りましたアイテムはクリックされていませんでした。
私は行方不明だがこれを行う簡単な方法が必要であると確信しているが、私はそれが何であるか分からない。
おそらくこれは?
//the menu may be closed with or without clicking any item
var contextMenuClosed = Observable.FromEventPattern(bookListContextMenu, "Closed");
var contextMenuClicked = Observable.FromEventPattern<ToolStripItemClickedEventArgs>(bookListContextMenu, "ItemClicked");
//combine the two event streams to get a pair of Book and Action
//which we can project to an anonymoue type as it will be consumed within this method
var bookaction = from mouseclick in rightclicks
let book = ClickedOnBook((ListView)mouseclick.Sender, mouseclick.EventArgs)
from menuclick in contextMenuClicked.Take(1).TakeUntil(contextMenuClosed)
let action = GetAction(menuclick.EventArgs.ClickedItem)
select new { Action = action, Book = book };
おそらく答えはSelectMany、Take、TakeUntilですが、もう5時間はまだ投稿できます。 – eddwo
私の答えの2番目のオプションを見てみましょう。 –