Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions FaultRecordWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,24 @@
SelectionChanged="FaultRecordsGrid_SelectionChanged">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Get" Width="48">
<!-- Restrict the zero-inset alignment to this checkbox column.
Other text headers/cells retain the existing 9px typography inset. -->
<DataGridTemplateColumn.HeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}"
BasedOn="{StaticResource {x:Type DataGridColumnHeader}}">
<Setter Property="Padding" Value="0"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
</Style>
</DataGridTemplateColumn.HeaderStyle>
<DataGridTemplateColumn.CellStyle>
<Style TargetType="{x:Type DataGridCell}"
BasedOn="{StaticResource {x:Type DataGridCell}}">
<Setter Property="Padding" Value="0"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
</Style>
</DataGridTemplateColumn.CellStyle>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Expand Down
2 changes: 2 additions & 0 deletions IpConnectWizardWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
Background="{StaticResource TaskDialogCard}"
BorderBrush="{StaticResource TaskDialogBorder}"
IsEditable="True" IsTextSearchEnabled="False"
HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
Loaded="RelayIpBox_Loaded"
ToolTip="Enter a new IP address or choose a previously saved address"
ItemsSource="{Binding RecentRelayIps, RelativeSource={RelativeSource AncestorType=Window}}"
Text="{Binding RelayIpAddress, RelativeSource={RelativeSource AncestorType=Window}, UpdateSourceTrigger=LostFocus}"/>
Expand Down
14 changes: 14 additions & 0 deletions IpConnectWizardWindow.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Collections.ObjectModel;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using ArIED61850Tester.Services;

namespace ArIED61850Tester;
Expand Down Expand Up @@ -38,6 +39,19 @@ public IpConnectWizardWindow(string initialIp = "", int initialPort = 102)
Loaded += (_, _) => RelayIpBox.Focus();
}

// WPF's editable ComboBox displays text in a templated TextBox, not directly
// in its selection presenter. Apply alignment to both modes while preserving
// the theme template, keyboard editing, focus and recent-address binding.
private void RelayIpBox_Loaded(object sender, RoutedEventArgs e)
{
if (RelayIpBox.Template.FindName("PART_EditableTextBox", RelayIpBox) is TextBox editor)
{
editor.TextAlignment = TextAlignment.Center;
editor.HorizontalContentAlignment = HorizontalAlignment.Center;
editor.VerticalContentAlignment = VerticalAlignment.Center;
}
}

