3 Commits

Author SHA1 Message Date
zhengxuan.zhang 455e452ec2 修复测试用例 2026-05-14 20:28:31 +08:00
zhengxuan.zhang d3e75f3fac 在现有的 位置节点属性中新增一个 checkbox 按钮,来确认是否保存图片 2026-05-14 17:04:29 +08:00
zhengxuan.zhang ca22f59447 修复combox垂直居中 2026-05-14 17:01:50 +08:00
14 changed files with 213 additions and 59 deletions
@@ -747,5 +747,54 @@ internal sealed class SynchronousProgress<T> : IProgress<T>
});
}
[Fact]
public async Task SavePosition_WithSaveImage_RefreshesInputImageForFollowingInspectionModule()
{
var (service, mockStore, _, mockMainViewport, _) = CreateService();
var initialImage = CreateBitmapSource(1, 1);
var refreshedImage = CreateBitmapSource(2, 3);
mockMainViewport.SetupGet(m => m.LatestManualImage).Returns((ImageSource)null);
mockMainViewport.SetupSequence(m => m.CurrentDisplayImage)
.Returns(initialImage)
.Returns(refreshedImage);
List<InspectionAssetWriteRequest> capturedAssets = null;
mockStore.Setup(s => s.AppendNodeResultAsync(
It.IsAny<InspectionNodeResult>(),
It.IsAny<IEnumerable<InspectionMetricResult>>(),
It.IsAny<PipelineExecutionSnapshot>(),
It.IsAny<IEnumerable<InspectionAssetWriteRequest>>()))
.Callback<InspectionNodeResult, IEnumerable<InspectionMetricResult>, PipelineExecutionSnapshot, IEnumerable<InspectionAssetWriteRequest>>(
(_, __, ___, assets) => capturedAssets = assets?.ToList())
.Returns(Task.CompletedTask);
var program = new CncProgram(
Guid.NewGuid(),
"Program",
DateTime.UtcNow,
DateTime.UtcNow,
new List<CncNode>
{
new SavePositionNode(Guid.NewGuid(), 0, "Pos_0", MotionState.Default, SaveImage: true),
new InspectionModuleNode(Guid.NewGuid(), 1, "Inspect_0", new PipelineModel { Name = "Pipeline" })
}.AsReadOnly());
await service.ExecuteAsync(program, null, CancellationToken.None);
var inputAsset = Assert.Single(capturedAssets.Where(a => a.AssetType == InspectionAssetType.NodeInputImage));
Assert.Equal(2, inputAsset.Width);
Assert.Equal(3, inputAsset.Height);
}
private static BitmapSource CreateBitmapSource(int width, int height)
{
var stride = width * 4;
var pixels = new byte[stride * height];
var bitmap = BitmapSource.Create(width, height, 96, 96, PixelFormats.Bgra32, null, pixels, stride);
bitmap.Freeze();
return bitmap;
}
}
}
@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using Moq;
using XP.Common.Logging.Interfaces;
using XP.Hardware.RaySource.Services;
using XplorePlane.Models;
using XplorePlane.Services.AppState;
using XplorePlane.Services.Cnc;
using Xunit;
namespace XplorePlane.Tests.Services
{
public class CncProgramServiceTests
{
[Fact]
public void SerializeDeserialize_SavePosition_PreservesSaveImage()
{
var appState = new Mock<IAppStateService>();
appState.SetupGet(s => s.MotionState).Returns(MotionState.Default);
appState.SetupGet(s => s.RaySourceState).Returns(RaySourceState.Default);
appState.SetupGet(s => s.DetectorState).Returns(DetectorState.Default);
var raySource = new Mock<IRaySourceService>();
var logger = new Mock<ILoggerService>();
logger.Setup(l => l.ForModule<CncProgramService>()).Returns(logger.Object);
var service = new CncProgramService(appState.Object, raySource.Object, logger.Object);
var program = new CncProgram(
Guid.NewGuid(),
"Program",
DateTime.UtcNow,
DateTime.UtcNow,
new List<CncNode>
{
new SavePositionNode(Guid.NewGuid(), 0, "保存位置_0", MotionState.Default, SaveImage: true)
}.AsReadOnly());
var json = service.Serialize(program);
var deserialized = service.Deserialize(json);
var savePosition = Assert.IsType<SavePositionNode>(Assert.Single(deserialized.Nodes));
Assert.True(savePosition.SaveImage);
}
}
}
@@ -7,15 +7,25 @@ using FsCheck.Fluent;
using FsCheck.Xunit;
using XplorePlane.Models;
using XplorePlane.ViewModels.Cnc;
using Xunit;
namespace XplorePlane.Tests.ViewModels
{
public class CncNodeViewModelTests
{
// ── Property 11: 节点执行状态转换正确性 ──────────────────────────────
[Fact]
public void SavePosition_SaveImage_CanBeUpdated()
{
var node = new SavePositionNode(Guid.NewGuid(), 0, "Pos_0", MotionState.Default, SaveImage: false);
var vm = new CncNodeViewModel(node, (_, __) => { });
vm.SaveImage = true;
var updatedNode = Assert.IsType<SavePositionNode>(vm.Model);
Assert.True(vm.SaveImage);
Assert.True(updatedNode.SaveImage);
}
// Feature: cnc-run-execution, Property 11: 节点执行状态转换正确性
// Validates: Requirements 6.1, 6.2
[Property(MaxTest = 100)]
public Property ExecutionState_TransitionsProduceCorrectBoolProperties()
{
@@ -30,31 +40,27 @@ namespace XplorePlane.Tests.ViewModels
gen.ToArbitrary(),
node =>
{
var vm = new CncNodeViewModel(node, (vm2, n) => { });
var vm = new CncNodeViewModel(node, (_, __) => { });
// Running
vm.ExecutionState = NodeExecutionState.Running;
bool runningOk = vm.IsRunningNode == true
&& vm.IsSucceededNode == false
&& vm.IsFailedNode == false;
bool runningOk = vm.IsRunningNode
&& !vm.IsSucceededNode
&& !vm.IsFailedNode;
// Succeeded
vm.ExecutionState = NodeExecutionState.Succeeded;
bool succeededOk = vm.IsRunningNode == false
&& vm.IsSucceededNode == true
&& vm.IsFailedNode == false;
bool succeededOk = !vm.IsRunningNode
&& vm.IsSucceededNode
&& !vm.IsFailedNode;
// Failed
vm.ExecutionState = NodeExecutionState.Failed;
bool failedOk = vm.IsRunningNode == false
&& vm.IsSucceededNode == false
&& vm.IsFailedNode == true;
bool failedOk = !vm.IsRunningNode
&& !vm.IsSucceededNode
&& vm.IsFailedNode;
// Idle
vm.ExecutionState = NodeExecutionState.Idle;
bool idleOk = vm.IsRunningNode == false
&& vm.IsSucceededNode == false
&& vm.IsFailedNode == false;
bool idleOk = !vm.IsRunningNode
&& !vm.IsSucceededNode
&& !vm.IsFailedNode;
return runningOk && succeededOk && failedOk && idleOk;
});
+25
View File
@@ -0,0 +1,25 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.9.34616.47
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XplorePlane.Tests", "XplorePlane.Tests.csproj", "{840B1949-FED1-4340-9CCB-6143018FB274}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{840B1949-FED1-4340-9CCB-6143018FB274}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{840B1949-FED1-4340-9CCB-6143018FB274}.Debug|Any CPU.Build.0 = Debug|Any CPU
{840B1949-FED1-4340-9CCB-6143018FB274}.Release|Any CPU.ActiveCfg = Release|Any CPU
{840B1949-FED1-4340-9CCB-6143018FB274}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2600346F-BCA0-41DE-8F91-6671B9FC89BB}
EndGlobalSection
EndGlobal
-14
View File
@@ -66,8 +66,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XP.Calibration", "XP.Calibr
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "XP.Calibration", "XP.Calibration", "{D4E5F6A7-B8C9-0123-4567-89ABCDEF0123}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XplorePlane.Tests", "XplorePlane.Tests\XplorePlane.Tests.csproj", "{223E2A75-E50E-BD82-506F-935F63B7A41A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -265,18 +263,6 @@ Global
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x64.Build.0 = Release|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x86.ActiveCfg = Release|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x86.Build.0 = Release|Any CPU
{223E2A75-E50E-BD82-506F-935F63B7A41A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{223E2A75-E50E-BD82-506F-935F63B7A41A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{223E2A75-E50E-BD82-506F-935F63B7A41A}.Debug|x64.ActiveCfg = Debug|x64
{223E2A75-E50E-BD82-506F-935F63B7A41A}.Debug|x64.Build.0 = Debug|x64
{223E2A75-E50E-BD82-506F-935F63B7A41A}.Debug|x86.ActiveCfg = Debug|Any CPU
{223E2A75-E50E-BD82-506F-935F63B7A41A}.Debug|x86.Build.0 = Debug|Any CPU
{223E2A75-E50E-BD82-506F-935F63B7A41A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{223E2A75-E50E-BD82-506F-935F63B7A41A}.Release|Any CPU.Build.0 = Release|Any CPU
{223E2A75-E50E-BD82-506F-935F63B7A41A}.Release|x64.ActiveCfg = Release|Any CPU
{223E2A75-E50E-BD82-506F-935F63B7A41A}.Release|x64.Build.0 = Release|Any CPU
{223E2A75-E50E-BD82-506F-935F63B7A41A}.Release|x86.ActiveCfg = Release|Any CPU
{223E2A75-E50E-BD82-506F-935F63B7A41A}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
+2 -1
View File
@@ -85,7 +85,8 @@ namespace XplorePlane.Models
Guid Id,
int Index,
string Name,
MotionState MotionState) : CncNode(Id, Index, CncNodeType.SavePosition, Name);
MotionState MotionState,
bool SaveImage = false) : CncNode(Id, Index, CncNodeType.SavePosition, Name);
/// <summary>检测模块节点 | Inspection module node</summary>
public record InspectionModuleNode(
@@ -82,7 +82,8 @@ namespace XplorePlane.Services.Cnc
return;
int inspectionNodeCount = program.Nodes.OfType<InspectionModuleNode>().Count();
var sourceImage = TryGetSourceImage();
var runSourceImage = TryGetSourceImage();
var currentSourceImage = runSourceImage;
Guid runId;
try
@@ -95,15 +96,15 @@ namespace XplorePlane.Services.Cnc
};
InspectionAssetWriteRequest sourceAsset = null;
if (sourceImage != null)
if (runSourceImage != null)
{
sourceAsset = new InspectionAssetWriteRequest
{
AssetType = InspectionAssetType.RunSourceImage,
Content = EncodeBitmapToBmp(sourceImage),
Content = EncodeBitmapToBmp(runSourceImage),
FileFormat = "bmp",
Width = sourceImage.PixelWidth,
Height = sourceImage.PixelHeight
Width = runSourceImage.PixelWidth,
Height = runSourceImage.PixelHeight
};
}
@@ -155,13 +156,29 @@ namespace XplorePlane.Services.Cnc
"Executing save-position node [{Index}] {Name} | " +
"StageX={StageX} StageY={StageY} SourceZ={SourceZ} DetectorZ={DetectorZ} " +
"DetectorSwing={DetectorSwing} FDD={FDD} FOD={FOD} Magnification={Magnification} " +
"StageRotation={StageRotation} FixtureRotation={FixtureRotation}",
"StageRotation={StageRotation} FixtureRotation={FixtureRotation} SaveImage={SaveImage}",
sp.Index, sp.Name,
sp.MotionState.StageX, sp.MotionState.StageY,
sp.MotionState.SourceZ, sp.MotionState.DetectorZ,
sp.MotionState.DetectorSwing, sp.MotionState.FDD,
sp.MotionState.FOD, sp.MotionState.Magnification,
sp.MotionState.StageRotation, sp.MotionState.FixtureRotation);
sp.MotionState.StageRotation, sp.MotionState.FixtureRotation,
sp.SaveImage);
if (sp.SaveImage)
{
var capturedImage = TryGetSourceImage();
if (capturedImage != null)
{
currentSourceImage = capturedImage;
}
else
{
_logger.ForModule<CncExecutionService>().Warn(
"Save-position node '{0}' requested image capture, but no current image was available.",
sp.Name);
}
}
break;
case SaveNodeNode sn:
@@ -214,7 +231,7 @@ namespace XplorePlane.Services.Cnc
case InspectionModuleNode inspectionNode:
try
{
var img = await ExecuteInspectionNodeAsync(runId, inspectionNode, sourceImage, linkedCts.Token);
var img = await ExecuteInspectionNodeAsync(runId, inspectionNode, currentSourceImage, linkedCts.Token);
if (img != null) lastResultImage = img;
}
catch (Exception ex)
@@ -430,7 +430,8 @@ namespace XplorePlane.Services.Cnc
{
return new SavePositionNode(
id, index, $"检测位置_{index}",
MotionState: _appStateService.MotionState);
MotionState: _appStateService.MotionState,
SaveImage: false);
}
private double TryReadCurrent()
{
@@ -930,9 +930,7 @@ WHERE run_id = @run_id";
{
return Path.Combine(
"Results",
startedAt.Value.ToString("yyyy"),
startedAt.Value.ToString("MM"),
startedAt.Value.ToString("dd"),
startedAt.Value.ToString("yyyy-MM-dd"),
runId.ToString("D"));
}
@@ -420,7 +420,7 @@ namespace XplorePlane.ViewModels.Cnc
return;
var sb = new StringBuilder();
sb.AppendLine("Index,NodeType,Name,SourceZ,DetectorZ,StageX,StageY,DetectorSwing,StageRotation,FixtureRotation,FOD,FDD,Magnification,Voltage_kV,Current_uA,Power_W,RayOn,DetectorConnected,FrameRate,Resolution,ImageFile,MarkerType,MarkerX,MarkerY,DialogTitle,DialogMessage,DelayMs,Pipeline");
sb.AppendLine("Index,NodeType,Name,SourceZ,DetectorZ,StageX,StageY,DetectorSwing,StageRotation,FixtureRotation,FOD,FDD,Magnification,Voltage_kV,Current_uA,Power_W,RayOn,DetectorConnected,FrameRate,Resolution,ImageFile,SaveImage,MarkerType,MarkerX,MarkerY,DialogTitle,DialogMessage,DelayMs,Pipeline");
var inv = CultureInfo.InvariantCulture;
@@ -431,13 +431,13 @@ namespace XplorePlane.ViewModels.Cnc
ReferencePointNode rp => $"{rp.Index},{rp.NodeType},{Esc(rp.Name)},{rp.SourceZ.ToString(inv)},{rp.DetectorZ.ToString(inv)},{rp.StageX.ToString(inv)},{rp.StageY.ToString(inv)},{rp.DetectorSwing.ToString(inv)},{rp.StageRotation.ToString(inv)},{rp.FixtureRotation.ToString(inv)},{rp.FOD.ToString(inv)},{rp.FDD.ToString(inv)},{rp.Magnification.ToString(inv)},{rp.Voltage.ToString(inv)},{rp.Current.ToString(inv)},,{rp.IsRayOn},,,,,,,,,,",
SaveNodeWithImageNode sni => $"{sni.Index},{sni.NodeType},{Esc(sni.Name)},{sni.MotionState.SourceZ.ToString(inv)},{sni.MotionState.DetectorZ.ToString(inv)},{sni.MotionState.StageX.ToString(inv)},{sni.MotionState.StageY.ToString(inv)},{sni.MotionState.DetectorSwing.ToString(inv)},{sni.MotionState.StageRotation.ToString(inv)},{sni.MotionState.FixtureRotation.ToString(inv)},{sni.MotionState.FOD.ToString(inv)},{sni.MotionState.FDD.ToString(inv)},{sni.MotionState.Magnification.ToString(inv)},{sni.RaySourceState.Voltage.ToString(inv)},,{sni.RaySourceState.Power.ToString(inv)},{sni.RaySourceState.IsOn},{sni.DetectorState.IsConnected},{sni.DetectorState.FrameRate.ToString(inv)},{Esc(sni.DetectorState.Resolution)},{Esc(sni.ImageFileName)},,,,,,",
SaveNodeNode sn => $"{sn.Index},{sn.NodeType},{Esc(sn.Name)},{sn.MotionState.SourceZ.ToString(inv)},{sn.MotionState.DetectorZ.ToString(inv)},{sn.MotionState.StageX.ToString(inv)},{sn.MotionState.StageY.ToString(inv)},{sn.MotionState.DetectorSwing.ToString(inv)},{sn.MotionState.StageRotation.ToString(inv)},{sn.MotionState.FixtureRotation.ToString(inv)},{sn.MotionState.FOD.ToString(inv)},{sn.MotionState.FDD.ToString(inv)},{sn.MotionState.Magnification.ToString(inv)},{sn.RaySourceState.Voltage.ToString(inv)},,{sn.RaySourceState.Power.ToString(inv)},{sn.RaySourceState.IsOn},{sn.DetectorState.IsConnected},{sn.DetectorState.FrameRate.ToString(inv)},{Esc(sn.DetectorState.Resolution)},,,,,,,",
SavePositionNode sp => $"{sp.Index},{sp.NodeType},{Esc(sp.Name)},{sp.MotionState.SourceZ.ToString(inv)},{sp.MotionState.DetectorZ.ToString(inv)},{sp.MotionState.StageX.ToString(inv)},{sp.MotionState.StageY.ToString(inv)},{sp.MotionState.DetectorSwing.ToString(inv)},{sp.MotionState.StageRotation.ToString(inv)},{sp.MotionState.FixtureRotation.ToString(inv)},{sp.MotionState.FOD.ToString(inv)},{sp.MotionState.FDD.ToString(inv)},{sp.MotionState.Magnification.ToString(inv)},,,,,,,,,,,,,,",
InspectionModuleNode im => $"{im.Index},{im.NodeType},{Esc(im.Name)},,,,,,,,,,,,,,,,,,,,,,{Esc(im.Pipeline?.Name ?? string.Empty)}",
InspectionMarkerNode mk => $"{mk.Index},{mk.NodeType},{Esc(mk.Name)},,,,,,,,,,,,,,,,,{Esc(mk.MarkerType)},{mk.MarkerX.ToString(inv)},{mk.MarkerY.ToString(inv)},,,",
PauseDialogNode pd => $"{pd.Index},{pd.NodeType},{Esc(pd.Name)},,,,,,,,,,,,,,,,,,,{Esc(pd.DialogTitle)},{Esc(pd.DialogMessage)},,",
WaitDelayNode wd => $"{wd.Index},{wd.NodeType},{Esc(wd.Name)},,,,,,,,,,,,,,,,,,,,,{wd.DelayMilliseconds},",
CompleteProgramNode cp => $"{cp.Index},{cp.NodeType},{Esc(cp.Name)},,,,,,,,,,,,,,,,,,,,,,",
_ => $"{node.Index},{node.NodeType},{Esc(node.Name)},,,,,,,,,,,,,,,,,,,,,,"
SavePositionNode sp => $"{sp.Index},{sp.NodeType},{Esc(sp.Name)},{sp.MotionState.SourceZ.ToString(inv)},{sp.MotionState.DetectorZ.ToString(inv)},{sp.MotionState.StageX.ToString(inv)},{sp.MotionState.StageY.ToString(inv)},{sp.MotionState.DetectorSwing.ToString(inv)},{sp.MotionState.StageRotation.ToString(inv)},{sp.MotionState.FixtureRotation.ToString(inv)},{sp.MotionState.FOD.ToString(inv)},{sp.MotionState.FDD.ToString(inv)},{sp.MotionState.Magnification.ToString(inv)},,,,,,,,{sp.SaveImage},,,,,,,",
InspectionModuleNode im => $"{im.Index},{im.NodeType},{Esc(im.Name)},,,,,,,,,,,,,,,,,,,,,,,{Esc(im.Pipeline?.Name ?? string.Empty)}",
InspectionMarkerNode mk => $"{mk.Index},{mk.NodeType},{Esc(mk.Name)},,,,,,,,,,,,,,,,,,{Esc(mk.MarkerType)},{mk.MarkerX.ToString(inv)},{mk.MarkerY.ToString(inv)},,,",
PauseDialogNode pd => $"{pd.Index},{pd.NodeType},{Esc(pd.Name)},,,,,,,,,,,,,,,,,,,,{Esc(pd.DialogTitle)},{Esc(pd.DialogMessage)},,",
WaitDelayNode wd => $"{wd.Index},{wd.NodeType},{Esc(wd.Name)},,,,,,,,,,,,,,,,,,,,,,{wd.DelayMilliseconds},",
CompleteProgramNode cp => $"{cp.Index},{cp.NodeType},{Esc(cp.Name)},,,,,,,,,,,,,,,,,,,,,,,",
_ => $"{node.Index},{node.NodeType},{Esc(node.Name)},,,,,,,,,,,,,,,,,,,,,,,"
};
sb.AppendLine(row);
@@ -349,6 +349,18 @@ namespace XplorePlane.ViewModels.Cnc
}
}
public bool SaveImage
{
get => _model is SavePositionNode sp && sp.SaveImage;
set
{
if (_model is SavePositionNode sp)
{
UpdateModel(sp with { SaveImage = value });
}
}
}
public string PipelineName
{
get => _model is InspectionModuleNode im ? im.Pipeline?.Name ?? string.Empty : string.Empty;
@@ -636,6 +648,7 @@ namespace XplorePlane.ViewModels.Cnc
RaisePropertyChanged(nameof(FrameRate));
RaisePropertyChanged(nameof(Resolution));
RaisePropertyChanged(nameof(ImageFileName));
RaisePropertyChanged(nameof(SaveImage));
RaisePropertyChanged(nameof(Pipeline));
RaisePropertyChanged(nameof(PipelineName));
RaisePropertyChanged(nameof(MarkerType));
+9
View File
@@ -584,6 +584,15 @@
</StackPanel>
</GroupBox>
<GroupBox
Style="{StaticResource CompactGroupBox}"
Header="位置参数"
Visibility="{Binding SelectedNode.IsSavePosition, Converter={StaticResource BoolToVisibilityConverter}}">
<StackPanel Margin="8,8,8,6">
<CheckBox Style="{StaticResource EditorCheck}" Content="保存图像" IsChecked="{Binding SelectedNode.SaveImage}" />
</StackPanel>
</GroupBox>
<GroupBox
Style="{StaticResource CompactGroupBox}"
Header="检测标记"
+6 -3
View File
@@ -232,9 +232,12 @@ namespace XplorePlane.Views.Cnc
}
var label = EnsureCheckDisplayLabel(checkBox, bindingPath);
checkBox.IsEnabled = !isReadOnlyNode;
checkBox.Visibility = isReadOnlyNode ? Visibility.Collapsed : Visibility.Visible;
label.Visibility = isReadOnlyNode ? Visibility.Visible : Visibility.Collapsed;
bool allowEditWhenReadOnly = bindingPath == "SelectedNode.SaveImage";
bool showReadOnlyLabel = isReadOnlyNode && !allowEditWhenReadOnly;
checkBox.IsEnabled = !isReadOnlyNode || allowEditWhenReadOnly;
checkBox.Visibility = showReadOnlyLabel ? Visibility.Collapsed : Visibility.Visible;
label.Visibility = showReadOnlyLabel ? Visibility.Visible : Visibility.Collapsed;
}
}
@@ -411,7 +411,8 @@
Grid.Column="1"
MinHeight="24"
Padding="4,1"
HorizontalContentAlignment="Center"
HorizontalContentAlignment="Left"
VerticalContentAlignment="Center"
BorderBrush="#CDCBCB"
BorderThickness="1"
FontFamily="{StaticResource UiFont}"