Search code examples
c#appium

C# Appium/Selenium Derive WindowsElement throws error on cast


In my C# .NET Framework 4.8 project I'm trying to derive the WindowsElement class to implement some additional functions in the new class. For example ClickInMiddleOfControl, IsVisible or SelectedItem (in a ComboBox).

public class WindowsElementWrapper : WindowsElement
{
    public WindowsElementWrapper(RemoteWebDriver parent, string id) : base(parent, id) { }

    public void ClickInMiddleOfControl()
    {
        var rect = this.Rect;
        var offsetx = (int)(0.5 * rect.Width);
        var offsety = (int)(0.5 * rect.Height);
        new Actions(Helper.Session)
            .MoveToElement(this, 0, 0)
            .MoveByOffset(offsetx, offsety)
            .Click()
            .Build()
            .Perform();
    }
}

I then try to cast a WindowsElement to my WindowsElementWrapper.

var element = (WindowsElementWrapper)Session.FindElementByName("ElementName");
element.ClickInMiddleOfControl();

But on runtime I get the following error message:

System.InvalidCastException: 'Unable to cast object of type OpenQA.Selenium.Appium.Windows.WindowsElement' to type 'WEW.WindowsElementWrapper'.'

Is it not possible to derive from the WindowsElement class? Or am I making a fundamental mistake?


Solution

  • A cast from WindowsElement to WindowsElementWrapper is a downcast.

    In order for a downcast to succeed, the runtime-type of the object has to be of the target-type itself, or a type derived from it.

    Although a WindowsElementWrapper is a WindowsElement, a WindowsElement does not have to be a WindowsElementWrapper.

    In other words, this downcast can never succeed, unless the SeleniumAPI instantiates the object as a WindowsElementWrapper to begin with.

    In order to achieve what you want to do, you may apply one of these designs:


    Composition

    public class WindowsElementWrapper
    {
       private readonly WindowsElement element;
    
       public WindowsElementWrapper(WindowsElement element, (int Width, int Height) rect)
       {
          this.element = element;
          Rect = rect;
       }
    
       public (int Width, int Height) Rect { get; init; }
    
       public void ClickInMiddleOfControl()
          => new Actions(Helper.Session)
                .MoveToElement(element, 0, 0)
                .MoveByOffset(Rect.Width / 2, Rect.Height / 2)
                .Click()
                .Build()
                .Perform();
    }
    

    Extension methods

    public static class Extensions
    {
       public static void ClickInMiddleOfControl(this WindowsElement source, (int Width, int Height) rect)
          => new Actions(Helper.Session)
                .MoveToElement(source, 0, 0)
                .MoveByOffset(rect.Width / 2, rect.Height / 2)
                .Click()
                .Build()
                .Perform();
    }
    

    Best Regards