Ayende @ Rahien

Hi!
My name is Oren Eini
Founder of Hibernating Rhinos LTD and RavenDB.
You can reach me by phone or email:

ayende@ayende.com

+972 52-548-6969

, @ Q c

Posts: 18 | Comments: 87

filter by tags archive

Conditions and facts

time to read 2 min | 373 words

When working with UI, quite often some part of the UI is dependant on some condition. The problem is that this condition is often subject to change, and we need some way of dealing with those changes. That is why I created the following class:

public class Fact : INotifyPropertyChanged
{
	private readonly Func<bool> predicate;

	public Fact(INotifyPropertyChanged observable, Func<bool> predicate)
	{
		this.predicate = predicate;
		observable.PropertyChanged += (sender, args) =>
		                              PropertyChanged(this, new PropertyChangedEventArgs("Value"));
	}

	public bool Value
	{
		get
		{
			return predicate();
		}
	}

	public event PropertyChangedEventHandler PropertyChanged = delegate { };
}

While it doesn’t looks like much, it does offer me a lot of value in terms of simplifying conditional logic. For example:

public Fact CanMovePrev
{
	get { return new Fact(CurrentPage, () => CurrentPage > 0); }
}

public void OnMovePrev()
{
	LoadPage(CurrentPage - 1);
}

Where CurrentPage is an Observable<T>, which I discussed previously.

My infrastructure knows about the naming convention and will automatically generate a Command and bind it to the MovePrev action, including both the action to execute upon commit and whatever or not we can execute this. For that matter, it will also gracefully handle can execute changes very easily. Here is the code to actually do that (I’ll spare you the convention based wiring code):

public class DelegatingCommand : ICommand
{
	private readonly Action action;
	private readonly Fact canExecute;

	public DelegatingCommand(Action action, Fact canExecute)
	{
		this.action = action;
		this.canExecute = canExecute;
		var dispatcher = Dispatcher.CurrentDispatcher;
		if (canExecute != null)
		{
			this.canExecute.PropertyChanged +=
				(sender, args) =>
					dispatcher.Invoke(CanExecuteChanged, this, EventArgs.Empty);
		}
	}

	public void Execute(object parameter)
	{
		action();
	}

	public bool CanExecute(object parameter)
	{
		if (canExecute == null)
			return true;
		return canExecute.Value;
	}

	public event EventHandler CanExecuteChanged = delegate { };
}

Tada! :-)


Comments

Stephen

I like it, you basically encapsulate a condition and manage handling its invalidation. Although I'm not sure you need fact to be INotifyPropertyChanged, you could well just have a bog standard 'Changed' event.

Fact is nice and short as a class name but something niggles at me regarding a fact changing.. to me its more like an ObservableCondition, but obviously that doesnt sound as good ;).

Michael L Perry

What if the condition is based on more than one observable? Say for example, CurrentPage < TotalPages-1? How would you subscribe to both?

Ayende Rahien

Stephen,

Fact implementing INPC means that I can bind directly to it

Ayende Rahien

Michael,

CompositeObservable to to rescue

Peter Morris

I used this:

public class ActionCommand : ICommand

{

    readonly Action

<object Action;

    public event EventHandler CanExecuteChanged;


    public ActionCommand(Action

<object action)

    {

        Action = action;

        Enabled = true;

    }


    bool enabled;

    public bool Enabled

    {

        get { return enabled; }

        set

        {

            enabled = value;

            var canExecuteChanged = CanExecuteChanged;

            if (canExecuteChanged != null)

                canExecuteChanged(this, EventArgs.Empty);

        }

    }


    public bool CanExecute(object parameter)

    {

        return Enabled;

    }


    public void Execute(object parameter)

    {

        if (Action != null)

            Action(parameter);

    }

}
Mike Minutillo

Could you possibly make the Command code even simpler if you used the Null Value Pattern to have your infrastructure insert a Fact that is always true instead of checking for null all over?

Ayende Rahien

Mike,

I wouldn't call 2 if statements all over, but yes, I could have done that

Comment preview

Comments have been closed on this topic.

FUTURE POSTS

  1. Buffer allocation strategies: A possible solution - 2 days from now
  2. Buffer allocation strategies: Explaining the solution - 3 days from now
  3. Buffer allocation strategies: Bad usage patterns - 4 days from now
  4. The useless text book algorithms - 5 days from now
  5. Find the bug: The concurrent memory buster - 6 days from now

There are posts all the way to Sep 11, 2015

RECENT SERIES

  1. Find the bug (5):
    20 Apr 2011 - Why do I get a Null Reference Exception?
  2. Production postmortem (10):
    03 Sep 2015 - The industry at large
  3. What is new in RavenDB 3.5 (7):
    12 Aug 2015 - Monitoring support
  4. Career planning (6):
    24 Jul 2015 - The immortal choices aren't
View all series

Syndication

Main feed Feed Stats
Comments feed   Comments Feed Stats