代码之家  ›  专栏  ›  技术社区  ›  Hamish Grubijan

有没有更好的方法来避免使用winforms的无限循环?

  •  1
  • Hamish Grubijan  · 技术社区  · 15 年前

    我现在正在使用.NET 3.5。

    现在我用的是 using 禁用和启用某些代码段周围的事件的技巧。用户可以更改天、小时、分钟或总分钟数,这不应导致事件的无限级联(例如分钟更改总分钟数、总更改分钟数等),而代码执行我所希望的操作,可能会有更好/更直接的方法。你知道吗?

    对于强壮的点:

    此控件将由多个团队使用-我不想让它难堪。我怀疑在定义一天中的时间、一周中的几天等等时,我不需要重新设计轮子。外面的一些其他标准.NET库必须有它。关于代码还有什么意见吗?这个 using (EventHacker.DisableEvents(this)) 业务-这必须是.NET中的常见模式…临时更改设置。它叫什么名字?我希望能够在评论中引用它,并且能够阅读更多关于当前实现的内容。在一般情况下,不仅需要记住要更改的对象的句柄,还需要记住以前的状态(在这种情况下,以前的状态不重要-事件是无条件地打开和关闭的)。也有可能 multi-threaded hacking . 人们还可以利用泛型使代码更干净。弄清楚这些可能会导致一篇多页的博客文章。我很高兴听到一些答案。

    另外,我似乎患有强迫症吗?有些人喜欢把事情完成,然后继续前进;我喜欢保持开放……总有更好的方法。

    // Corresponding Designer class is omitted.
    using System;
    using System.Windows.Forms;
    
    namespace XYZ // Real name masked
    {
        interface IEventHackable
        {
            void EnableEvents();
            void DisableEvents();
        }
    
        public partial class PollingIntervalGroupBox : GroupBox, IEventHackable
        {
            private const int DAYS_IN_WEEK      = 7;
            private const int MINUTES_IN_HOUR   = 60;
            private const int HOURS_IN_DAY      = 24;
            private const int MINUTES_IN_DAY    = MINUTES_IN_HOUR * HOURS_IN_DAY;
            private const int MAX_TOTAL_DAYS    = 100;
    
            private static readonly decimal MIN_TOTAL_NUM_MINUTES = 1; // Anything faster than once per minute can bog down our servers.
            private static readonly decimal MAX_TOTAL_NUM_MINUTES = (MAX_TOTAL_DAYS * MINUTES_IN_DAY) - 1; // 99 days should be plenty.
            // The value above was chosen so to not cause an overflow exception.
            // Watch out for it - numericUpDownControls each have a MaximumValue setting.
    
            public PollingIntervalGroupBox()
            {
                InitializeComponent();
    
                InitializeComponentCustom();
            }
    
            private void InitializeComponentCustom()
            {
                this.m_upDownDays.Maximum           = MAX_TOTAL_DAYS    - 1;
                this.m_upDownHours.Maximum          = HOURS_IN_DAY      - 1;
                this.m_upDownMinutes.Maximum        = MINUTES_IN_HOUR   - 1;
                this.m_upDownTotalMinutes.Maximum   = MAX_TOTAL_NUM_MINUTES;
                this.m_upDownTotalMinutes.Minimum   = MIN_TOTAL_NUM_MINUTES;
            }
    
            private void m_upDownTotalMinutes_ValueChanged(object sender, EventArgs e)
            {
                setTotalMinutes(this.m_upDownTotalMinutes.Value);
            }
    
            private void m_upDownDays_ValueChanged(object sender, EventArgs e)
            {
                updateTotalMinutes();
            }
    
            private void m_upDownHours_ValueChanged(object sender, EventArgs e)
            {
                updateTotalMinutes();
            }
    
            private void m_upDownMinutes_ValueChanged(object sender, EventArgs e)
            {
                updateTotalMinutes();
            }
    
            private void updateTotalMinutes()
            {
                this.setTotalMinutes(
                    MINUTES_IN_DAY * m_upDownDays.Value + 
                    MINUTES_IN_HOUR * m_upDownHours.Value + 
                    m_upDownMinutes.Value);
            }
    
            public decimal TotalMinutes
            {
                get
                {
                    return m_upDownTotalMinutes.Value;
                }
                set
                {
                    m_upDownTotalMinutes.Value = value;
                }
            }
    
            public decimal TotalHours
            {
                set
                {
                    setTotalMinutes(value * MINUTES_IN_HOUR);
                }
            }
    
            public decimal TotalDays
            {
                set
                {
                    setTotalMinutes(value * MINUTES_IN_DAY);
                }
            }
    
            public decimal TotalWeeks
            {
                set
                {
                    setTotalMinutes(value * DAYS_IN_WEEK * MINUTES_IN_DAY);
                }
            }
    
            private void setTotalMinutes(decimal nTotalMinutes)
            {
                if (nTotalMinutes < MIN_TOTAL_NUM_MINUTES)
                {
                    setTotalMinutes(MIN_TOTAL_NUM_MINUTES);
                    return; // Must be carefull with recursion.
                }
                if (nTotalMinutes > MAX_TOTAL_NUM_MINUTES)
                {
                    setTotalMinutes(MAX_TOTAL_NUM_MINUTES);
                    return; // Must be carefull with recursion.
                }
                using (EventHacker.DisableEvents(this))
                {
                    // First set the total minutes
                    this.m_upDownTotalMinutes.Value = nTotalMinutes;
    
                    // Then set the rest
                    this.m_upDownDays.Value = (int)(nTotalMinutes / MINUTES_IN_DAY);
                    nTotalMinutes = nTotalMinutes % MINUTES_IN_DAY; // variable reuse.
                    this.m_upDownHours.Value = (int)(nTotalMinutes / MINUTES_IN_HOUR);
                    nTotalMinutes = nTotalMinutes % MINUTES_IN_HOUR;
                    this.m_upDownMinutes.Value = nTotalMinutes;
                }
            }
    
            // Event magic
            public void EnableEvents()
            {
                this.m_upDownTotalMinutes.ValueChanged += this.m_upDownTotalMinutes_ValueChanged;
                this.m_upDownDays.ValueChanged += this.m_upDownDays_ValueChanged;
                this.m_upDownHours.ValueChanged += this.m_upDownHours_ValueChanged;
                this.m_upDownMinutes.ValueChanged += this.m_upDownMinutes_ValueChanged;
            }
    
            public void DisableEvents()
            {
                this.m_upDownTotalMinutes.ValueChanged -= this.m_upDownTotalMinutes_ValueChanged;
                this.m_upDownDays.ValueChanged -= this.m_upDownDays_ValueChanged;
                this.m_upDownHours.ValueChanged -= this.m_upDownHours_ValueChanged;
                this.m_upDownMinutes.ValueChanged -= this.m_upDownMinutes_ValueChanged;
            }
    
            // We give as little info as possible to the 'hacker'.
            private sealed class EventHacker : IDisposable
            {
                IEventHackable _hackableHandle;
    
                public static IDisposable DisableEvents(IEventHackable hackableHandle)
                {
                    return new EventHacker(hackableHandle);
                }
    
                public EventHacker(IEventHackable hackableHandle)
                {
                    this._hackableHandle = hackableHandle;
                    this._hackableHandle.DisableEvents();
                }
    
                public void Dispose()
                {
                    this._hackableHandle.EnableEvents();
                }
            }
        }
    }
    
    2 回复  |  直到 11 年前
        1
  •  2
  •   Jens Granlund    15 年前

    我将使用布尔字段停止 setTotalMinutes 方法,并将事件处理程序的创建移动到 InitializeComponentCustom 方法。

    像这样:

    public partial class PollingIntervalGroupBox : GroupBox
    {
        private const int DAYS_IN_WEEK = 7;
        private const int MINUTES_IN_HOUR = 60;
        private const int HOURS_IN_DAY = 24;
        private const int MINUTES_IN_DAY = MINUTES_IN_HOUR * HOURS_IN_DAY;
        private const int MAX_TOTAL_DAYS = 100;
    
        private static readonly decimal MIN_TOTAL_NUM_MINUTES = 1; // Anything faster than once per minute can bog down our servers.
        private static readonly decimal MAX_TOTAL_NUM_MINUTES = (MAX_TOTAL_DAYS * MINUTES_IN_DAY) - 1; // 99 days should be plenty.
        // The value above was chosen so to not cause an overflow exception.
        // Watch out for it - numericUpDownControls each have a MaximumValue setting.
        private bool _totalMinutesChanging;
    
        public PollingIntervalGroupBox()
        {
            InitializeComponent();
            InitializeComponentCustom();
        }
    
        private void InitializeComponentCustom()
        {
            this.m_upDownDays.Maximum = MAX_TOTAL_DAYS - 1;
            this.m_upDownHours.Maximum = HOURS_IN_DAY - 1;
            this.m_upDownMinutes.Maximum = MINUTES_IN_HOUR - 1;
            this.m_upDownTotalMinutes.Maximum = MAX_TOTAL_NUM_MINUTES;
            this.m_upDownTotalMinutes.Minimum = MIN_TOTAL_NUM_MINUTES;
    
            this.m_upDownTotalMinutes.ValueChanged += this.m_upDownTotalMinutes_ValueChanged;
            this.m_upDownDays.ValueChanged += this.m_upDownDays_ValueChanged;
            this.m_upDownHours.ValueChanged += this.m_upDownHours_ValueChanged;
            this.m_upDownMinutes.ValueChanged += this.m_upDownMinutes_ValueChanged;
        }
    
        private void m_upDownTotalMinutes_ValueChanged(object sender, EventArgs e)
        {
            setTotalMinutes(this.m_upDownTotalMinutes.Value);
        }
    
        private void m_upDownDays_ValueChanged(object sender, EventArgs e)
        {
            updateTotalMinutes();
        }
    
        private void m_upDownHours_ValueChanged(object sender, EventArgs e)
        {
            updateTotalMinutes();
        }
    
        private void m_upDownMinutes_ValueChanged(object sender, EventArgs e)
        {
            updateTotalMinutes();
        }
    
        private void updateTotalMinutes()
        {
            this.setTotalMinutes(
                MINUTES_IN_DAY * m_upDownDays.Value +
                MINUTES_IN_HOUR * m_upDownHours.Value +
                m_upDownMinutes.Value);
        }
    
        public decimal TotalMinutes { get { return m_upDownTotalMinutes.Value; } set { m_upDownTotalMinutes.Value = value; } }
    
        public decimal TotalHours { set { setTotalMinutes(value * MINUTES_IN_HOUR); } }
    
        public decimal TotalDays { set { setTotalMinutes(value * MINUTES_IN_DAY); } }
    
        public decimal TotalWeeks { set { setTotalMinutes(value * DAYS_IN_WEEK * MINUTES_IN_DAY); } }
    
        private void setTotalMinutes(decimal totalMinutes)
        {
            if (_totalMinutesChanging) return;
            try
            {
                _totalMinutesChanging = true;
                decimal nTotalMinutes = totalMinutes;
                if (totalMinutes < MIN_TOTAL_NUM_MINUTES)
                {
                    nTotalMinutes = MIN_TOTAL_NUM_MINUTES;
                }
                if (totalMinutes > MAX_TOTAL_NUM_MINUTES)
                {
                    nTotalMinutes = MAX_TOTAL_NUM_MINUTES;
                }
                // First set the total minutes
                this.m_upDownTotalMinutes.Value = nTotalMinutes;
    
                // Then set the rest
                this.m_upDownDays.Value = (int)(nTotalMinutes / MINUTES_IN_DAY);
                nTotalMinutes = nTotalMinutes % MINUTES_IN_DAY; // variable reuse.
                this.m_upDownHours.Value = (int)(nTotalMinutes / MINUTES_IN_HOUR);
                nTotalMinutes = nTotalMinutes % MINUTES_IN_HOUR;
                this.m_upDownMinutes.Value = nTotalMinutes;
            }
            finally
            {
                _totalMinutesChanging = false;
            }
        }
    }
    
        2
  •  2
  •   Hans Passant    15 年前

    这个看起来不太好。客户机代码不知道为什么它会禁用它看不到的私有控件上的事件。它也没有办法看到公共财产发生了变化,也没有任何事件表明公共财产改变了价值。如果它想忽略变更事件,那么它只需取消订阅或忽略那些(丢失的)事件。

    以.NET框架控件为指南。它们都没有类似于IEventhackable的东西。

    推荐文章