你是否在开发桌面应用自动化测试时,面对复杂的界面元素定位而头疼?是否在处理不同控件交互时,总是找不到合适的方法?作为一名资深的C#开发者,我发现许多同行都在UI Automation的学习路上踩过这些坑:不理解UI自动化树结构、找不准控件元素、搞不清楚控件模式的使用场景。
今天这篇文章,我将带你深入理解UI Automation的四大核心概念,通过实战代码和真实场景,让你彻底掌握这些技术要点,从此告别"盲人摸象"式的开发模式!
什么是UI自动化树?
UI Automation Tree就像网页的DOM树一样,是Windows桌面应用程序界面元素的层次化表示。每个窗口、按钮、文本框都是这棵树上的一个节点,通过父子关系组织起来。
实战场景:定位计算器中的按钮
C#using UIAutomationClient;
namespace AppAutomationTreeExample
{
internal class Program
{
static void Main(string[] args)
{
Console.OutputEncoding = System.Text.Encoding.UTF8;
CUIAutomation8 automation = new CUIAutomation8();
var desktop = automation.GetRootElement();
var windowCondition = automation.CreateAndConditionFromArray(new[]
{
automation.CreatePropertyCondition(
UIA_PropertyIds.UIA_NamePropertyId, "Calculator"),
automation.CreatePropertyCondition(
UIA_PropertyIds.UIA_ControlTypePropertyId,
UIA_ControlTypeIds.UIA_WindowControlTypeId)
});
var calcWindow = desktop.FindFirst(TreeScope.TreeScope_Children, windowCondition);
if (calcWindow != null)
{
Console.WriteLine($"找到计算器窗口:{calcWindow.CurrentName}");
// 🎯 分析整个UI树结构
AnalyzeUITree(calcWindow, automation, 0, 5); // 最多5层深度
}
}
// 🔥 递归分析UI树结构
static void AnalyzeUITree(IUIAutomationElement element, CUIAutomation8 automation, int level, int maxLevel)
{
if (level > maxLevel) return;
string indent = new string(' ', level * 2);
string controlType = GetControlTypeName(element.CurrentControlType);
Console.WriteLine($"{indent}├─ {element.CurrentName} ({controlType})");
Console.WriteLine($"{indent} AutomationId: {element.CurrentAutomationId}");
Console.WriteLine($"{indent} ClassName: {element.CurrentClassName}");
// 查找所有子元素
var children = element.FindAll(TreeScope.TreeScope_Children,
automation.CreateTrueCondition());
for (int i = 0; i < children.Length; i++)
{
AnalyzeUITree(children.GetElement(i), automation, level + 1, maxLevel);
}
}
// 获取控件类型名称
static string GetControlTypeName(int controlTypeId)
{
var controlTypes = new Dictionary<int, string>
{
{ UIA_ControlTypeIds.UIA_ButtonControlTypeId, "Button" },
{ UIA_ControlTypeIds.UIA_WindowControlTypeId, "Window" },
{ UIA_ControlTypeIds.UIA_PaneControlTypeId, "Pane" },
{ UIA_ControlTypeIds.UIA_GroupControlTypeId, "Group" },
{ UIA_ControlTypeIds.UIA_TextControlTypeId, "Text" },
{ UIA_ControlTypeIds.UIA_EditControlTypeId, "Edit" }
};
return controlTypes.ContainsKey(controlTypeId) ? controlTypes[controlTypeId] : $"Unknown({controlTypeId})";
}
}
}

