I simplified the code a lot by adding a Ball class and using the Vector2 Struct from the System.Numerics Namespace (I have included a minimal implementation for mono below). Vector2 contains  useful methods and operators for vector math. E.g., you can add two vectors with Vector2 result = v1 + v2.
The Ball class wraps all the state of a ball and some methods like CollideWithWall. The advantage is that we have to write this code only once for all the balls. Ball now stores the center coordinates of the ball, not the top left position. This makes it easier to reason about it. It also stores the radius, allowing us to have balls of different radii.
For the collision I found a working solution from the user mmcdole. I adapted it to C# and your simulation. But the core of your simulation, the integration of the speeds to get the motion, remains the same.
public class Ball
{
    public Brush Brush { get; set; }
    public Vector2 Center { get; set; }
    public Vector2 Velocity { get; set; }
    public float Radius { get; set; }
    // Make mass proportional to the area of the circle
    public float Mass => Radius * Radius;
    public void Move()
    {
        Center += Velocity;
    }
    public void CollideWithWall(Rectangle wall)
    {
        // Only reverse velocity if moving towards the walls
        if (Center.X + Radius >= wall.Right && Velocity.X > 0 || Center.X - Radius < 0 && Velocity.X < 0) {
            Velocity = new Vector2(-Velocity.X, Velocity.Y);
        }
        if (Center.Y + Radius >= wall.Bottom && Velocity.Y > 0 || Center.Y - Radius < 0 && Velocity.Y < 0) {
            Velocity = new Vector2(Velocity.X, -Velocity.Y);
        }
    }
    public void CollideWith(Ball other)
    {
        // From: https://stackoverflow.com/q/345838/880990, author: mmcdole
        Vector2 delta = Center - other.Center;
        float d = delta.Length();
        if (d <= Radius + other.Radius && d > 1e-5) {
            // Minimum translation distance to push balls apart after intersecting
            Vector2 mtd = delta * ((Radius + other.Radius - d) / d);
            // Resolve intersection - inverse mass quantities
            float im1 = 1 / Mass;
            float im2 = 1 / other.Mass;
            // Push-pull them apart based off their mass
            Center += mtd * (im1 / (im1 + im2));
            other.Center -= mtd * (im2 / (im1 + im2));
            // Impact speed
            Vector2 v = Velocity - other.Velocity;
            Vector2 mtdNormalized = Vector2.Normalize(mtd);
            float vn = Vector2.Dot(v, mtdNormalized);
            // Sphere intersecting but moving away from each other already
            if (vn > 0.0f) return;
            // Collision impulse
            const float Restitution = 1.0f; //  perfectly elastic collision
            float i = -(1.0f + Restitution) * vn / (im1 + im2);
            Vector2 impulse = mtdNormalized * i;
            // Change in momentum
            Velocity += impulse * im1;
            other.Velocity -= impulse * im2;
        }
    }
    public void Draw(Graphics g)
    {
        g.FillEllipse(Brush, Center.X - Radius, Center.Y - Radius, 2 * Radius, 2 * Radius);
    }
}
then we can initialize the form with (in Form1)
Ball a = new Ball() {
    Brush = Brushes.Red,
    Center = new Vector2(),
    Velocity = new Vector2(2, 2),
    Radius = 25
};
Ball b = new Ball() {
    Brush = Brushes.Blue,
    Center = new Vector2(),
    Velocity = new Vector2(-2, -2),
    Radius = 40
};
public Form1()
{
    InitializeComponent();
    DoubleBuffered = true;
    Load += Form1_Load; ;
    Paint += Form1_Paint;
    var refreshTimer = new System.Windows.Forms.Timer {
        Interval = 1
    };
    refreshTimer.Tick += RefreshTimer_Tick;
    refreshTimer.Start();
}
void Form1_Load(object sender, EventArgs e)
{
    WindowState = FormWindowState.Normal;
    System.Diagnostics.Debug.WriteLine(Width);
    b.Center = new Vector2(Width - 60, Height - 60);
}
private void RefreshTimer_Tick(object sender, EventArgs e)
{
    Invalidate();
}
Our Paint method now looks like this:
private void Form1_Paint(object sender, PaintEventArgs e)
{
    Graphics g = e.Graphics;
    g.FillRectangle(Brushes.LightBlue, ClientRectangle);
    a.Draw(g);
    b.Draw(g);
    a.Move();
    b.Move();
    a.CollideWithWall(ClientRectangle);
    b.CollideWithWall(ClientRectangle);
    a.CollideWith(b);
}
If you want to change the form properties in the forms designer, then you must also call InitializeComponent(); in the form's constructor.
EDIT:
Since you are using mono that does not have the Vextor2 struct, here is a minimal version of it, implementing only the required stuff for the code above:
public struct Vector2
{
    public float X;
    public float Y;
    public Vector2(float x, float y)
    {
        X = x;
        Y = y;
    }
    public static Vector2 operator +(Vector2 left, Vector2 right)
    {
        return new Vector2(left.X + right.X, left.Y + right.Y);
    }
    public static Vector2 operator -(Vector2 left, Vector2 right)
    {
        return new Vector2(left.X - right.X, left.Y - right.Y);
    }
    public static Vector2 operator *(Vector2 left, Vector2 right)
    {
        return new Vector2(left.X * right.X, left.Y * right.Y);
    }
    public static Vector2 operator *(float left, Vector2 right)
    {
        return new Vector2(left * right.X, left * right.Y);
    }
    public static Vector2 operator *(Vector2 left, float right)
    {
        return new Vector2(left.X * right, left.Y * right);
    }
    public static float Dot(Vector2 value1, Vector2 value2)
    {
        return value1.X * value2.X + value1.Y * value2.Y;
    }
    public static Vector2 Normalize(Vector2 value)
    {
        float d = MathF.Sqrt(value.X * value.X + value.Y * value.Y);
        if (d < 1e-10) {
            return value;
        }
        float invNorm = 1.0f / d;
        return new Vector2(value.X * invNorm, value.Y * invNorm);
    }
    public float Length()
    {
        return MathF.Sqrt(X * X + Y * Y);
    }
}
Explanation
I am not going to explain the collision itself. Follow the link to mmcdole's code for this.
You are using a lot of variables like xpa, ypa, xva, yva, xpb, ypb, xvb, yvb. Most of the changes I've made are to reduce the number of variables and avoid code duplication.
For instance we have float xpa and float ypa storing the position of object a. The Vector2 type stores both coordinates in its X and Y fields and requires only one variable. It also contains methods and operator overloads that allow performing arithmetic operations on them.
Example:
// Old code with individual floats
float xpa = 0;
float ypa = 0;
float xva = 2;
float yva = 2;
...
xpa += xva;
ypa += yva;
// New code with Vector2
Vector2 pa = new Vector2(0, 0);
Vector2 va = new Vector2(2, 2);
...
pa += va;
Another problem is that a lot of code is duplicated because it must be applied to the <whatever>a variables and the <whatever>b variables. Especially in the Form1_Paint method.
The idea is to wrap all the variables belonging to a ball in a Ball object (declared as class). Inside this object the variables (or properties with { get; set; }) have the same name, no matter whether the object represents the a ball or the b ball.
Methods inside this Ball class now work with the object's properties.
Example:
public void Draw(Graphics g)
{
    g.FillEllipse(Brush, Center.X - Radius, Center.Y - Radius, 2 * Radius, 2 * Radius);
}
It uses the Brush, Center and Radius properties of the object. I decided to store the color of the ball as Brush, since FillEllipse requires a brush.
From outside, if we have two balls called a and b, we can draw them with the calls:
a.Draw(g);
b.Draw(g);
One code duplication eliminated! The same applies to Move, CollideWithWall and CollideWith (colliding with another ball).
This code works like yours, except for the ball-ball collision.
See also: