c# - 我如何使用约定实现 Caliburn.Micro 操作的延迟

标签 c# wpf async-await caliburn.micro

我有时需要延迟绑定(bind)执行(想想一个调用服务器的搜索框,你希望它只在用户暂停一秒钟而不是每次击键时执行)。

延迟 WPF 绑定(bind)没有问题 - 您只需指定绑定(bind)的延迟: <TextBlock Text="{Binding Name, Delay=500}"/> .

每当我需要在使用 Caliburn.Micro 的 Message.Attach 的情况下延迟执行时,我通常以这种方式实现它(消息附加到带有 TextChanged 操作的 DoSomething 事件):

private int doingSomething;

public async void DoSomething()
{
    int current = ++doingSomething;
    await Task.Delay(500);
    if (current != doingSomething) //method was reentered
        return;

    await DoWorkCallServerEtc();
}

这很好用,但它不能很好地扩展并且违反了 DRY 原则(我需要在需要延迟的地方重新写一遍)。

我的问题是,我可以使用 Caliburn.Micro 以某种方式为此编写约定吗?
或者可能是一种不同的、更具可扩展性的方法?

最佳答案

正如您在 documentation page 中所读到的那样

Actions feature leverages System.Windows.Interactivity for it's trigger mechanism.

意思是

<TextBox Name="textBox" Margin="5"
    cal:Message.Attach="[Event TextChanged] = [Action DoAction(textBox.Text)]" />

相当于:

<TextBox Margin="5" Name="textBox">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="TextChanged">
            <cal:ActionMessage MethodName="DoAction">
                <cal:Parameter Value="{Binding ElementName=textBox, Path=Text}" />
            </cal:ActionMessage>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</TextBox>

负责执行 DoAction 方法的对象是 EventTrigger . EventTrigger 立即触发,没有延迟。所以我们需要创建自己的DelayedEventTrigger;类似的东西:

public class DelayedEventTrigger : System.Windows.Interactivity.EventTrigger
{
    private EventArgs args;
    private DispatcherTimer dispatcherTimer;

    public static readonly DependencyProperty DelayProperty =
        DependencyProperty.Register("Delay", typeof(int), typeof(DelayedEventTrigger), new PropertyMetadata(1000));

    public int Delay
    {
        get { return (int)base.GetValue(DelayProperty); }
        set { base.SetValue(DelayProperty, value); }
    }

    protected override void OnEvent(EventArgs eventArgs)
    {
        if (dispatcherTimer != null)
        {
            dispatcherTimer.Stop();
        }
        args = eventArgs;
        dispatcherTimer = new DispatcherTimer();
        dispatcherTimer.Interval = TimeSpan.FromMilliseconds(Delay);
        dispatcherTimer.Tick += new EventHandler(OnDispatcherTimerTick);
        dispatcherTimer.Start();
    }

    protected override void OnDetaching()
    {
        if (dispatcherTimer != null)
        {
            dispatcherTimer.Stop();
            dispatcherTimer = null;
        }
        base.OnDetaching();
    }

    private void OnDispatcherTimerTick(object sender, EventArgs e)
    {
        dispatcherTimer.Stop();
        InvokeActions(args);
    }
}

它的默认延迟是 1 秒(1000 毫秒)。所以现在我们可以在我们的 XAML 中使用它:

<TextBox Margin="5" Name="textBox">
    <i:Interaction.Triggers>
        <local:DelayedEventTrigger Delay="800" EventName="TextChanged">
            <cal:ActionMessage MethodName="DoDelayAction">
                <cal:Parameter Value="{Binding ElementName=textBox, Path=Text}" />
            </cal:ActionMessage>
        </local:DelayedEventTrigger>
    </i:Interaction.Triggers>
</TextBox>

在我看来,没有必要使用约定(您可以明确地使用 DelayedEventTrigger),但是如果您愿意,可以在您的 Bootstrapper 类中配置 Caliburn 解析器:

protected override void Configure()
{
    base.Configure();

    Parser.CreateTrigger = delegate(DependencyObject target, string triggerText)
    {
        System.Windows.Interactivity.EventTrigger eventTrigger;
        if (triggerText == null)
        {
            ElementConvention elementConvention = ConventionManager.GetElementConvention(target.GetType());
            return elementConvention.CreateTrigger();
        }
        string eventName = triggerText.Replace("[", String.Empty).Replace("]", String.Empty);
        if (eventName.StartsWith("Delayed", StringComparison.OrdinalIgnoreCase))
        {
            eventName = eventName.Replace("DelayedEvent", String.Empty).Trim();
            eventTrigger = new DelayedEventTrigger();
        }
        else
        {
            eventName = eventName.Replace("Event", String.Empty).Trim();
            eventTrigger = new System.Windows.Interactivity.EventTrigger();
        }

        eventTrigger.EventName = eventName;
        return eventTrigger;
    };     
}

通过添加此代码,您可以使用此约定:

<TextBox Name="textBox" Margin="5"
    cal:Message.Attach="[DelayedEvent TextChanged] = [Action DoDelayAction(textBox.Text)]" />

希望对您有所帮助。

关于c# - 我如何使用约定实现 Caliburn.Micro 操作的延迟,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35535702/

相关文章:

node.js - 构建异步 Express GET 请求的最佳方式

node.js - 顶级等待是否有超时?

c# - 我将如何同步运行异步 Task<T> 方法?

c# - MongoDB 哈希表平均值

c# - Newtonsoft.Json System.InvalidOperationException : Synchronous operations are disallowed

wpf - WPF窗口实现ISynchronizeInvoke以与System.Timers.Timer一起使用

c# - 将按钮控件嵌入到现有的 Direct3D 应用程序中

c# - 如何从sql连接查询中获取对象列表?

c# - 通过 LINQ 中的参数动态 "Not"(或与此相关的任何其他代码)

WPF:功能区控制应用程序按钮和快速访问工具栏位置