gpt4 book ai didi

c# - 使用 Rx 进行上下文菜单操作

转载 作者:行者123 更新时间:2023-11-30 17:17:19 25 4
gpt4 key购买 nike

我想我会尝试使用新的 System.Reactive 位,看看它是否会简化对上下文菜单单击 ListView 中的项目的响应执行操作。到目前为止,所有设置位似乎都更容易一些,但我正在努力以正确的方式组合两个事件流(项目选择和菜单点击)。

这是我目前所拥有的(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”然后右键单击一本书,但在上下文菜单之外单击而不是在它上面单击,那么下次我右键单击并实际单击菜单时,将在第一次调用操作选择而不是第二个。

我尝试了各种形式的时间限制缓冲区和窗口以及最新的等等,但通常只能在菜单出现时立即阻塞从而无法进行选择,或者如果显示菜单但出现空序列异常没有项目被点击。

我确信一定有一种更简单的方法可以做到这一点,但我不知道它是什么。

也许是这个?

//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 };

最佳答案

解决这些问题的最简单方法是在整个可观察序列中携带起因元素(您从 ClickedOnBook 获得的元素)。一种方法是在每次有人单击该书时创建一个上下文菜单的单独实例。有点像

IObservable<BookAction> WhenBookAction()
{
return Observable.Defer(() =>
{
var menu = ContextMenuFactory.CreateMenu;
return Observable
.FromEventPattern<ToolStripItemClickedEventArgs>(
menu, "ItemClicked")
.Select(click => GetAction(click.EventArgs.ClickedItem));
}
}

现在一个简单的“from/from”就可以了:

from book in rightclicks.Select(
click => ClickedOnBook((ListView)click.Sender, click.EventArgs))
from action in WhenBookAction()
select book, action;

你可以保证这对书正是导致菜单出现的那本书 - “from/from”又名“SelectMany”又名“Monad”会处理这个问题。

编辑:这是另一种很好的方法。我懒得复制上下文菜单,所以我的“测试套件”是一个带有三个按钮的窗口,分别名为“firstBn”、“secondBn”和“actionBn”——这与这个问题中的模式相同。这是我得到的:

    public MainWindow()
{
InitializeComponent();
var actions = Observable
.FromEventPattern<RoutedEventArgs>(actionBn, "Click");
var firsts = Observable
.FromEventPattern<RoutedEventArgs>(firstBn, "Click")
.Select(x => firstBn);
var seconds = Observable
.FromEventPattern<RoutedEventArgs>(secondBn, "Click")
.Select(x => secondBn);
var buttons = firsts.Merge(seconds);
var buttonActions = buttons
.Select(x => actions.Select(_ => x))
.Switch();
buttonActions.Subscribe(x => Console.WriteLine(x.Name));
}

关于c# - 使用 Rx 进行上下文菜单操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6562617/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com