Есть ли лучший способ избежать бесконечного цикла, используя winforms? - PullRequest
1 голос
/ 18 мая 2010

Я сейчас использую .Net 3.5.

Прямо сейчас я использую трюк using для отключения и включения событий вокруг определенных разделов кода. Пользователь может изменить либо дни, часы, минуты или общее количество минут, и это не должно вызывать бесконечный каскад событий (например, общее изменение минут, общее изменение минут и т. Д.). В то время как код выполняет то, что я хочу, может быть лучше / более прямой путь. Вы знаете кого-нибудь?

Для мускулистых очков:

Этот элемент управления будет использоваться несколькими командами - я не хочу смущать его. Я подозреваю, что мне не нужно заново изобретать колесо при определении часов в дне, дней в неделе и т. Д. Некоторые другие стандартные библиотеки .Net там должны иметь его. Любые другие замечания относительно кода? Это using (EventHacker.DisableEvents(this)) бизнес - это должно быть обычным делом в .Net ... временное изменение настроек. Как это называется? Я хотел бы иметь возможность ссылаться на него в комментарии, а также читать больше о текущих реализациях. В общем случае необходимо запомнить не только дескриптор изменяемой вещи, но и предыдущее состояние (в этом случае предыдущее состояние не имеет значения - события включаются и выключаются безоговорочно). Тогда есть также возможность multi-threaded hacking. Можно также использовать дженерики, чтобы сделать код, возможно, чище. Выяснение всего этого может привести к многостраничному сообщению в блоге. Я был бы рад услышать некоторые ответы.

P.S. Кажется ли, что я страдаю от обсессивно-компульсивного расстройства? Некоторые люди любят закончить дела и двигаться дальше; Мне нравится держать их открытыми ... всегда есть лучший способ.

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

2 голосов
/ 18 мая 2010

Это не выглядит хорошо. Клиентский код не знает, почему он отключает события в частном элементе управления, который он не видит. Также нет способа увидеть, что публичная собственность изменилась, нет событий, чтобы сказать, что публичная собственность изменила значение. Если бы он хотел игнорировать события изменения, он просто отменил бы подписку или проигнорировал эти (отсутствующие) события.

Рассмотрим элементы управления .NET Framework как руководство. Ни у одного из них нет ничего похожего на IEventHackable.

2 голосов
/ 18 мая 2010

Я бы использовал логическое поле, чтобы остановить выполнение метода 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;
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...