【问题标题】:How can I implement a delay with Caliburn.Micro actions using a convention如何使用约定实现 Caliburn.Micro 操作的延迟
【发布时间】:2016-06-02 19:41:28
【问题描述】:

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

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

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

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 以某种方式为此编写一个约定吗?
或者也许是一种不同的、更具可扩展性的方法?

【问题讨论】:

    标签: c# wpf async-await caliburn.micro


    【解决方案1】:

    正如您在documentation page 中看到的那样

    Actions 功能利用 System.Windows.Interactivity 触发机制。

    意思是

    <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 方法的对象是EventTriggerEventTrigger 立即触发,没有延迟。所以我们需要创建自己的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 Parser:

    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)]" />
    

    希望对你有帮助。

    【讨论】:

    • 我无语了..谢谢!我特别喜欢你从 在我看来没有必要使用约定.. 然后 BOOM 无论如何这里有一个约定.. :-)
    猜你喜欢
    • 2016-04-04
    • 2011-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多