Answering the newly rephrased question...
So I do this all the time. I use the page object model to write automation so this works well for that but you can still use it even if you aren't using the page object model.
The basic concept is this... you find an element that loads last and wait for that element. At that point, you know that the page is done loading and your script can continue without fear of interacting with some element on the page that hasn't finished loading.
The caveat... how in the world do I know which element is the last to load? I have no idea. You basically make a guess (hopefully educated guess) and use it until it fails. If your page isn't terribly dynamic, then just choose about any element. If there's a part of the page that loads late, choose an element inside that portion of the page.
Here's how this typically goes for me. I write a page object for some page. I pick a unique element on the page and declare that as By waitForLocator. Now when I want to wait for the page to finish loading I call
By waitForLocator = By.Id("myId");
new WebDriverWait(Driver, TimeSpan.FromSeconds(10)).Until(ExpectedConditions.ElementIsVisible(waitForLocator));
Now I'm (fairly) confident that the page is done loading and I go about writing methods for the page object and so on. I write a script that consumes that page object and interact with the page according to my test case and then I run the script. Hopefully I picked my waitForLocator well. If I haven't, at some point during the script run I'll get some exception because some element wasn't fully loaded (it loaded after the element I picked). So, I change the waitForElement to the element that I just interacted with that threw the exception and now I should have a better guess at the last loading element. You basically repeat this process until you get no more exceptions.
You might say, this is a very haphazard way to do this. It really seems more haphazard than it really is. If you know your site and your pages, you will make a pretty good first guess. It's pretty rare that I have to change that guess once and it's REALLY rare that I have to change it more than once. That said, changing it is really easy. You already have the locator for the element that threw the exception, you just paste it into the declaration for waitForLocator and you're done.
So one exception that you might run into is if you've properly chosen your waitForLocator and after the page has loaded, you click on some element on the page which dynamically loads some other portion of the page. This scenario is beyond the scope of waiting for the page to load. In this case, you would have to build in another wait that waits for the newly loading portion of the page to finish loading after a click is performed. It will not affect the page loading mechanism.
Additional comments
After going back and reading your question, here's some additional info that will make your life easier.
Your original question had a method that I've simplified/modified below to demonstrate this principle
public void ClickElementById(RemoteWebDriver driver, string id)
{
driver.FindElement(By.Id(id)).Click();
}
The way this method is written, you forced yourself to create a method for each locator type, ID, name, CSS selector, XPath, etc. Rather than pass in a string, pass in a By locator.
public void ClickElement(RemoteWebDriver driver, By locator)
{
driver.FindElement(locator).Click();
}
Now with this new method, it's completely flexible. You can pass in any locator type and it will work. To call it you use something like
ClickElement(driver, By.Id("someId"));