Du kannst im CodeBehind die DataBinding-Vorteile nutzen, indem Du INotifyPropertyChanged im CodeBehind implementierst und den DataContext auf this setzt.
Allerdings ist dein Control dann einfach nur Control und ViewModel in Einem, Du hast also weiterhin MVVM, nur in verkrüppelt
Da ist nichts tricky, es ist nur unsauber - oder Du meinst etwas anderes?
Wenn Du im CodeBehind auf Property-Änderungen vom DataContext (ViewModel) reagieren willst, ja, dann ist es tricky
In dem Fall bleibt dir nur, das PropertyChanged-Event zu abonnieren und darauf zu reagieren. Und wenn es mehrere Properties nacheinander sind ... mein Beileid
Oder Du machst es nicht im CodeBehind, dann hast Du das Problem nicht.
CodeBehind ist nicht automatisch falsch, es ist nur dann falsch, wenn es ins ViewModel gehört - leichter gesagt, als getan.
Ich persönlich schreibe nur ganz selten was ins CodeBehind.
Meistens arbeite ich mit Bindings und Triggern (das ViewModel kann die Grundlage liefern) oder Behaviors.
Wenn das auch nicht geht, ist ggf. ein CustomControl eine gute Idee, gerade für mehrfach genutzte und komplexere Controls oder wenn man ein eigenes Template braucht, ist das das Mittel der Wahl.
In den Fällen, wo das alles nicht geht und die Logik wirklich ausschließlich WPF spezifische UI-Logik ist und konkreten Control-Zugang benötigt, dann kommt es in den CodeBehind und dann ist es auch in Ordnung.
Manche schreiben auch im CodeBehind, wenn eine saubere Lösung zu aufwändig wäre, aber damit sollte man immer vorsichtig sein, solche "Lösungen" neigen dazu, auf Füße zu fallen
Je nachdem, was Du vor hast, bringt das der CodeBehind auch Nachteile mit sich, wie z.B. dass es ziemlich nervig ist, auf sich ändernde ViewModel-Properties zu reagieren.