Here is a proof in a more 'constructive' form that can be directly applied to any real-world programming language. Just to make clear, a property of programs must be a predicate that only depends on the halting and output behaviour, in other words two equivalent programs will have exactly the same properties. I will use $X \equiv Y$ to denote that two programs are equivalent, and so $P$ is a property iff $P$ is a predicate and $P(X)=P(Y)$ for any programs $X \equiv Y$.

For any property $P$ of programs such that $P(T)$ and $\neg P(F)$ for some programs $T$,$F$:

If $P$ is decidable:

Let $D$ be a program that decides $P$

Let $M$ be the following program on input $x$:

Create $N$ to be the following program on input $y$:

If $x$ is an invalid program: Return "" [may be unnecessary in some languages]

Return $x(x)(y)$ [using closures or some interpreter written in the language itself]

If D(N): Return F

Return T

Let $R$ be the program that $M(M)$ creates in the variable $N$

Clearly $R \equiv M(M)$

Also $M(M)$ halts because $D$ halts on all inputs

Thus $M(M) \equiv T \vee M(M) \equiv F$

$P(M(M)) \Leftrightarrow P(R) \Leftrightarrow D(R) = True \Leftrightarrow M(M) \equiv F \Leftrightarrow \neg P(M(M))$

Contradiction

Therefore $P$ is not decidable