Skip to content
Open
114 changes: 114 additions & 0 deletions src/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
root = true

[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8-bom
trim_trailing_whitespace = true
insert_final_newline = false

[*.{cs,vb}]
# Indentation preferences
indent_size = 4
indent_style = space
tab_width = 4

# Newline preferences
csharp_new_line_before_open_brace = none
csharp_new_line_before_else = false
csharp_new_line_before_catch = false
csharp_new_line_before_finally = false
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_between_query_expression_clauses = true

# Indentation options
csharp_indent_case_contents = true
csharp_indent_switch_labels = true
csharp_indent_labels = one_less_than_current

# Spacing options
csharp_space_after_cast = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_between_parentheses = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_around_binary_operators = before_and_after
csharp_space_between_method_declaration_name_and_open_parenthesis = false:none
csharp_space_between_method_call_name_and_opening_parenthesis = false:none

# Code style defaults
csharp_using_directive_placement = outside_namespace:suggestion
csharp_style_namespace_declarations = file_scoped:suggestion
csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_operators = false:silent
csharp_style_expression_bodied_properties = true:suggestion
csharp_style_expression_bodied_indexers = true:suggestion
csharp_style_expression_bodied_accessors = true:suggestion
csharp_style_expression_bodied_lambdas = true:suggestion
csharp_style_expression_bodied_local_functions = false:silent
csharp_prefer_braces = true:suggestion
csharp_style_prefer_primary_constructors = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion

# Expression-level preferences
csharp_style_var_for_built_in_types = false:suggestion
csharp_style_var_when_type_is_apparent = false:suggestion
csharp_style_var_elsewhere = false:suggestion
csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
csharp_prefer_simple_default_expression = true:suggestion
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion

# Naming conventions
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i

dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers =

dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case

dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case

dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers =

dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case

dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case

dotnet_naming_symbols.non_field_members.applicable_kinds = property, method, field, event, delegate
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =

dotnet_naming_rule.private_fields_should_be_camel_case_with_underscore.severity = suggestion
dotnet_naming_rule.private_fields_should_be_camel_case_with_underscore.symbols = private_fields
dotnet_naming_rule.private_fields_should_be_camel_case_with_underscore.style = camel_case_with_underscore

dotnet_naming_symbols.private_fields.applicable_kinds = field
dotnet_naming_symbols.private_fields.applicable_accessibilities = private
dotnet_naming_symbols.private_fields.required_modifiers =

dotnet_naming_style.camel_case_with_underscore.required_prefix = _
dotnet_naming_style.camel_case_with_underscore.required_suffix =
dotnet_naming_style.camel_case_with_underscore.word_separator =
dotnet_naming_style.camel_case_with_underscore.capitalization = camel_case

[*.xaml]
indent_size = 4
indent_style = space
7 changes: 4 additions & 3 deletions src/FlaUInspect.sln
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
# Visual Studio Version 18
VisualStudioVersion = 18.3.11312.210 d18.3
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlaUInspect", "FlaUInspect\FlaUInspect.csproj", "{B1C3A9D6-326F-4A42-A2B8-C7605D111F47}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E443DA57-D2BF-49DA-A583-DCE901F5EB84}"
ProjectSection(SolutionItems) = preProject
..\README.md = ..\README.md
.editorconfig = .editorconfig
..\LICENSE = ..\LICENSE
..\README.md = ..\README.md
EndProjectSection
EndProject
Global
Expand Down
35 changes: 16 additions & 19 deletions src/FlaUInspect/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,24 @@ private void ApplicationStart(object sender, StartupEventArgs e) {
string applicationVersion = versionAttribute?.Version ?? "N/A";
InternalLogger logger = new ();

#if AUTOMATION_UIA3
MainViewModel mainViewModel = new (AutomationType.UIA3, applicationVersion, logger);
MainWindow mainWindow = new () { DataContext = mainViewModel };

//Re-enable normal shutdown mode.
Current.ShutdownMode = ShutdownMode.OnMainWindowClose;
Current.MainWindow = mainWindow;
mainWindow.Show();
#elif AUTOMATION_UIA2
MainViewModel mainViewModel = new (AutomationType.UIA2, applicationVersion, logger);
MainWindow mainWindow = new() { DataContext = mainViewModel };

//Re-enable normal shutdown mode.
Current.ShutdownMode = ShutdownMode.OnMainWindowClose;
Current.MainWindow = mainWindow;
mainWindow.Show();
#else
Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;
ChooseVersionWindow dialog = new ();
#if AUTOMATION_UIA3
dialog.SelectedAutomationType = AutomationType.UIA3;
#elif AUTOMATION_UIA2
dialog.SelectedAutomationType = AutomationType.UIA2;
#else
var args = Environment.GetCommandLineArgs();
if (args.Any(a=>a.Equals("--uia2", StringComparison.OrdinalIgnoreCase)))
dialog.SelectedAutomationType = AutomationType.UIA2;
else if (args.Any(a=>a.Equals("--uia3", StringComparison.OrdinalIgnoreCase)))
dialog.SelectedAutomationType = AutomationType.UIA3;
else if (dialog.ShowDialog() != true)
return;
#endif


if (dialog.ShowDialog() == true) {

MainViewModel mainViewModel = new (dialog.SelectedAutomationType, applicationVersion, logger);
MainWindow mainWindow = new () { DataContext = mainViewModel };
Expand All @@ -42,7 +39,7 @@ private void ApplicationStart(object sender, StartupEventArgs e) {
Current.ShutdownMode = ShutdownMode.OnMainWindowClose;
Current.MainWindow = mainWindow;
mainWindow.Show();
}
#endif


}
}
17 changes: 17 additions & 0 deletions src/FlaUInspect/Core/FindByType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace FlaUInspect.Core;