⚠️ 常见坑点提醒:
核心理解:万物皆Element
AutomationElement是UI Automation的基础单元,每个界面控件都对应一个AutomationElement对象。它封装了控件的属性、支持的操作模式等信息。
实战代码:多种方式定位文本框
C#using UIAutomationClient;
namespace AppAutomationTreeExample
{
public class UIAutomationHelper
{
private readonly CUIAutomation8 _automation;
public UIAutomationHelper()
{
_automation = new CUIAutomation8();
}
/// <summary>
/// 根据应用程序名称查找窗口
/// </summary>
/// <param name="windowName">窗口名称</param>
/// <returns>找到的窗口元素</returns>
public IUIAutomationElement? FindWindowByName(string windowName)
{
var desktop = _automation.GetRootElement();
var windowCondition = _automation.CreateAndConditionFromArray(new[]
{
_automation.CreatePropertyCondition(
UIA_PropertyIds.UIA_NamePropertyId, windowName),
_automation.CreatePropertyCondition(
UIA_PropertyIds.UIA_ControlTypePropertyId,
UIA_ControlTypeIds.UIA_WindowControlTypeId)
});
return desktop.FindFirst(TreeScope.TreeScope_Children, windowCondition);
}
/// <summary>
/// 根据进程名称查找窗口
/// </summary>
/// <param name="processName">进程名称(不包含.exe)</param>
/// <returns>找到的窗口元素</returns>
public IUIAutomationElement? FindWindowByProcessName(string processName)
{
var desktop = _automation.GetRootElement();
var allWindows = desktop.FindAll(TreeScope.TreeScope_Children,
_automation.CreatePropertyCondition(
UIA_PropertyIds.UIA_ControlTypePropertyId,
UIA_ControlTypeIds.UIA_WindowControlTypeId));
for (int i = 0; i < allWindows.Length; i++)
{
var window = allWindows.GetElement(i);
if (window.CurrentProcessId > 0)
{
try
{
var process = System.Diagnostics.Process.GetProcessById(window.CurrentProcessId);
if (process.ProcessName.Equals(processName, StringComparison.OrdinalIgnoreCase))
{
return window;
}
}
catch
{
// 进程可能已经结束,忽略异常
}
}
}
return null;
}
/// <summary>
/// 在指定窗口中查找所有 TextBox 控件
/// </summary>
/// <param name="window">窗口元素</param>
/// <returns>TextBox 控件列表</returns>
public List<TextBoxInfo> FindAllTextBoxes(IUIAutomationElement window)
{
var textBoxes = new List<TextBoxInfo>();
var editCondition = _automation.CreatePropertyCondition(
UIA_PropertyIds.UIA_ControlTypePropertyId,
UIA_ControlTypeIds.UIA_EditControlTypeId);
var foundElements = window.FindAll(TreeScope.TreeScope_Descendants, editCondition);
for (int i = 0; i < foundElements.Length; i++)
{
var element = foundElements.GetElement(i);
textBoxes.Add(new TextBoxInfo
{
Element = element,
Name = element.CurrentName ?? "",
AutomationId = element.CurrentAutomationId ?? "",
ClassName = element.CurrentClassName ?? "",
Value = GetElementValue(element),
IsEnabled = element.CurrentIsEnabled>0,
IsVisible = element.CurrentIsOffscreen>0
});
}
return textBoxes;
}
/// <summary>
/// 根据 AutomationId 查找 TextBox
/// </summary>
/// <param name="window">窗口元素</param>
/// <param name="automationId">AutomationId</param>
/// <returns>找到的 TextBox 信息</returns>
public TextBoxInfo? FindTextBoxByAutomationId(IUIAutomationElement window, string automationId)
{
var condition = _automation.CreateAndConditionFromArray(new[]
{
_automation.CreatePropertyCondition(
UIA_PropertyIds.UIA_ControlTypePropertyId,
UIA_ControlTypeIds.UIA_EditControlTypeId),
_automation.CreatePropertyCondition(
UIA_PropertyIds.UIA_AutomationIdPropertyId, automationId)
});
var element = window.FindFirst(TreeScope.TreeScope_Descendants, condition);
if (element != null)
{
return new TextBoxInfo
{
Element = element,
Name = element.CurrentName ?? "",
AutomationId = element.CurrentAutomationId ?? "",
ClassName = element.CurrentClassName ?? "",
Value = GetElementValue(element),
IsEnabled = element.CurrentIsEnabled>0,
IsVisible = element.CurrentIsOffscreen>0
};
}
return null;
}
/// <summary>
/// 根据名称查找 TextBox
/// </summary>
/// <param name="window">窗口元素</param>
/// <param name="name">TextBox 名称</param>
/// <returns>找到的 TextBox 信息</returns>
public TextBoxInfo? FindTextBoxByName(IUIAutomationElement window, string name)
{
var condition = _automation.CreateAndConditionFromArray(new[]
{
_automation.CreatePropertyCondition(
UIA_PropertyIds.UIA_ControlTypePropertyId,
UIA_ControlTypeIds.UIA_EditControlTypeId),
_automation.CreatePropertyCondition(
UIA_PropertyIds.UIA_NamePropertyId, name)
});
var element = window.FindFirst(TreeScope.TreeScope_Descendants, condition);
if (element != null)
{
return new TextBoxInfo
{
Element = element,
Name = element.CurrentName ?? "",
AutomationId = element.CurrentAutomationId ?? "",
ClassName = element.CurrentClassName ?? "",
Value = GetElementValue(element),
IsEnabled = element.CurrentIsEnabled>0,
IsVisible = element.CurrentIsOffscreen>0
};
}
return null;
}
/// <summary>
/// 根据类名查找 TextBox
/// </summary>
/// <param name="window">窗口元素</param>
/// <param name="className">类名</param>
/// <returns>找到的 TextBox 信息列表</returns>
public List<TextBoxInfo> FindTextBoxesByClassName(IUIAutomationElement window, string className)
{
var textBoxes = new List<TextBoxInfo>();
var condition = _automation.CreateAndConditionFromArray(new[]
{
_automation.CreatePropertyCondition(
UIA_PropertyIds.UIA_ControlTypePropertyId,
UIA_ControlTypeIds.UIA_EditControlTypeId),
_automation.CreatePropertyCondition(
UIA_PropertyIds.UIA_ClassNamePropertyId, className)
});
var foundElements = window.FindAll(TreeScope.TreeScope_Descendants, condition);
for (int i = 0; i < foundElements.Length; i++)
{
var element = foundElements.GetElement(i);
textBoxes.Add(new TextBoxInfo
{
Element = element,
Name = element.CurrentName ?? "",
AutomationId = element.CurrentAutomationId ?? "",
ClassName = element.CurrentClassName ?? "",
Value = GetElementValue(element),
IsEnabled = element.CurrentIsEnabled>0,
IsVisible = element.CurrentIsOffscreen>0
});
}
return textBoxes;
}
/// <summary>
/// 设置 TextBox 的值
/// </summary>
/// <param name="textBoxInfo">TextBox 信息</param>
/// <param name="value">要设置的值</param>
/// <returns>是否设置成功</returns>
public bool SetTextBoxValue(TextBoxInfo textBoxInfo, string value)
{
try
{
var valuePattern = textBoxInfo.Element.GetCurrentPattern(UIA_PatternIds.UIA_ValuePatternId) as IUIAutomationValuePattern;
if (valuePattern != null)
{
valuePattern.SetValue(value);
return true;
}
return false;
}
catch
{
return false;
}
}
/// <summary>
/// 获取元素的值
/// </summary>
/// <param name="element">UI 元素</param>
/// <returns>元素值</returns>
private string GetElementValue(IUIAutomationElement element)
{
try
{
var valuePattern = element.GetCurrentPattern(UIA_PatternIds.UIA_ValuePatternId) as IUIAutomationValuePattern;
return valuePattern?.CurrentValue ?? "";
}
catch
{
return "";
}
}
}
/// <summary>
/// TextBox 信息类
/// </summary>
public class TextBoxInfo
{
public IUIAutomationElement Element { get; set; } = null!;
public string Name { get; set; } = "";
public string AutomationId { get; set; } = "";
public string ClassName { get; set; } = "";
public string Value { get; set; } = "";
public bool IsEnabled { get; set; }
public bool IsVisible { get; set; }
public override string ToString()
{
return $"Name: {Name}, AutomationId: {AutomationId}, Value: '{Value}', Enabled: {IsEnabled}, Visible: {IsVisible}";
}
}
}
C#private static void Test1()
{
Console.OutputEncoding = System.Text.Encoding.UTF8;
CUIAutomation8 automation = new CUIAutomation8();
var desktop = automation.GetRootElement();
var windowCondition = automation.CreateAndConditionFromArray(new[]
{
automation.CreatePropertyCondition(
UIA_PropertyIds.UIA_NamePropertyId, "Calculator"),
automation.CreatePropertyCondition(
UIA_PropertyIds.UIA_ControlTypePropertyId,
UIA_ControlTypeIds.UIA_WindowControlTypeId)
});
var calcWindow = desktop.FindFirst(TreeScope.TreeScope_Children, windowCondition);
if (calcWindow != null)
{
Console.WriteLine($"找到计算器窗口:{calcWindow.CurrentName}");
// 🎯 分析整个UI树结构
AnalyzeUITree(calcWindow, automation, 0, 5); // 最多5层深度
}
}

