C# MVVM模式中的去抖动事件/命令

C# MVVM模式中的去抖动事件/命令,c#,mvvm,uwp,caliburn.micro,C#,Mvvm,Uwp,Caliburn.micro,我正在使用Caliburn.Micro构建一个通用windows应用程序,不幸的是,由于某些硬件限制,我们需要针对windows 10 1607,因此无法实现任何依赖于.NET标准/UWP 16299的软件包,这包括ReactiveUI 在这个特定的场景中,我有一种视图模型优先的方法,它生成一个映射(和一些其他资源),然后将这些映射绑定到XAML视图中的一个映射视图。理想情况下,我希望在通过ViewpointChanged事件移动地图时触发一个进程 查看模型 public class Examp

我正在使用Caliburn.Micro构建一个通用windows应用程序,不幸的是,由于某些硬件限制,我们需要针对windows 10 1607,因此无法实现任何依赖于.NET标准/UWP 16299的软件包,这包括ReactiveUI

在这个特定的场景中,我有一种视图模型优先的方法,它生成一个映射(和一些其他资源),然后将这些映射绑定到XAML视图中的一个映射视图。理想情况下,我希望在通过
ViewpointChanged
事件移动地图时触发一个进程

查看模型

public class ExampleViewModel : Screen
{
    public ExampleViewModel()
    {
        Map = new Map();
    }

    public Map Map { get; set; }
    public BindableCollection<MapItems> MapItems { get; set; }

    private UpdateMapItems(Envelope visibleArea)
    {
        // The visibleArea param will include the current viewpoint of the map view
        // This method will effectively generate the appropriate map items based on the current coordinates
    }
}
公共类示例视图模型:屏幕
{
公共示例ViewModel()
{
Map=新Map();
}
公共映射映射{get;set;}
公共BindableCollection映射项{get;set;}
私有更新包(信封可视区域)
{
//visibleArea参数将包括地图视图的当前视点
//此方法将根据当前坐标有效地生成适当的地图项目
}
}
查看

...
<MapView x:Name="MapView" Map="{Binding Map}" cal:Message.Attach="[Event ViewpointChanged] = [Action UpdateMapItems(MapView.VisibleArea.Extent)]" />
...
。。。
...
现在,这在技术上是可行的,但有一个主要缺陷,即地图的每次移动都会多次触发ViewpointChanged事件(类似于OnMouseMove的效果)

理想情况下,我希望能够限制/取消此事件,以便仅当视图在300ms内未移动时才处理贴图项

我发现一篇文章涉及实现一个
dispatchermer
,但是该代码的元素,如
DispatcherPriority
Dispatcher
在UWP中似乎不可用,因此除非存在替代方案,否则我认为这不会起作用

我已经看过这个系统了。反应性的,但对于我想要实现的目标来说,这似乎异常复杂


任何指点都将不胜感激

您可以通过几种方式来实现这一点

  • 反应性扩展
  • 使用
    油门
    操作员可以实现所需的行为

    可观察
    .FromEventPattern(MapView,名称(ViewpointChanged));
    .节气门(时间跨度从毫秒(300));
    .Subscribe(eventPattern=>vm.UpdateMapItems(eventPattern.Sender.VisibleArea.Extent));
    
    当使用
    FromEventPattern
    时,我们将事件映射到的实例,其中包括事件的
    发送方
    (源)

    我通过订阅
    UIElement
    PointerMoved
    事件进行测试。如果我们继续移动,会多次触发
    HandleEvent
    。但是,对于
    节流阀
    ,事件处理程序只执行一次。这是我们停止移动后的间隔时间

    MainPage.xaml

    
    
    MainPage.xaml.cs

    公共密封部分类主页面:第页
    {
    公共主页()
    {
    this.InitializeComponent();
    可观察
    .FromEventPattern(MyUIElement,名称(UIElement.PointerMoved))
    .节气门(时间跨度从毫秒(300))
    .Subscribe(eventPattern=>HandleEvent(eventPattern.Sender,eventPattern.EventArgs));
    }
    私有void HandleEvent(对象源,PointErrorutedEventArgs args args)
    {
    Debug.WriteLine(“指针移动”);
    }
    }
    
  • 习俗
  • 我们的自定义
    Throttle
    类跟踪已处理的最后一个
    sender
    args
    。按照“传递到
    节流阀
    进行处理”进行处理。只有当计时器过期且没有发生其他事件时,
    eventHandler
    (作为构造函数参数传递)才会实际执行

    公共级油门
    {
    专用只读调度程序计时器;
    私有对象\u lastSender;
    私人TEventArgs_lastEventArgs;
    公共节流(EventHandler EventHandler,时间间隔)
    {
    _计时器=新调度程序
    {
    间隔=间隔
    };
    _计时器.勾号+=(s,e)=>
    {
    _timer.Stop();
    eventHandler(_lastSender,_lastEventArgs);
    };
    }
    public void ProcessEvent(对象发送方,TEventArgs args)
    {
    _timer.Stop();
    _timer.Start();
    _lastSender=发送方;
    _lastEventArgs=args;
    }
    }
    
    MainPage.xaml.cs

    公共密封部分类主页面:第页
    {
    专用只读节流阀_节流阀;
    公共主页()
    {
    this.InitializeComponent();
    var interval=时间跨度从毫秒(300);
    _节气门=新节气门(扶手通风口,间隔);
    MyUIElement.PointerMoved+=(发送方,e)=>\u throttle.ProcessEvent(发送方,e);
    }
    私有void HandleEvent(对象发送方,PointErroroutedEventArgs e)
    {
    Debug.WriteLine(“指针移动”);
    }
    }
    

    更新

    我正在努力弄清楚在MVVM环境中,所有东西是如何结合在一起的。事件需要触发的逻辑包含在ViewModel中,但视图和ViewModel应该完全分开

    有几件事我想提一下:

    • 关于分离关注点的必要性,您是对的,但是许多开发人员不清楚这到底意味着什么。视图模型应该完全不知道谁在听,这是毫无疑问的。但是视图依赖于视图模型来获取其数据,因此视图可以了解视图模型。问题更多的是以松散耦合的方式执行,即使用绑定和契约,而不是直接访问视图模型成员
    • 这就是为什么我并不特别喜欢卡利本的行为。使用
      cal:Message.Attach
      没有将视图语法与视图模型的语法解耦的契约(例如ICommand)。当然,有绑定在起作用,所以您仍然可以获得解耦的MVVM层
    长话短说,有一个