Pregunta

In C#, events can only be fired by the owning class through protected virtual methods such as OnClick, OnExit, etc... What I'm trying to do is implementing GOF State Pattern for some of my complex classes. Everything worked great so far except that I can't find a way to fire an event of the owning class from my state class.

For example, my button has two states (up and down) and the DownState class will check for user input and if necessary, fire the button's Click event.

public class Button
{
    public Button()
    {
        State = new ButtonStateNormal(this);
    }

    public event EventHandler<MouseEventArgs> Click;
    public ButtonState State { get; set; }

    protected virtual void OnClick(MouseEventArgs e)
    {
        if (Click != null)
            Click(this, e);
    }
}

public class ButtonStateNormal : ButtonState
{
    Button button;
    public ButtonStateNormal(Button button)
    {
        this.button = button;
    }

    public override void CheckForInput()
    {
        //...
        //Check for user input and change the state
        //...
        button.State = new ButtonStateDown(button);
    }
}

public class ButtonStateDown : ButtonState
{
    Button button;
    public ButtonStateDown(Button button)
    {
        this.button = button;
    }

    public override void CheckForInput()
    {
        //...
        //Check for user input, fire the event of the button and change the state
        //...
        button.OnClick(new MouseEventArgs(mouse.X, mouse.Y)); //I want to do this, obviously it won't work this way
        button.State = new ButtonStateNormal(button);
    }
}

How can I do that?

¿Fue útil?

Solución

Set up an event or delegate on your ButtonState class, that your buttons thus can call. Change your automatic State property in Button to one with a backing store that hooks and unhooks the this event or delegate whenever the state is changed.

public class Button
{
    public Button()
    {
        State = new ButtonStateNormal(this);
    }
    private ButtonState _state;
    public event EventHandler<MouseEventArgs> Click;
    public ButtonState State
    {
        protected get
        {
            return _state;
        }
        set
        {
            if (_state == value)
                return;
            if (_state != null)
                _state.Click -= OnClick;
            if (value != null)
                value.Click += OnClick;

            _state = value;

        }
    }

    protected virtual void OnClick(MouseEventArgs e)
    {
        if (Click != null)
            Click(this, e);
    }

    public void CheckForInput(){
        State.CheckForInput();
    }
}

public abstract class ButtonState
{
    public abstract void CheckForInput();
    public ClickDelegate Click;
    public delegate void ClickDelegate(MouseEventArgs a);
}

And then you can do this in your concrete State classes

public class ButtonStateDown : ButtonState
{
    Button button;
    public ButtonStateDown(Button button)
    {
        this.button = button;
    }

    public void CheckForInput()
    {
        //...
        //Check for user input, fire the event of the button and change the state
        //...
        if(Click != null)
            Click(new MouseEventArgs(mouse.X, mouse.Y))
        button.State = new ButtonStateNormal(button);
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top