OK, thanks guys. Those were good.
I couldn't figure out how to bind the event handler ouside of startup, so I ended up going with a temp holder storing the identifier of which tb is actually changing.
so each tb changed event looked like this. At the start it checks what is being chaged to make sure it should run:
(this is for tb1)
string changing = MainDataSource.CreateNavigator().SelectSingleNode("/my:Form/my:changing";, NamespaceManager).Value;
if (e.Operation == XmlOperation.ValueChange && (changing == "" || changing == "tb1"))
{
MainDataSource.CreateNavigator().SelectSingleNode("/my:Form/my:changing";, NamespaceManager).SetValue("tb1");
MainDataSource.CreateNavigator().SelectSingleNode("/my:Form/my:tb2";, NamespaceManager).SetValue("my new value");
//tb2 changed event will not fire because my:changing is set to "tb1".
MainDataSource.CreateNavigator().SelectSingleNode("/my:Form/my:changing";, NamespaceManager).SetValue("");
}