除控制台外功能修改
This commit is contained in:
parent
310644500f
commit
b8a92220f9
@ -17,7 +17,7 @@ public sealed class MockDaq : MockDeviceBase, IDaq
|
||||
private double? _nextTriggerSec;
|
||||
|
||||
/// <summary>满量程电压(V)。</summary>
|
||||
public double NominalVoltage { get; set; } = 28.0;
|
||||
public double NominalVoltage { get; set; } = 5.0;
|
||||
|
||||
/// <summary>满量程电流(A)。</summary>
|
||||
public double NominalCurrent { get; set; } = 1.0;
|
||||
@ -49,6 +49,9 @@ public sealed class MockDaq : MockDeviceBase, IDaq
|
||||
public async Task<CurveData> CaptureAsync(
|
||||
int[] channels, int sampleRateHz, double durationSec, CancellationToken ct = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(channels);
|
||||
if (channels.Length == 0)
|
||||
throw new ArgumentException("At least one channel must be requested.", nameof(channels));
|
||||
if (sampleRateHz <= 0) throw new ArgumentOutOfRangeException(nameof(sampleRateHz));
|
||||
if (durationSec <= 0) throw new ArgumentOutOfRangeException(nameof(durationSec));
|
||||
|
||||
@ -57,9 +60,18 @@ public sealed class MockDaq : MockDeviceBase, IDaq
|
||||
await Task.Delay(simulatedDelayMs, ct).ConfigureAwait(false);
|
||||
|
||||
int n = (int)Math.Round(sampleRateHz * durationSec);
|
||||
var v = new double[n];
|
||||
var i = new double[n];
|
||||
var cardVoltage = new double[n];
|
||||
var cardCurrent = new double[n];
|
||||
double dt = 1.0 / sampleRateHz;
|
||||
int channelCount = Math.Max(1, _opts.ChannelCount);
|
||||
var requestedChannels = channels.Distinct().ToArray();
|
||||
foreach (int channel in requestedChannels)
|
||||
{
|
||||
if (channel < 0 || channel >= channelCount)
|
||||
throw new ArgumentOutOfRangeException(
|
||||
nameof(channels),
|
||||
$"Requested channel {channel} is outside the configured PCIe8586 channel count {channelCount}.");
|
||||
}
|
||||
|
||||
bool rising = NextCaptureIsRising;
|
||||
double delay = rising ? OnDelaySec : OffDelaySec;
|
||||
@ -81,7 +93,7 @@ public sealed class MockDaq : MockDeviceBase, IDaq
|
||||
voltage = tRel <= 0 ? NominalVoltage : NominalVoltage * Math.Exp(-tRel / TauSec);
|
||||
}
|
||||
voltage += NextGaussian() * NominalVoltage * 0.005;
|
||||
v[k] = voltage;
|
||||
cardVoltage[k] = voltage;
|
||||
|
||||
double current;
|
||||
if (rising)
|
||||
@ -89,16 +101,10 @@ public sealed class MockDaq : MockDeviceBase, IDaq
|
||||
else
|
||||
current = tRel <= 0 ? NominalCurrent : NominalCurrent * Math.Exp(-tRel / TauSec);
|
||||
current += NextGaussian() * NominalCurrent * 0.01;
|
||||
i[k] = current;
|
||||
cardCurrent[k] = current;
|
||||
}
|
||||
|
||||
return new CurveData
|
||||
{
|
||||
SampleRateHz = sampleRateHz,
|
||||
TriggerTimeSec = triggerSec,
|
||||
Voltage = v,
|
||||
Current = i
|
||||
};
|
||||
return BuildCurve(cardVoltage, cardCurrent, sampleRateHz, triggerSec, triggerIndex: 0, requestedChannels[0]);
|
||||
}
|
||||
|
||||
public Task<double[]> ReadInstantAsync(int[] channels, CancellationToken ct = default)
|
||||
@ -112,7 +118,7 @@ public sealed class MockDaq : MockDeviceBase, IDaq
|
||||
? DaqChannelRole.Unconfigured
|
||||
: Pcie8586Daq.ParseRole(cfg.Role);
|
||||
|
||||
result[k] = role switch
|
||||
double cardValue = role switch
|
||||
{
|
||||
DaqChannelRole.OutputVoltage or DaqChannelRole.InputVoltage =>
|
||||
NominalVoltage * (1 + (_rng.NextDouble() - 0.5) * 0.004),
|
||||
@ -120,6 +126,7 @@ public sealed class MockDaq : MockDeviceBase, IDaq
|
||||
NominalCurrent * (1 + (_rng.NextDouble() - 0.5) * 0.02),
|
||||
_ => 0
|
||||
};
|
||||
result[k] = ApplyCalibration(cardValue, cfg);
|
||||
}
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
@ -137,7 +144,7 @@ public sealed class MockDaq : MockDeviceBase, IDaq
|
||||
NextCaptureIsRising = request.IsRising;
|
||||
await action(ct).ConfigureAwait(false);
|
||||
_nextTriggerSec = request.PreTriggerMs / 1_000.0;
|
||||
var curve = await CaptureAsync(new[] { 0 }, sampleRate, durationSec, ct).ConfigureAwait(false);
|
||||
var curve = await CaptureAsync(Enumerable.Range(0, Math.Max(1, _opts.ChannelCount)).ToArray(), sampleRate, durationSec, ct).ConfigureAwait(false);
|
||||
int triggerIndex = (int)Math.Round(sampleRate * request.PreTriggerMs / 1_000.0);
|
||||
return new CurveData
|
||||
{
|
||||
@ -146,14 +153,60 @@ public sealed class MockDaq : MockDeviceBase, IDaq
|
||||
TriggerTimeSec = triggerIndex / (double)curve.SampleRateHz,
|
||||
Voltage = curve.Voltage,
|
||||
Current = curve.Current,
|
||||
EngineeringChannels = new[]
|
||||
{
|
||||
new CurveChannelData(0, DaqChannelRole.OutputVoltage, "模拟输出电压", "V", 1, 0, curve.Voltage),
|
||||
new CurveChannelData(1, DaqChannelRole.OutputCurrent, "模拟输出电流", "A", 1, 0, curve.Current!)
|
||||
}
|
||||
EngineeringChannels = curve.EngineeringChannels
|
||||
};
|
||||
}
|
||||
|
||||
private CurveData BuildCurve(
|
||||
double[] cardVoltage,
|
||||
double[] cardCurrent,
|
||||
int sampleRateHz,
|
||||
double triggerSec,
|
||||
int triggerIndex,
|
||||
int primaryPhysicalChannel)
|
||||
{
|
||||
var engineering = new List<CurveChannelData>();
|
||||
int channelCount = Math.Max(1, _opts.ChannelCount);
|
||||
for (int channel = 0; channel < channelCount; channel++)
|
||||
{
|
||||
var cfg = _opts.Channels.FirstOrDefault(x => x.PhysicalChannel == channel)
|
||||
?? new DaqChannelOptions { PhysicalChannel = channel, Name = $"CH{channel}" };
|
||||
var role = Pcie8586Daq.ParseRole(cfg.Role);
|
||||
var source = role is DaqChannelRole.OutputCurrent or DaqChannelRole.InputCurrent
|
||||
? cardCurrent
|
||||
: cardVoltage;
|
||||
var values = source.Select(x => ApplyCalibration(x, cfg)).ToArray();
|
||||
engineering.Add(new CurveChannelData(
|
||||
channel, role, cfg.Name, UnitFor(role), cfg.Scale, cfg.Offset, values));
|
||||
}
|
||||
|
||||
var outputVoltage = engineering.Where(x => x.Role == DaqChannelRole.OutputVoltage).ToArray();
|
||||
var primary = outputVoltage.Length == 1
|
||||
? outputVoltage[0]
|
||||
: engineering.FirstOrDefault(x => x.PhysicalChannel == primaryPhysicalChannel) ?? engineering.FirstOrDefault();
|
||||
var current = engineering.FirstOrDefault(x => x.Role == DaqChannelRole.OutputCurrent)?.Samples;
|
||||
|
||||
return new CurveData
|
||||
{
|
||||
SampleRateHz = sampleRateHz,
|
||||
TriggerSampleIndex = triggerIndex,
|
||||
TriggerTimeSec = triggerSec,
|
||||
Voltage = primary?.Samples ?? Array.Empty<double>(),
|
||||
Current = current,
|
||||
EngineeringChannels = engineering
|
||||
};
|
||||
}
|
||||
|
||||
private static double ApplyCalibration(double value, DaqChannelOptions? cfg) =>
|
||||
cfg is null ? value : value * cfg.Scale + cfg.Offset;
|
||||
|
||||
private static string UnitFor(DaqChannelRole role) => role switch
|
||||
{
|
||||
DaqChannelRole.OutputVoltage or DaqChannelRole.InputVoltage => "V",
|
||||
DaqChannelRole.OutputCurrent or DaqChannelRole.InputCurrent => "A",
|
||||
_ => ""
|
||||
};
|
||||
|
||||
/// <summary>由 ReadInstantAsync 衍生的"电流读数"接口(基础测试页用)。</summary>
|
||||
public (double volts, double amps) ReadVoltageCurrent(int channel, bool channelOn)
|
||||
{
|
||||
|
||||
@ -7,8 +7,9 @@ namespace SSPCTester.Logic.Testing;
|
||||
public static class BasicTestCriteria
|
||||
{
|
||||
public const double OnVoltageThreshold = 1.0;
|
||||
public const double OffVoltageThreshold = 0.5;
|
||||
public const double OffVoltageThreshold = 0.3;
|
||||
public const double CurrentPresentThreshold = 0.001;
|
||||
public const double OffCurrentThreshold = 0.3;
|
||||
public const int SwitchSettleDelayMs = 1_000;
|
||||
public const int OffSettleTimeoutMs = 2_000;
|
||||
public const int OffInitialDelayMs = 100;
|
||||
@ -27,7 +28,7 @@ public static class BasicTestCriteria
|
||||
HasVoltageData(voltage) && HasCurrentData(current);
|
||||
|
||||
public static bool IsOffState(double voltage, double current) =>
|
||||
IsOff(voltage) && !HasCurrentData(current);
|
||||
IsOff(voltage) && Math.Abs(current) < OffCurrentThreshold;
|
||||
|
||||
/// <summary>
|
||||
/// Waits until the channel is stably off: no voltage and no current for consecutive samples.
|
||||
|
||||
@ -15,10 +15,10 @@ public sealed class BasicTestCriteriaTests
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0.49, true)]
|
||||
[InlineData(-0.49, true)]
|
||||
[InlineData(0.50, false)]
|
||||
[InlineData(-0.50, false)]
|
||||
[InlineData(0.29, true)]
|
||||
[InlineData(-0.29, true)]
|
||||
[InlineData(0.30, false)]
|
||||
[InlineData(-0.30, false)]
|
||||
public void IsOff_UsesAbsoluteVoltageThreshold(double voltage, bool expected)
|
||||
{
|
||||
Assert.Equal(expected, BasicTestCriteria.IsOff(voltage));
|
||||
@ -35,12 +35,12 @@ public sealed class BasicTestCriteriaTests
|
||||
|
||||
[Theory]
|
||||
[InlineData(0.0, 0.0, true)]
|
||||
[InlineData(0.49, 0.0, true)]
|
||||
[InlineData(-0.49, 0.0, true)]
|
||||
[InlineData(0.0, 0.0005, true)]
|
||||
[InlineData(0.50, 0.0, false)]
|
||||
[InlineData(0.29, 0.0, true)]
|
||||
[InlineData(-0.29, 0.0, true)]
|
||||
[InlineData(0.0, 0.29, true)]
|
||||
[InlineData(0.30, 0.0, false)]
|
||||
[InlineData(0.75, 0.0, false)]
|
||||
[InlineData(0.0, 0.01, false)]
|
||||
[InlineData(0.0, 0.30, false)]
|
||||
[InlineData(28.0, 0.0, false)]
|
||||
public void IsOffState_RequiresNoVoltageAndNoCurrent(double voltage, double current, bool expected)
|
||||
{
|
||||
|
||||
@ -56,6 +56,51 @@ public sealed class DaqIntegrationTests
|
||||
Assert.False(await sspc.GetChannelStateAsync(3));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MockDaq_AppliesScaleAndOffset()
|
||||
{
|
||||
var options = new DeviceOptions();
|
||||
options.Daq.ClockDivider = 100_000;
|
||||
options.Daq.ChannelCount = 2;
|
||||
options.Daq.Channels[0].Role = nameof(DaqChannelRole.OutputVoltage);
|
||||
options.Daq.Channels[0].Name = "Vout";
|
||||
options.Daq.Channels[0].Scale = 2;
|
||||
options.Daq.Channels[0].Offset = 1;
|
||||
options.Daq.Channels[0].IsCalibrated = true;
|
||||
options.Daq.Channels[1].Role = nameof(DaqChannelRole.OutputCurrent);
|
||||
options.Daq.Channels[1].Name = "Iout";
|
||||
options.Daq.Channels[1].Scale = 3;
|
||||
options.Daq.Channels[1].Offset = -1;
|
||||
options.Daq.Channels[1].IsCalibrated = true;
|
||||
var daq = new MockDaq(Options.Create(options), NullLogger<MockDaq>.Instance)
|
||||
{
|
||||
NominalVoltage = 10,
|
||||
NominalCurrent = 1
|
||||
};
|
||||
|
||||
var curve = await daq.CaptureAroundActionAsync(
|
||||
new DaqActionCaptureRequest(10, 40, true),
|
||||
_ => Task.CompletedTask);
|
||||
var instant = await daq.ReadInstantAsync(new[] { 0, 1 });
|
||||
|
||||
Assert.True(curve.Voltage.Max() > 20);
|
||||
Assert.True(curve.Current!.Max() > 1.8);
|
||||
Assert.Contains(curve.EngineeringChannels, x =>
|
||||
x.PhysicalChannel == 0 &&
|
||||
x.Role == DaqChannelRole.OutputVoltage &&
|
||||
x.Name == "Vout" &&
|
||||
x.Scale == 2 &&
|
||||
x.Offset == 1);
|
||||
Assert.Contains(curve.EngineeringChannels, x =>
|
||||
x.PhysicalChannel == 1 &&
|
||||
x.Role == DaqChannelRole.OutputCurrent &&
|
||||
x.Name == "Iout" &&
|
||||
x.Scale == 3 &&
|
||||
x.Offset == -1);
|
||||
Assert.InRange(instant[0], 20.9, 21.1);
|
||||
Assert.InRange(instant[1], 1.9, 2.1);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(1000, 100_000)]
|
||||
[InlineData(100, 1_000_000)]
|
||||
|
||||
@ -298,6 +298,36 @@ public partial class CurveTestViewModel : ObservableObject
|
||||
[RelayCommand]
|
||||
private Task CaptureFalling() => CaptureSingleCurveAsync(CurveCaptureKind.Falling);
|
||||
|
||||
[RelayCommand]
|
||||
private void Clear()
|
||||
{
|
||||
foreach (var row in Rows)
|
||||
{
|
||||
row.OnTimeMs = 0;
|
||||
row.RiseTimeMs = 0;
|
||||
row.OffTimeMs = 0;
|
||||
row.FallTimeMs = 0;
|
||||
row.RisingWaveformPath = null;
|
||||
row.FallingWaveformPath = null;
|
||||
row.ViewStartMs = null;
|
||||
row.ViewEndMs = null;
|
||||
row.RisingViewStartMs = null;
|
||||
row.RisingViewEndMs = null;
|
||||
row.FallingViewStartMs = null;
|
||||
row.FallingViewEndMs = null;
|
||||
row.Status = TestStatus.Untested;
|
||||
}
|
||||
|
||||
RisingCurve = null;
|
||||
FallingCurve = null;
|
||||
RisingViewStartMs = null;
|
||||
RisingViewEndMs = null;
|
||||
FallingViewStartMs = null;
|
||||
FallingViewEndMs = null;
|
||||
SaveProjectQuietly();
|
||||
_log.Add(UiLogLevel.Info, "开启与关断曲线结果已清空。");
|
||||
}
|
||||
|
||||
private async Task CaptureSingleCurveAsync(CurveCaptureKind kind)
|
||||
{
|
||||
if (Project == null || Busy) return;
|
||||
|
||||
@ -181,6 +181,23 @@ public partial class PowerTestViewModel : ObservableObject
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void Clear()
|
||||
{
|
||||
foreach (var row in Rows)
|
||||
{
|
||||
row.Vin = 0;
|
||||
row.Iin = 0;
|
||||
row.Vout = 0;
|
||||
row.Iout = 0;
|
||||
row.MeasuredPowerOut = null;
|
||||
row.Status = TestStatus.Untested;
|
||||
}
|
||||
|
||||
Current = Rows.FirstOrDefault();
|
||||
_log.Add(UiLogLevel.Info, "功率损耗结果已清空。");
|
||||
}
|
||||
|
||||
/// <summary>解析用于输入端电压的高速采集卡通道(取已标定的电压通道,优先 OutputVoltage)。</summary>
|
||||
private static int ResolveDaqVoltageChannel(DaqOptions options)
|
||||
{
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
<RowDefinition Height="2*" MinHeight="180" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Text="开启与关断曲线(覆盖指标 1.1–1.6)" FontSize="{StaticResource FsH1}" FontWeight="Bold" Margin="0,0,0,12" />
|
||||
<TextBlock Text="开启与关断曲线" FontSize="{StaticResource FsH1}" FontWeight="Bold" Margin="0,0,0,12" />
|
||||
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
@ -144,7 +144,17 @@
|
||||
|
||||
<Border Style="{StaticResource Card}" Margin="0,0,12,0">
|
||||
<DockPanel>
|
||||
<TextBlock DockPanel.Dock="Top" Style="{StaticResource CardTitle}" Text="各通道时间参数" />
|
||||
<Grid DockPanel.Dock="Top" Margin="0,0,0,8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Style="{StaticResource CardTitle}" Text="各通道时间参数" />
|
||||
<Button Grid.Column="1"
|
||||
Content="清空结果"
|
||||
Command="{Binding ClearCommand}"
|
||||
IsEnabled="{Binding Busy, Converter={StaticResource InverseBoolConverter}}" />
|
||||
</Grid>
|
||||
<DataGrid ItemsSource="{Binding Rows}"
|
||||
SelectedItem="{Binding SelectedRow, Mode=TwoWay}">
|
||||
<DataGrid.Columns>
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
<RowDefinition Height="3*" MinHeight="220" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Text="功率损耗(采集输入/输出两端数据)" FontSize="{StaticResource FsH1}" FontWeight="Bold" Margin="0,0,0,12" />
|
||||
<TextBlock Text="功率损耗" FontSize="{StaticResource FsH1}" FontWeight="Bold" Margin="0,0,0,12" />
|
||||
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
@ -178,7 +178,17 @@
|
||||
|
||||
<Border Style="{StaticResource Card}" Margin="0,0,12,0">
|
||||
<DockPanel>
|
||||
<TextBlock DockPanel.Dock="Top" Style="{StaticResource CardTitle}" Text="各通道汇总" />
|
||||
<Grid DockPanel.Dock="Top" Margin="0,0,0,8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Style="{StaticResource CardTitle}" Text="各通道汇总" />
|
||||
<Button Grid.Column="1"
|
||||
Content="清空结果"
|
||||
Command="{Binding ClearCommand}"
|
||||
IsEnabled="{Binding CanOperate}" />
|
||||
</Grid>
|
||||
<DataGrid ItemsSource="{Binding Rows}">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="通道" Width="60">
|
||||
|
||||
@ -15,7 +15,6 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel>
|
||||
<TextBlock Text="{Binding Project.Name, StringFormat='项目:{0}'}" FontSize="{StaticResource FsH1}" FontWeight="Bold" />
|
||||
<TextBlock Text="填写被测件信息并选择测试项。" Foreground="{StaticResource TextSecondaryBrush}" Margin="0,4,0,0" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
<RowDefinition Height="2*" MinHeight="200" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Text="保护功能(覆盖指标 11/14/15/17/19/21/22/23)" FontSize="{StaticResource FsH1}" FontWeight="Bold" Margin="0,0,0,8" />
|
||||
<TextBlock Text="保护功能" FontSize="{StaticResource FsH1}" FontWeight="Bold" Margin="0,0,0,8" />
|
||||
|
||||
|
||||
|
||||
|
||||
@ -18,7 +18,10 @@
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="测试报告" FontSize="{StaticResource FsH1}" FontWeight="Bold" />
|
||||
<TextBlock FontSize="{StaticResource FsH1}" FontWeight="Bold">
|
||||
<Run Text="测试报告:" />
|
||||
<Run Text="{Binding Project.Name, Mode=OneWay}" />
|
||||
</TextBlock>
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal">
|
||||
<ComboBox Width="130"
|
||||
Margin="0,0,8,0"
|
||||
|
||||
@ -7,19 +7,31 @@
|
||||
mc:Ignorable="d"
|
||||
d:DataContext="{d:DesignInstance Type=vm:TestHostViewModel}">
|
||||
<Border Background="{StaticResource BgSurfaceBrush}" BorderBrush="{StaticResource BorderSubtleBrush}" BorderThickness="0,0,0,1">
|
||||
<TabControl x:Name="Tabs">
|
||||
<TabItem Header="基础测试">
|
||||
<ContentControl Content="{Binding Basic}" />
|
||||
</TabItem>
|
||||
<TabItem Header="开启与关断曲线">
|
||||
<ContentControl Content="{Binding Curve}" />
|
||||
</TabItem>
|
||||
<TabItem Header="功率损耗">
|
||||
<ContentControl Content="{Binding Power}" />
|
||||
</TabItem>
|
||||
<TabItem Header="保护功能">
|
||||
<ContentControl Content="{Binding Protect}" />
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
<Grid>
|
||||
<TabControl x:Name="Tabs">
|
||||
<TabItem Header="基础测试">
|
||||
<ContentControl Content="{Binding Basic}" />
|
||||
</TabItem>
|
||||
<TabItem Header="开启与关断曲线">
|
||||
<ContentControl Content="{Binding Curve}" />
|
||||
</TabItem>
|
||||
<TabItem Header="功率损耗">
|
||||
<ContentControl Content="{Binding Power}" />
|
||||
</TabItem>
|
||||
<TabItem Header="保护功能">
|
||||
<ContentControl Content="{Binding Protect}" />
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
|
||||
<TextBlock Text="{Binding Project.Name, StringFormat='当前项目:{0}'}"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top"
|
||||
Margin="0,10,20,0"
|
||||
Foreground="{StaticResource TextSecondaryBrush}"
|
||||
FontWeight="SemiBold"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
MaxWidth="420"
|
||||
IsHitTestVisible="False" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</UserControl>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user