public enum FindByType {
ByAutomationId,
ByName,
ByClassName,
ByControlType,
FindFirstByXPath,
ByText,
ByFrameworkId,
ByLocalizedControlType,
ByValue,
}

public static class FindByTypeValues {
public static FindByType[] All { get; } = Enum.GetValues<FindByType>();
}
2 changes: 1 addition & 1 deletion src/FlaUInspect/Core/PatternItemsFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ private static IEnumerable<PatternItem> AddTogglePatternDetails(AutomationElemen
yield break;
}
ITogglePattern pattern = element.Patterns.Toggle.Pattern;
yield return PatternItem.FromAutomationProperty("ToggleState", pattern.ToggleState);
yield return new PatternItem("ToggleState", "Toggle", pattern.Toggle);
}

private static IEnumerable<PatternItem> AddTextPatternDetails(AutomationElement? element) {
Expand Down
2 changes: 1 addition & 1 deletion src/FlaUInspect/Resources/RibbonIcons.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@
<RectangleGeometry Rect="0.0,0.0,512.0,512.0" />
</Canvas.Clip>
<Canvas UseLayoutRounding="False">
<Path Fill="#ff000000">
<Path Fill="{Binding Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContentControl}}">
<Path.Data>
<PathGeometry
Figures="m 234.5 468.297 c -8.4501 -0.77576 -27.431 -4.50654 -37.1218 -7.29646 C 162.622 450.994 132.017 432.984 106.467 407.503 C 100.399 401.451 93.1994 393.626 90.4673 390.113 L 85.5 383.725 L 85 398.281 c -0.489257 14.2428 -0.565411 14.6486 -3.5444 18.8874 c -8.66513 12.3296 -26.2531 12.3275 -34.9112 -0.004 L 43.5 412.828 V 362.578 V 312.329 l 2.73377 -3.9145 c 1.50358 -2.15297 4.3956 -5.0395 6.42673 -6.4145 l 3.69296 -2.5 h 50.2371 h 50.2371 l 4.33614 3.0444 c 12.3324 8.65857 12.3338 26.2479 0.003 34.9112 l -4.3333 3.0444 l -24.0041 0.30109 c -21.0368 0.26387 -23.9317 0.48987 -23.4181 1.82819 c 0.3223 0.8399 3.26463 5.42941 6.53852 10.1989 c 47.3738 69.0155 138.231 93.2387 214.05 57.0676 c 34.61 -16.5114 63.8128 -45.7907 79.9727 -80.1822 c 4.3681 -9.29626 12.0133 -30.856 12.0371 -33.9451 c 0.005 -0.69767 1.09625 -3.60202 2.42417 -6.45412 c 5.63929 -12.112 21.5905 -15.6886 31.9362 -7.16078 c 8.11114 6.68588 9.45139 13.395 5.66193 28.3429 c -12.622 49.7885 -44.2653 94.2692 -87.5321 123.043 c -27.6496 18.3877 -61.6457 30.7117 -95 34.4388 c -8.43052 0.94204 -36.0773 1.13781 -45 0.31865 z M 64.5 234.095 C 57.6872 232.368 52.6687 228.42 49.7598 222.5 C 46.9296 216.74 46.9529 213.398 49.9046 201.754 C 62.6136 151.618 94.1189 107.277 137.5 78.4721 C 172.915 54.9562 212.733 43 255.632 43 c 50.8901 0 98.3714 17.2367 137.54 49.9297 c 9.74855 8.13697 23.149 21.8171 29.0564 29.6628 c 1.79961 2.3901 3.57551 4.35954 3.94644 4.37655 c 0.37093 0.017 0.82093 -6.23228 1 -13.8873 c 0.31955 -13.6603 0.38201 -13.9985 3.36998 -18.25 c 8.66528 -12.3295 26.2531 -12.3273 34.9112 0.0044 L 468.5 99.1723 V 149.336 V 199.5 l -2.65608 3.84392 c -1.46084 2.11416 -4.38584 5.03916 -6.5 6.5 L 455.5 212.5 h -50.1639 h -50.1639 l -4.33614 -3.0444 c -12.3324 -8.65857 -12.3338 -26.2479 -0.003 -34.9112 l 4.3333 -3.0444 l 23.9167 -0.30052 C 392.237 171.034 403 170.556 403 170.137 c 0 -0.9217 -10.9698 -17.0925 -14.581 -21.4941 c -5.75972 -7.02043 -20.4021 -21.2302 -27.919 -27.094 c -10.3362 -8.06321 -17.211 -12.3819 -29.6354 -18.6168 c -46.8604 -23.5157 -104.246 -23.2791 -151.96 0.62637 c -32.6254 16.3459 -61.438 45.809 -76.7442 78.477 c -4.98963 10.6493 -9.89839 24.2948 -12.1061 33.6527 c -1.78771 7.57775 -5.98994 13.6246 -11.1908 16.1032 c -5.27882 2.51569 -10.3327 3.32644 -14.3634 2.30419 z"
Expand Down
38 changes: 37 additions & 1 deletion src/FlaUInspect/ViewModels/ElementViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@
using FlaUInspect.Core;
using FlaUInspect.Core.Extensions;
using FlaUInspect.Core.Logger;

using System.Collections.ObjectModel;
using System.Windows.Input;
using MenuItem = System.Windows.Controls.MenuItem;
namespace FlaUInspect.ViewModels;

public class ElementViewModel(AutomationElement? automationElement, ILogger? logger) : ObservableObject {
private readonly object _lockObject = new ();
public AutomationElement? AutomationElement { get; } = automationElement;
private RelayCommand? _refreshItemCommand;
private RelayCommand? _focusCommand;

public bool IsExpanded {
get => GetProperty<bool>();
Expand All @@ -36,6 +40,38 @@ public bool IsSelected {
public ExtendedObservableCollection<ElementViewModel?> Children { get; set; } = [];


public ICommand RefreshItemCommand =>
_refreshItemCommand ??= new((_) => {
Children.Clear();
IsExpanded = true;
});

public ICommand FocusCommand =>
_focusCommand ??= new((_) => {
try {
AutomationElement.Focus();
} catch { }
});


private ObservableCollection<MenuItem>? _mouseActions;
public ObservableCollection<MenuItem> MouseActions { get => _mouseActions ??= BuildMouseActions(); }

private ObservableCollection<MenuItem> BuildMouseActions() {
return [
CreateMenuItem("Left Click", () => AutomationElement?.Click()),
CreateMenuItem("Right Click", () => AutomationElement?.RightClick()),
CreateMenuItem("Double Click", () => AutomationElement?.DoubleClick()),
];
}

private MenuItem CreateMenuItem(string header, Action value) {
return new MenuItem {
Header = header,
Command = new RelayCommand(_ => value())
};
}

public string XPath => AutomationElement == null ? string.Empty : Debug.GetXPathToElement(AutomationElement);
public event Action<ElementViewModel>? SelectionChanged;

Expand Down
28 changes: 20 additions & 8 deletions src/FlaUInspect/ViewModels/MainViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,12 @@ public class MainViewModel : ObservableObject {
private RelayCommand? _openErrorListCommand;
private PatternItemsFactory? _patternItemsFactory;
private RelayCommand? _refreshCommand;
private RelayCommand? _refreshItemCommand;
private AutomationElement? _rootElement;
private RelayCommand? _startNewInstanceCommand;
private ITreeWalker? _treeWalker;

public SearchViewModel Search { get; }

public MainViewModel(AutomationType automationType, string applicationVersion, InternalLogger logger) {
_logger = logger;
ApplicationVersion = applicationVersion;
Expand All @@ -49,6 +50,11 @@ public MainViewModel(AutomationType automationType, string applicationVersion, I
Elements = [];
BindingOperations.EnableCollectionSynchronization(Elements, _itemsLock);

Search = new SearchViewModel(
() => SelectedItem,
() => Elements.FirstOrDefault(),
() => _treeWalker,
ElementToSelectChanged);
}

public ICommand OpenErrorListCommand =>
Expand Down Expand Up @@ -146,23 +152,21 @@ public ElementViewModel? SelectedItem {
if (value != null) {
ReadPatternsForSelectedItem(value.AutomationElement);
}
if (!Search.IsNavigating) {
Search.NotifySelectionChanged();
}
}
}
}

public ICommand RefreshItemCommand =>
_refreshItemCommand ??= new RelayCommand(o => {
if (o is ElementViewModel item) {
item.Children.Clear();
item.IsExpanded = true;
}
});

public IEnumerable<ElementPatternItem> ElementPatterns {
get => _elementPatterns ?? Enumerable.Empty<ElementPatternItem>();
private set => SetProperty(ref _elementPatterns, value as ObservableCollection<ElementPatternItem>);
}

public ObservableCollection<System.Windows.Controls.MenuItem> PatternActionContextItems {get; } = new();

public ICommand InfoCommand => _infoCommand ??= new RelayCommand(_ => {
IsInfoVisible = !IsInfoVisible;
});
Expand Down Expand Up @@ -197,6 +201,7 @@ private void ReadPatternsForSelectedItem(AutomationElement? selectedItemAutomati
HashSet<PatternId> supportedPatterns = [.. selectedItemAutomationElement.GetSupportedPatterns()];
IDictionary<string, PatternItem[]> patternItemsForElement = _patternItemsFactory.CreatePatternItemsForElement(selectedItemAutomationElement, supportedPatterns);

PatternActionContextItems.Clear();
foreach (ElementPatternItem elementPattern in ElementPatterns) {
elementPattern.IsVisible = elementPattern.PatternIdName == PatternItemsFactory.Identification
|| elementPattern.PatternIdName == PatternItemsFactory.Details
Expand All @@ -207,6 +212,13 @@ private void ReadPatternsForSelectedItem(AutomationElement? selectedItemAutomati
if (patternItemsForElement.TryGetValue(elementPattern.PatternIdName, out PatternItem[]? children)) {
foreach (PatternItem patternItem in children) {
elementPattern.Children.Add(patternItem);
if (patternItem.HasExecutableAction) {
System.Windows.Controls.MenuItem actionMenuItem = new() {
Header = $"{patternItem.Key}"
};
actionMenuItem.Click += (_, _) => patternItem.Action?.Invoke();
PatternActionContextItems.Add(actionMenuItem);
}
}
}

Expand Down
Loading