private void Connect_Click(object sender, RoutedEventArgs e)
{
RelayIpAddress = (RelayIpAddress ?? string.Empty).Trim();
Expand Down
96 changes: 96 additions & 0 deletions tests/ARSAS.Tests/C264FieldAlignmentRegressionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
using System.Xml.Linq;

namespace ARSAS.Tests;

/// <summary>
/// Field-reported alignment regressions on the native WPF C264 workstation UI.
/// Assertions inspect scoped style contracts without launching a relay or Npcap.
/// </summary>
public sealed class C264FieldAlignmentRegressionTests
{
private static readonly XNamespace Wpf = "http://schemas.microsoft.com/winfx/2006/xaml/presentation";

[Fact]
public void IedIpAddress_CentersEditableText_WithoutReplacingComboBoxTemplate()
{
var document = XDocument.Load(Find("IpConnectWizardWindow.xaml"));
var combo = document.Descendants(Wpf + "ComboBox")
.Single(x => (string?)x.Attribute(XName.Get("Name", "http://schemas.microsoft.com/winfx/2006/xaml")) == "RelayIpBox");

Assert.Equal("True", (string?)combo.Attribute("IsEditable"));
Assert.Equal("Center", (string?)combo.Attribute("HorizontalContentAlignment"));
Assert.Equal("Center", (string?)combo.Attribute("VerticalContentAlignment"));
Assert.Equal("RelayIpBox_Loaded", (string?)combo.Attribute("Loaded"));
Assert.Contains("RecentRelayIps", (string?)combo.Attribute("ItemsSource"));
Assert.Contains("RelayIpAddress", (string?)combo.Attribute("Text"));
Assert.Empty(combo.Elements(Wpf + "ComboBox.Template"));

var source = File.ReadAllText(Find("IpConnectWizardWindow.xaml.cs"));
Assert.Contains("RelayIpBox.Template.FindName(\"PART_EditableTextBox\", RelayIpBox)", source);
Assert.Contains("editor.TextAlignment = TextAlignment.Center;", source);
Assert.Contains("editor.VerticalContentAlignment = VerticalAlignment.Center;", source);
Assert.Contains("UserPreferenceStore.LoadRecentEndpoints()", source);
Assert.Contains("System.Net.IPAddress.TryParse", source);
}

[Fact]
public void FaultRecordSelectAllAndRows_CenterInSameInsetFreeColumn_WithoutChangingOtherColumns()
{
var document = XDocument.Load(Find("FaultRecordWindow.xaml"));
var checkboxColumn = document.Descendants(Wpf + "DataGridTemplateColumn")
.Single(x => (string?)x.Attribute("Header") == "Get");

Assert.Equal("48", (string?)checkboxColumn.Attribute("Width"));
var header = checkboxColumn.Element(Wpf + "DataGridTemplateColumn.HeaderStyle")!
.Element(Wpf + "Style")!;
var cell = checkboxColumn.Element(Wpf + "DataGridTemplateColumn.CellStyle")!
.Element(Wpf + "Style")!;
Assert.Equal("{StaticResource {x:Type DataGridColumnHeader}}", (string?)header.Attribute("BasedOn"));
Assert.Equal("{StaticResource {x:Type DataGridCell}}", (string?)cell.Attribute("BasedOn"));
AssertSetter(header, "Padding", "0");
AssertSetter(header, "HorizontalContentAlignment", "Center");
AssertSetter(cell, "Padding", "0");
AssertSetter(cell, "HorizontalContentAlignment", "Stretch");

var rowCheckbox = checkboxColumn.Descendants(Wpf + "CheckBox").Single();
Assert.Equal("Center", (string?)rowCheckbox.Attribute("HorizontalAlignment"));
Assert.Equal("Center", (string?)rowCheckbox.Attribute("VerticalAlignment"));
Assert.Contains("IsSelected", (string?)rowCheckbox.Attribute("IsChecked"));
Assert.Contains("CanSelectForDownload", (string?)rowCheckbox.Attribute("IsEnabled"));

// Text and status columns keep the existing 9px global typography inset.
Assert.Contains(document.Descendants(Wpf + "Style"),
s => (string?)s.Attribute("TargetType") == "DataGridColumnHeader"
&& s.Elements(Wpf + "Setter").Any(p =>
(string?)p.Attribute("Property") == "Padding" &&
(string?)p.Attribute("Value") == "9,0"));
Assert.Contains(document.Descendants(Wpf + "Style"),
s => (string?)s.Attribute("TargetType") == "DataGridCell"
&& s.Elements(Wpf + "Setter").Any(p =>
(string?)p.Attribute("Property") == "Padding" &&
(string?)p.Attribute("Value") == "9,0"));

var code = File.ReadAllText(Find("FaultRecordWindow.HeaderSelection.cs"));
Assert.Contains("IsThreeState = true", code);
Assert.Contains("HorizontalAlignment = HorizontalAlignment.Center", code);
Assert.Contains("column.Header = headerCheckBox", code);
Assert.Contains("FaultRecordHeaderSelectionCheckBox_Click", code);
}

private static void AssertSetter(XElement style, string name, string value)
=> Assert.Contains(style.Elements(Wpf + "Setter"),
setter => (string?)setter.Attribute("Property") == name &&
(string?)setter.Attribute("Value") == value);

private static string Find(string path)
{
DirectoryInfo? d = new(AppContext.BaseDirectory);
while (d is not null)
{
var file = Path.Combine(d.FullName, path);
if (File.Exists(file)) return file;
d = d.Parent;
}
throw new FileNotFoundException(path);
}
}
Loading