💡 最佳实践技巧:
C#using UIAutomationClient;
namespace AppAutomationTreeExample
{
public class UIAutomationHelper
{
private readonly CUIAutomation8 _automation;
public UIAutomationHelper()
{
_automation = new CUIAutomation8();
}
/// <summary>
/// 根据应用程序名称查找窗口
/// </summary>
/// <param name="windowName">窗口名称</param>
/// <returns>找到的窗口元素</returns>
public IUIAutomationElement? FindWindowByName(string windowName)
{
var desktop = _automation.GetRootElement();
var windowCondition = _automation.CreateAndConditionFromArray(new[]
{
_automation.CreatePropertyCondition(
UIA_PropertyIds.UIA_NamePropertyId, windowName),
_automation.CreatePropertyCondition(
UIA_PropertyIds.UIA_ControlTypePropertyId,
UIA_ControlTypeIds.UIA_WindowControlTypeId)
});
return desktop.FindFirst(TreeScope.TreeScope_Children, windowCondition);
}
/// <summary>
/// 根据进程名称查找窗口
/// </summary>
/// <param name="processName">进程名称(不包含.exe)</param>
/// <returns>找到的窗口元素</returns>
public IUIAutomationElement? FindWindowByProcessName(string processName)
{
var desktop = _automation.GetRootElement();
var allWindows = desktop.FindAll(TreeScope.TreeScope_Children,
_automation.CreatePropertyCondition(
UIA_PropertyIds.UIA_ControlTypePropertyId,
UIA_ControlTypeIds.UIA_WindowControlTypeId));
for (int i = 0; i < allWindows.Length; i++)
{
var window = allWindows.GetElement(i);
if (window.CurrentProcessId > 0)
{
try
{
var process = System.Diagnostics.Process.GetProcessById(window.CurrentProcessId);
if (process.ProcessName.Equals(processName, StringComparison.OrdinalIgnoreCase))
{
return window;
}
}
catch
{
// 进程可能已经结束,忽略异常
}
}
}
return null;
}
/// <summary>
/// 在指定窗口中查找所有 TextBox 控件
/// </summary>
/// <param name="window">窗口元素</param>
/// <returns>TextBox 控件列表</returns>
public List<TextBoxInfo> FindAllTextBoxes(IUIAutomationElement window)
{
var textBoxes = new List<TextBoxInfo>();
var editCondition = _automation.CreatePropertyCondition(
UIA_PropertyIds.UIA_ControlTypePropertyId,
UIA_ControlTypeIds.UIA_EditControlTypeId);
var foundElements = window.FindAll(TreeScope.TreeScope_Descendants, editCondition);
for (int i = 0; i < foundElements.Length; i++)
{
var element = foundElements.GetElement(i);
textBoxes.Add(new TextBoxInfo
{
Element = element,
Name = element.CurrentName ?? "",
AutomationId = element.CurrentAutomationId ?? "",
ClassName = element.CurrentClassName ?? "",
Value = GetElementValue(element),
IsEnabled = element.CurrentIsEnabled>0,
IsVisible = element.CurrentIsOffscreen>0
});
}
return textBoxes;
}
/// <summary>
/// 根据 AutomationId 查找 TextBox
/// </summary>
/// <param name="window">窗口元素</param>
/// <param name="automationId">AutomationId</param>
/// <returns>找到的 TextBox 信息</returns>
public TextBoxInfo? FindTextBoxByAutomationId(IUIAutomationElement window, string automationId)
{
var condition = _automation.CreateAndConditionFromArray(new[]
{
_automation.CreatePropertyCondition(
UIA_PropertyIds.UIA_ControlTypePropertyId,
UIA_ControlTypeIds.UIA_EditControlTypeId),
_automation.CreatePropertyCondition(
UIA_PropertyIds.UIA_AutomationIdPropertyId, automationId)
});
var element = window.FindFirst(TreeScope.TreeScope_Descendants, condition);
if (element != null)
{
return new TextBoxInfo
{
Element = element,
Name = element.CurrentName ?? "",
AutomationId = element.CurrentAutomationId ?? "",
ClassName = element.CurrentClassName ?? "",
Value = GetElementValue(element),
IsEnabled = element.CurrentIsEnabled>0,
IsVisible = element.CurrentIsOffscreen>0
};
}
return null;
}
/// <summary>
/// 根据名称查找 TextBox
/// </summary>
/// <param name="window">窗口元素</param>
/// <param name="name">TextBox 名称</param>
/// <returns>找到的 TextBox 信息</returns>
public TextBoxInfo? FindTextBoxByName(IUIAutomationElement window, string name)
{
var condition = _automation.CreateAndConditionFromArray(new[]
{
_automation.CreatePropertyCondition(
UIA_PropertyIds.UIA_ControlTypePropertyId,
UIA_ControlTypeIds.UIA_EditControlTypeId),
_automation.CreatePropertyCondition(
UIA_PropertyIds.UIA_NamePropertyId, name)
});
var element = window.FindFirst(TreeScope.TreeScope_Descendants, condition);
if (element != null)
{
return new TextBoxInfo
{
Element = element,
Name = element.CurrentName ?? "",
AutomationId = element.CurrentAutomationId ?? "",
ClassName = element.CurrentClassName ?? "",
Value = GetElementValue(element),
IsEnabled = element.CurrentIsEnabled>0,
IsVisible = element.CurrentIsOffscreen>0
};
}
return null;
}
/// <summary>
/// 根据类名查找 TextBox
/// </summary>
/// <param name="window">窗口元素</param>
/// <param name="className">类名</param>
/// <returns>找到的 TextBox 信息列表</returns>
public List<TextBoxInfo> FindTextBoxesByClassName(IUIAutomationElement window, string className)
{
var textBoxes = new List<TextBoxInfo>();
var condition = _automation.CreateAndConditionFromArray(new[]
{
_automation.CreatePropertyCondition(
UIA_PropertyIds.UIA_ControlTypePropertyId,
UIA_ControlTypeIds.UIA_EditControlTypeId),
_automation.CreatePropertyCondition(
UIA_PropertyIds.UIA_ClassNamePropertyId, className)
});
var foundElements = window.FindAll(TreeScope.TreeScope_Descendants, condition);
for (int i = 0; i < foundElements.Length; i++)
{
var element = foundElements.GetElement(i);
textBoxes.Add(new TextBoxInfo
{
Element = element,
Name = element.CurrentName ?? "",
AutomationId = element.CurrentAutomationId ?? "",
ClassName = element.CurrentClassName ?? "",
Value = GetElementValue(element),
IsEnabled = element.CurrentIsEnabled>0,
IsVisible = element.CurrentIsOffscreen>0
});
}
return textBoxes;
}
/// <summary>
/// 设置 TextBox 的值
/// </summary>
/// <param name="textBoxInfo">TextBox 信息</param>
/// <param name="value">要设置的值</param>
/// <returns>是否设置成功</returns>
public bool SetTextBoxValue(TextBoxInfo textBoxInfo, string value)
{
try
{
var valuePattern = textBoxInfo.Element.GetCurrentPattern(UIA_PatternIds.UIA_ValuePatternId) as IUIAutomationValuePattern;
if (valuePattern != null)
{
valuePattern.SetValue(value);
return true;
}
return false;
}
catch
{
return false;
}
}
/// <summary>
/// 获取元素的值
/// </summary>
/// <param name="element">UI 元素</param>
/// <returns>元素值</returns>
private string GetElementValue(IUIAutomationElement element)
{
try
{
var valuePattern = element.GetCurrentPattern(UIA_PatternIds.UIA_ValuePatternId) as IUIAutomationValuePattern;
return valuePattern?.CurrentValue ?? "";
}
catch
{
return "";
}
}
}
/// <summary>
/// TextBox 信息类
/// </summary>
public class TextBoxInfo
{
public IUIAutomationElement Element { get; set; } = null!;
public string Name { get; set; } = "";
public string AutomationId { get; set; } = "";
public string ClassName { get; set; } = "";
public string Value { get; set; } = "";
public bool IsEnabled { get; set; }
public bool IsVisible { get; set; }
public override string ToString()
{
return $"Name: {Name}, AutomationId: {AutomationId}, Value: '{Value}', Enabled: {IsEnabled}, Visible: {IsVisible}";
}
}
}
C#private static void Test3()
{
Console.OutputEncoding = System.Text.Encoding.UTF8;
// 获取桌面
var desktop = ElementFinder.GetDesktop();
// 查找计算器窗口
var calcWindow = ElementFinder.FindByNameAndControlType(
desktop, "Calculator", UIA_ControlTypeIds.UIA_WindowControlTypeId, 3000);
if (calcWindow != null)
{
Console.WriteLine($"找到计算器窗口:{calcWindow.CurrentName}");
Console.WriteLine();
// 示例1:查找所有按钮
var buttons = ElementFinder.FindByControlType(calcWindow, UIA_ControlTypeIds.UIA_ButtonControlTypeId);
Console.WriteLine($"找到 {buttons?.Length ?? 0} 个按钮");
// 示例2:查找特定按钮
var button1 = ElementFinder.FindButton(calcWindow, "One");
if (button1 != null)
{
Console.WriteLine($"找到按钮1: {ElementFinder.GetElementInfo(button1)}");
}
// 示例3:查找所有文本框
var textBoxes = ElementFinder.FindAllTextBoxes(calcWindow);
if (textBoxes != null)
{
Console.WriteLine($"找到 {textBoxes.Length} 个文本框:");
for (int i = 0; i < textBoxes.Length; i++)
{
var textBox = textBoxes.GetElement(i);
Console.WriteLine($" - {ElementFinder.GetElementInfo(textBox)}");
}
}
// 示例4:等待元素出现
Console.WriteLine("\n等待结果显示...");
var resultCondition = ElementFinder.CreateCondition(
UIA_PropertyIds.UIA_AutomationIdPropertyId, "CalculatorResults");
bool appeared = ElementFinder.WaitForElement(calcWindow, resultCondition, 2000);
Console.WriteLine($"结果元素出现: {appeared}");
// 示例5:查找可见元素
var visibleButton = ElementFinder.FindVisibleElement(calcWindow,
ElementFinder.CreateCondition(UIA_PropertyIds.UIA_ControlTypePropertyId, UIA_ControlTypeIds.UIA_ButtonControlTypeId));
if (visibleButton != null)
{
Console.WriteLine($"找到可见按钮: {ElementFinder.GetElementInfo(visibleButton)}");
}
// 示例6:调试查找
Console.WriteLine("\n=== 调试信息 ===");
var debugCondition = ElementFinder.CreateCondition(
UIA_PropertyIds.UIA_ControlTypePropertyId, UIA_ControlTypeIds.UIA_ButtonControlTypeId);
var debugInfo = ElementFinder.DebugFindAll(calcWindow, debugCondition, 50);
Console.WriteLine(debugInfo);
// 示例7:组合条件查找
var conditions = new IUIAutomationCondition[]
{
ElementFinder.CreateCondition(UIA_PropertyIds.UIA_ControlTypePropertyId, UIA_ControlTypeIds.UIA_ButtonControlTypeId),
ElementFinder.CreateCondition(UIA_PropertyIds.UIA_NamePropertyId, "Equals")
};
var equalButton = ElementFinder.FindByAndConditions(calcWindow, conditions);
if (equalButton != null)
{
Console.WriteLine($"找到等号按钮: {ElementFinder.GetElementInfo(equalButton)}");
}
// 示例8:按索引查找
var buttonCondition = ElementFinder.CreateCondition(
UIA_PropertyIds.UIA_ControlTypePropertyId, UIA_ControlTypeIds.UIA_ButtonControlTypeId);
var thirdButton = ElementFinder.FindElementByIndex(calcWindow, buttonCondition, 2);
if (thirdButton != null)
{
Console.WriteLine($"第3个按钮: {ElementFinder.GetElementInfo(thirdButton)}");
}
}
else
{
Console.WriteLine("未找到计算器窗口,请确保计算器应用程序正在运行。");
}
Console.WriteLine("\n按任意键退出...");
Console.ReadKey();
}

看完这篇深度解析,你有没有想过:
欢迎在评论区分享你的UI自动化实战经验,或者遇到的技术难题,我会第一时间回复大家!
通过今天的深度讲解,我们掌握了UI Automation的四大核心概念:
🌳 UI自动化树:理解了界面元素的层次化组织方式,学会了高效的元素遍历和定位策略
🎯 自动化元素:掌握了多种可靠的元素查找方法,特别是组合条件定位和超时处理的最佳实践
🎮 控件模式:深入理解了不同控件类型的交互方式,学会了智能化的模式检测和处理
这四个概念就像是UI自动化开发的"四大基石",只有牢固掌握它们,才能在复杂的桌面应用自动化项目中游刃有余。记住:理论结合实践,多调试多总结,你的UI Automation技能必将更上一层楼!
觉得这篇文章对你有帮助的话,请转发给更多需要的同行,让我们一起提升C#开发技能! 🚀
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!