I'm not bothering with comparing to previous lengths (as TextChanged doesn't compare to previous text as well).

The only complication comes from the fact that you have to unsubscribe the proper subscription (not just the last one), otherwise code would be almost as simple and short as observable version is:

private Dictionary<Action<int>, Action<string>> actions = new Dictionary<Action<int>, Action<string>>();

        public event Action<int> LengthChanged
        {
            add
            {
                Action<string> action = text => value(text == null ? 0 : text.Length);
                actions.Add(value, action);
                TextChanged += action;
            }
            remove
            {
                Action<string> action;
                if (actions.TryGetValue(value, out action))
                {
                    actions.Remove(value);
                    TextChanged -= action;
                }
            }
        }