This commit is contained in:
Курнат Андрей
2026-03-14 14:31:52 +03:00
parent a0f50842f5
commit 13e451acf5
12 changed files with 904 additions and 6 deletions

View File

@@ -13,6 +13,8 @@ namespace XLAB
IReadOnlyList<string> ShowCloneVerificationDialog(CloneVerificationSeed seed); IReadOnlyList<string> ShowCloneVerificationDialog(CloneVerificationSeed seed);
SpnmtpDirectoryItem ShowSpnmtpEditDialog(SpnmtpDirectoryItem seed, bool isNew, IReadOnlyList<SpnmtpDirectoryItem> existingItems);
VerificationEditResult ShowVerificationDialog( VerificationEditResult ShowVerificationDialog(
VerificationEditSeed seed, VerificationEditSeed seed,
IReadOnlyList<PersonReference> verifiers, IReadOnlyList<PersonReference> verifiers,
@@ -76,6 +78,16 @@ namespace XLAB
return result.HasValue && result.Value ? viewModel.GetSerialNumbers() : null; return result.HasValue && result.Value ? viewModel.GetSerialNumbers() : null;
} }
public SpnmtpDirectoryItem ShowSpnmtpEditDialog(SpnmtpDirectoryItem seed, bool isNew, IReadOnlyList<SpnmtpDirectoryItem> existingItems)
{
var viewModel = new SpnmtpEditWindowViewModel(seed, isNew, existingItems);
var window = new SpnmtpEditWindow(viewModel);
window.Owner = _owner;
var result = window.ShowDialog();
return result.HasValue && result.Value ? viewModel.ToResult() : null;
}
public VerificationEditResult ShowVerificationDialog( public VerificationEditResult ShowVerificationDialog(
VerificationEditSeed seed, VerificationEditSeed seed,
IReadOnlyList<PersonReference> verifiers, IReadOnlyList<PersonReference> verifiers,

View File

@@ -9,6 +9,20 @@
WindowState="Maximized" WindowState="Maximized"
Loaded="Window_Loaded"> Loaded="Window_Loaded">
<Grid Margin="12"> <Grid Margin="12">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Menu Grid.Row="0"
Margin="0,0,0,12">
<MenuItem Header="Справочники">
<MenuItem Header="Наименования типов СИ"
Click="SpnmtpDirectoryMenuItem_Click" />
</MenuItem>
</Menu>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="430" /> <ColumnDefinition Width="430" />
<ColumnDefinition Width="12" /> <ColumnDefinition Width="12" />
@@ -322,4 +336,5 @@
</GroupBox> </GroupBox>
</Grid> </Grid>
</Grid> </Grid>
</Grid>
</Window> </Window>

View File

@@ -45,6 +45,13 @@ namespace XLAB
} }
} }
private void SpnmtpDirectoryMenuItem_Click(object sender, RoutedEventArgs e)
{
var window = new SpnmtpDirectoryWindow();
window.Owner = this;
window.ShowDialog();
}
private async void Window_Loaded(object sender, RoutedEventArgs e) private async void Window_Loaded(object sender, RoutedEventArgs e)
{ {
await _viewModel.InitializeAsync(); await _viewModel.InitializeAsync();

View File

@@ -146,6 +146,153 @@ ORDER BY fr.NMFRD, v.IDVDODVDD;";
return forms; return forms;
} }
public IReadOnlyList<SpnmtpDirectoryItem> LoadSpnmtpItems()
{
const string sql = @"
SELECT
s.IDSPNMTP AS Id,
s.NMTP AS Name,
s.NMTPSP AS SpecialName
FROM dbo.SPNMTP s
ORDER BY s.NMTP, s.IDSPNMTP;";
var items = new List<SpnmtpDirectoryItem>();
using (var connection = CreateConnection())
using (var command = new SqlCommand(sql, connection))
{
connection.Open();
command.CommandTimeout = 60;
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
items.Add(new SpnmtpDirectoryItem
{
Id = GetInt32(reader, "Id"),
Name = GetString(reader, "Name"),
SpecialName = GetString(reader, "SpecialName")
});
}
}
}
return items;
}
public int AddSpnmtpItem(SpnmtpDirectoryItem item)
{
var normalizedItem = NormalizeSpnmtpItem(item);
const string sql = @"
INSERT INTO dbo.SPNMTP
(
NMTP,
NMTPSP
)
VALUES
(
@Name,
@SpecialName
);
SELECT CAST(SCOPE_IDENTITY() AS int);";
using (var connection = CreateConnection())
using (var command = new SqlCommand(sql, connection))
{
connection.Open();
EnsureSpnmtpNameIsUnique(connection, normalizedItem.Name, null);
command.CommandTimeout = 60;
command.Parameters.Add("@Name", SqlDbType.NVarChar, SpnmtpDirectoryRules.NameMaxLength).Value = normalizedItem.Name;
command.Parameters.Add("@SpecialName", SqlDbType.VarChar, SpnmtpDirectoryRules.SpecialNameMaxLength).Value = (object)normalizedItem.SpecialName ?? DBNull.Value;
try
{
return Convert.ToInt32(command.ExecuteScalar());
}
catch (SqlException ex) when (IsSpnmtpDuplicateNameViolation(ex))
{
throw CreateSpnmtpDuplicateNameException(normalizedItem.Name, ex);
}
}
}
public void UpdateSpnmtpItem(SpnmtpDirectoryItem item)
{
var normalizedItem = NormalizeSpnmtpItem(item);
if (normalizedItem.Id <= 0)
{
throw new InvalidOperationException("Не выбрана запись SPNMTP для изменения.");
}
const string sql = @"
UPDATE dbo.SPNMTP
SET NMTP = @Name,
NMTPSP = @SpecialName
WHERE IDSPNMTP = @Id;
SELECT @@ROWCOUNT;";
using (var connection = CreateConnection())
using (var command = new SqlCommand(sql, connection))
{
connection.Open();
EnsureSpnmtpNameIsUnique(connection, normalizedItem.Name, normalizedItem.Id);
command.CommandTimeout = 60;
command.Parameters.Add("@Id", SqlDbType.Int).Value = normalizedItem.Id;
command.Parameters.Add("@Name", SqlDbType.NVarChar, SpnmtpDirectoryRules.NameMaxLength).Value = normalizedItem.Name;
command.Parameters.Add("@SpecialName", SqlDbType.VarChar, SpnmtpDirectoryRules.SpecialNameMaxLength).Value = (object)normalizedItem.SpecialName ?? DBNull.Value;
try
{
if (Convert.ToInt32(command.ExecuteScalar()) == 0)
{
throw new InvalidOperationException("Запись SPNMTP для изменения не найдена.");
}
}
catch (SqlException ex) when (IsSpnmtpDuplicateNameViolation(ex))
{
throw CreateSpnmtpDuplicateNameException(normalizedItem.Name, ex);
}
}
}
public void DeleteSpnmtpItem(int id)
{
if (id <= 0)
{
throw new InvalidOperationException("Не выбрана запись SPNMTP для удаления.");
}
const string sql = @"
DELETE FROM dbo.SPNMTP
WHERE IDSPNMTP = @Id;
SELECT @@ROWCOUNT;";
try
{
using (var connection = CreateConnection())
using (var command = new SqlCommand(sql, connection))
{
connection.Open();
command.CommandTimeout = 60;
command.Parameters.Add("@Id", SqlDbType.Int).Value = id;
if (Convert.ToInt32(command.ExecuteScalar()) == 0)
{
throw new InvalidOperationException("Запись SPNMTP для удаления не найдена.");
}
}
}
catch (SqlException ex) when (ex.Number == 547)
{
throw new InvalidOperationException("Запись справочника используется в связанных данных и не может быть удалена.", ex);
}
}
public IReadOnlyList<PsvDocumentSummary> LoadDocuments() public IReadOnlyList<PsvDocumentSummary> LoadDocuments()
{ {
const string sql = @" const string sql = @"
@@ -2136,6 +2283,99 @@ WHERE z.IDEKZ = @InstrumentId
return value.Trim(); return value.Trim();
} }
private static SpnmtpDirectoryItem NormalizeSpnmtpItem(SpnmtpDirectoryItem item)
{
if (item == null)
{
throw new ArgumentNullException("item");
}
var name = NormalizeRequiredSpnmtpValue(
item.Name,
"Наименование типа СИ не заполнено.",
SpnmtpDirectoryRules.NameMaxLength,
"Наименование типа СИ превышает допустимую длину.");
var specialName = NormalizeOptionalSpnmtpValue(
item.SpecialName,
SpnmtpDirectoryRules.SpecialNameMaxLength,
"Специальное наименование типа превышает допустимую длину.");
return new SpnmtpDirectoryItem
{
Id = item.Id,
Name = name,
SpecialName = specialName
};
}
private static void EnsureSpnmtpNameIsUnique(SqlConnection connection, string name, int? excludeId)
{
const string sql = @"
SELECT TOP (1) IDSPNMTP
FROM dbo.SPNMTP
WHERE NMTP = @Name
AND (@ExcludeId IS NULL OR IDSPNMTP <> @ExcludeId);";
using (var command = new SqlCommand(sql, connection))
{
command.CommandTimeout = 60;
command.Parameters.Add("@Name", SqlDbType.NVarChar, SpnmtpDirectoryRules.NameMaxLength).Value = name;
command.Parameters.Add("@ExcludeId", SqlDbType.Int).Value = (object)excludeId ?? DBNull.Value;
var existingId = command.ExecuteScalar();
if (existingId != null && existingId != DBNull.Value)
{
throw CreateSpnmtpDuplicateNameException(name, null);
}
}
}
private static InvalidOperationException CreateSpnmtpDuplicateNameException(string name, Exception innerException)
{
return new InvalidOperationException(
string.Format("Наименование типа СИ \"{0}\" уже существует в справочнике.", name),
innerException);
}
private static bool IsSpnmtpDuplicateNameViolation(SqlException ex)
{
return ex != null
&& (ex.Number == 2601 || ex.Number == 2627)
&& ex.Message.IndexOf("XAK1SPNMTP", StringComparison.OrdinalIgnoreCase) >= 0;
}
private static string NormalizeRequiredSpnmtpValue(string value, string emptyErrorMessage, int maxLength, string tooLongErrorMessage)
{
if (string.IsNullOrWhiteSpace(value))
{
throw new InvalidOperationException(emptyErrorMessage);
}
var normalizedValue = value.Trim();
if (normalizedValue.Length > maxLength)
{
throw new InvalidOperationException(tooLongErrorMessage);
}
return normalizedValue;
}
private static string NormalizeOptionalSpnmtpValue(string value, int maxLength, string tooLongErrorMessage)
{
if (string.IsNullOrWhiteSpace(value))
{
return null;
}
var normalizedValue = value.Trim();
if (normalizedValue.Length > maxLength)
{
throw new InvalidOperationException(tooLongErrorMessage);
}
return normalizedValue;
}
private static int UpdateDocumentHeader(SqlConnection connection, SqlTransaction transaction, string currentDocumentNumber, DocumentEditorResult document) private static int UpdateDocumentHeader(SqlConnection connection, SqlTransaction transaction, string currentDocumentNumber, DocumentEditorResult document)
{ {
const string sql = @" const string sql = @"

View File

@@ -344,6 +344,22 @@ namespace XLAB
} }
} }
public sealed class SpnmtpDirectoryItem
{
public int Id { get; set; }
public string Name { get; set; }
public string SpecialName { get; set; }
}
internal static class SpnmtpDirectoryRules
{
public const int NameMaxLength = 150;
public const int SpecialNameMaxLength = 150;
}
public sealed class DocumentFormReference public sealed class DocumentFormReference
{ {
public int DocumentFormId { get; set; } public int DocumentFormId { get; set; }

View File

@@ -0,0 +1,86 @@
<Window x:Class="XLAB.SpnmtpDirectoryWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Наименования типов СИ"
Height="680"
Width="980"
MinHeight="520"
MinWidth="820"
Loaded="Window_Loaded"
WindowStartupLocation="CenterOwner">
<Grid Margin="12">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<DockPanel Grid.Row="0"
Margin="0,0,0,8">
<StackPanel DockPanel.Dock="Left"
Orientation="Horizontal">
<TextBlock Width="170"
VerticalAlignment="Center"
Text="Поиск по справочнику" />
<TextBox Width="320"
Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
<StackPanel DockPanel.Dock="Right"
Orientation="Horizontal"
HorizontalAlignment="Right">
<Button Width="110"
Margin="8,0,0,0"
Command="{Binding RefreshCommand}"
Content="Обновить" />
</StackPanel>
</DockPanel>
<DataGrid Grid.Row="1"
ItemsSource="{Binding ItemsView}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
AutoGenerateColumns="False"
CanUserAddRows="False"
IsReadOnly="True"
HeadersVisibility="Column">
<DataGrid.Columns>
<DataGridTextColumn Header="ID"
Width="90"
Binding="{Binding Id}" />
<DataGridTextColumn Header="Наименование типа СИ"
Width="*"
Binding="{Binding Name}" />
<DataGridTextColumn Header="Специальное наименование типа"
Width="260"
Binding="{Binding SpecialName}" />
</DataGrid.Columns>
</DataGrid>
<TextBlock Grid.Row="2"
Margin="0,8,0,0"
Foreground="DimGray"
Text="{Binding StatusText}" />
<StackPanel Grid.Row="3"
Margin="0,12,0,0"
Orientation="Horizontal"
HorizontalAlignment="Right">
<Button Width="110"
Margin="0,0,8,0"
Command="{Binding AddCommand}"
Content="Добавить" />
<Button Width="110"
Margin="0,0,8,0"
Command="{Binding EditCommand}"
Content="Изменить" />
<Button Width="110"
Margin="0,0,8,0"
Command="{Binding DeleteCommand}"
Content="Удалить" />
<Button Width="90"
IsCancel="True"
Content="Закрыть" />
</StackPanel>
</Grid>
</Window>

View File

@@ -0,0 +1,21 @@
using System.Windows;
namespace XLAB
{
public partial class SpnmtpDirectoryWindow : Window
{
private readonly SpnmtpDirectoryWindowViewModel _viewModel;
public SpnmtpDirectoryWindow()
{
InitializeComponent();
_viewModel = new SpnmtpDirectoryWindowViewModel(new PsvDataService(), new DialogService(this));
DataContext = _viewModel;
}
private async void Window_Loaded(object sender, RoutedEventArgs e)
{
await _viewModel.InitializeAsync();
}
}
}

View File

@@ -0,0 +1,272 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Data;
using System.Windows.Input;
namespace XLAB
{
internal sealed class SpnmtpDirectoryWindowViewModel : ObservableObject
{
private readonly IDialogService _dialogService;
private readonly PsvDataService _service;
private bool _isBusy;
private string _searchText;
private SpnmtpDirectoryItem _selectedItem;
private string _statusText;
public SpnmtpDirectoryWindowViewModel(PsvDataService service, IDialogService dialogService)
{
_service = service;
_dialogService = dialogService;
Items = new ObservableCollection<SpnmtpDirectoryItem>();
ItemsView = CollectionViewSource.GetDefaultView(Items);
ItemsView.Filter = FilterItems;
AddCommand = new RelayCommand(delegate { AddItemAsync(); }, delegate { return !IsBusy; });
DeleteCommand = new RelayCommand(delegate { DeleteSelectedItemAsync(); }, delegate { return !IsBusy && SelectedItem != null; });
EditCommand = new RelayCommand(delegate { EditSelectedItemAsync(); }, delegate { return !IsBusy && SelectedItem != null; });
RefreshCommand = new RelayCommand(delegate { RefreshAsync(); }, delegate { return !IsBusy; });
UpdateStatus();
}
public ICommand AddCommand { get; private set; }
public ICommand DeleteCommand { get; private set; }
public ICommand EditCommand { get; private set; }
public ObservableCollection<SpnmtpDirectoryItem> Items { get; private set; }
public ICollectionView ItemsView { get; private set; }
public bool IsBusy
{
get { return _isBusy; }
private set
{
if (SetProperty(ref _isBusy, value))
{
RaiseCommandStates();
}
}
}
public ICommand RefreshCommand { get; private set; }
public string SearchText
{
get { return _searchText; }
set
{
if (SetProperty(ref _searchText, value))
{
ItemsView.Refresh();
UpdateStatus();
}
}
}
public SpnmtpDirectoryItem SelectedItem
{
get { return _selectedItem; }
set
{
if (SetProperty(ref _selectedItem, value))
{
RaiseCommandStates();
}
}
}
public string StatusText
{
get { return _statusText; }
private set { SetProperty(ref _statusText, value); }
}
public async Task InitializeAsync()
{
await ExecuteBusyOperationAsync(delegate { return RefreshCoreAsync(null); });
}
private void AddItemAsync()
{
var result = _dialogService.ShowSpnmtpEditDialog(new SpnmtpDirectoryItem(), true, Items.ToList());
if (result == null)
{
return;
}
RunMutationOperation(async delegate
{
var createdId = await Task.Run(delegate { return _service.AddSpnmtpItem(result); });
await RefreshCoreAsync(createdId);
_dialogService.ShowInfo("Запись справочника добавлена.");
});
}
private bool Contains(string source, string searchText)
{
return !string.IsNullOrWhiteSpace(source)
&& source.IndexOf(searchText, StringComparison.OrdinalIgnoreCase) >= 0;
}
private void DeleteSelectedItemAsync()
{
if (SelectedItem == null)
{
return;
}
var selectedItem = SelectedItem;
if (!_dialogService.Confirm(string.Format("Удалить запись \"{0}\"?", selectedItem.Name)))
{
return;
}
RunMutationOperation(async delegate
{
await Task.Run(delegate { _service.DeleteSpnmtpItem(selectedItem.Id); });
await RefreshCoreAsync(null);
_dialogService.ShowInfo("Запись справочника удалена.");
});
}
private void EditSelectedItemAsync()
{
if (SelectedItem == null)
{
return;
}
var seed = new SpnmtpDirectoryItem
{
Id = SelectedItem.Id,
Name = SelectedItem.Name,
SpecialName = SelectedItem.SpecialName
};
var result = _dialogService.ShowSpnmtpEditDialog(seed, false, Items.ToList());
if (result == null)
{
return;
}
RunMutationOperation(async delegate
{
await Task.Run(delegate { _service.UpdateSpnmtpItem(result); });
await RefreshCoreAsync(result.Id);
_dialogService.ShowInfo("Запись справочника обновлена.");
});
}
private async Task ExecuteBusyOperationAsync(Func<Task> operation)
{
try
{
IsBusy = true;
await operation();
}
catch (Exception ex)
{
_dialogService.ShowError(ex.Message);
}
finally
{
IsBusy = false;
}
}
private async Task ExecuteMutationOperationAsync(Func<Task> operation)
{
try
{
IsBusy = true;
await operation();
}
catch (InvalidOperationException ex)
{
_dialogService.ShowWarning(ex.Message);
}
catch (Exception ex)
{
_dialogService.ShowError(ex.Message);
}
finally
{
IsBusy = false;
}
}
private bool FilterItems(object item)
{
var directoryItem = item as SpnmtpDirectoryItem;
if (directoryItem == null)
{
return false;
}
if (string.IsNullOrWhiteSpace(SearchText))
{
return true;
}
return Contains(directoryItem.Name, SearchText)
|| Contains(directoryItem.SpecialName, SearchText)
|| directoryItem.Id.ToString().IndexOf(SearchText, StringComparison.OrdinalIgnoreCase) >= 0;
}
private async Task RefreshCoreAsync(int? idToSelect)
{
var items = await Task.Run(delegate { return _service.LoadSpnmtpItems(); });
var currentId = idToSelect ?? (SelectedItem == null ? (int?)null : SelectedItem.Id);
Items.Clear();
foreach (var item in items)
{
Items.Add(item);
}
ItemsView.Refresh();
SelectedItem = currentId.HasValue
? Items.FirstOrDefault(delegate(SpnmtpDirectoryItem item) { return item.Id == currentId.Value; })
: Items.FirstOrDefault();
UpdateStatus();
}
private void RefreshAsync()
{
RunBusyOperation(delegate { return RefreshCoreAsync(null); });
}
private void RaiseCommandStates()
{
((RelayCommand)AddCommand).RaiseCanExecuteChanged();
((RelayCommand)DeleteCommand).RaiseCanExecuteChanged();
((RelayCommand)EditCommand).RaiseCanExecuteChanged();
((RelayCommand)RefreshCommand).RaiseCanExecuteChanged();
}
private async void RunBusyOperation(Func<Task> operation)
{
await ExecuteBusyOperationAsync(operation);
}
private async void RunMutationOperation(Func<Task> operation)
{
await ExecuteMutationOperationAsync(operation);
}
private void UpdateStatus()
{
var visibleCount = ItemsView.Cast<object>().Count();
StatusText = string.Format("Всего записей: {0}. По фильтру: {1}.", Items.Count, visibleCount);
}
}
}

View File

@@ -0,0 +1,64 @@
<Window x:Class="XLAB.SpnmtpEditWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="{Binding Title}"
Height="250"
Width="560"
MinHeight="230"
MinWidth="520"
ResizeMode="NoResize"
WindowStartupLocation="CenterOwner">
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="220" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0"
Grid.Column="0"
Margin="0,0,12,8"
VerticalAlignment="Center"
Text="Наименование типа СИ" />
<TextBox Grid.Row="0"
Grid.Column="1"
Margin="0,0,0,8"
Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Grid.Row="1"
Grid.Column="0"
Margin="0,0,12,8"
VerticalAlignment="Center"
Text="Специальное наименование типа" />
<TextBox Grid.Row="1"
Grid.Column="1"
Margin="0,0,0,8"
Text="{Binding SpecialName, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Grid.Row="2"
Grid.ColumnSpan="2"
Margin="0,8,0,0"
Foreground="Firebrick"
Text="{Binding ValidationMessage}" />
<StackPanel Grid.Row="3"
Grid.ColumnSpan="2"
Margin="0,12,0,0"
Orientation="Horizontal"
HorizontalAlignment="Right">
<Button Width="100"
Margin="0,0,8,0"
IsDefault="True"
Command="{Binding ConfirmCommand}"
Content="Сохранить" />
<Button Width="90"
Command="{Binding CancelCommand}"
Content="Отмена" />
</StackPanel>
</Grid>
</Window>

View File

@@ -0,0 +1,20 @@
using System.Windows;
namespace XLAB
{
public partial class SpnmtpEditWindow : Window
{
internal SpnmtpEditWindow(SpnmtpEditWindowViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
viewModel.CloseRequested += ViewModelOnCloseRequested;
}
private void ViewModelOnCloseRequested(object sender, bool? dialogResult)
{
DialogResult = dialogResult;
Close();
}
}
}

View File

@@ -0,0 +1,127 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Input;
namespace XLAB
{
internal sealed class SpnmtpEditWindowViewModel : ObservableObject
{
private readonly IReadOnlyList<SpnmtpDirectoryItem> _existingItems;
private string _name;
private string _specialName;
private string _validationMessage;
public SpnmtpEditWindowViewModel(SpnmtpDirectoryItem seed, bool isNew, IReadOnlyList<SpnmtpDirectoryItem> existingItems)
{
var source = seed ?? new SpnmtpDirectoryItem();
_existingItems = existingItems ?? Array.Empty<SpnmtpDirectoryItem>();
Id = source.Id;
IsNew = isNew;
Name = source.Name ?? string.Empty;
SpecialName = source.SpecialName ?? string.Empty;
ConfirmCommand = new RelayCommand(Confirm);
CancelCommand = new RelayCommand(Cancel);
}
public event EventHandler<bool?> CloseRequested;
public ICommand CancelCommand { get; private set; }
public ICommand ConfirmCommand { get; private set; }
public int Id { get; private set; }
public bool IsNew { get; private set; }
public string Name
{
get { return _name; }
set { SetProperty(ref _name, value); }
}
public string SpecialName
{
get { return _specialName; }
set { SetProperty(ref _specialName, value); }
}
public string Title
{
get { return IsNew ? "Новое наименование типа СИ" : "Редактирование наименования типа СИ"; }
}
public string ValidationMessage
{
get { return _validationMessage; }
private set { SetProperty(ref _validationMessage, value); }
}
public SpnmtpDirectoryItem ToResult()
{
return new SpnmtpDirectoryItem
{
Id = Id,
Name = string.IsNullOrWhiteSpace(Name) ? string.Empty : Name.Trim(),
SpecialName = string.IsNullOrWhiteSpace(SpecialName) ? string.Empty : SpecialName.Trim()
};
}
private void Cancel(object parameter)
{
RaiseCloseRequested(false);
}
private void Confirm(object parameter)
{
var normalizedName = string.IsNullOrWhiteSpace(Name) ? string.Empty : Name.Trim();
var normalizedSpecialName = string.IsNullOrWhiteSpace(SpecialName) ? string.Empty : SpecialName.Trim();
if (normalizedName.Length == 0)
{
ValidationMessage = "Укажите наименование типа СИ.";
return;
}
if (normalizedName.Length > SpnmtpDirectoryRules.NameMaxLength)
{
ValidationMessage = string.Format("Наименование типа СИ не должно превышать {0} символов.", SpnmtpDirectoryRules.NameMaxLength);
return;
}
if (normalizedSpecialName.Length > SpnmtpDirectoryRules.SpecialNameMaxLength)
{
ValidationMessage = string.Format("Специальное наименование типа не должно превышать {0} символов.", SpnmtpDirectoryRules.SpecialNameMaxLength);
return;
}
var duplicate = _existingItems.FirstOrDefault(delegate(SpnmtpDirectoryItem item)
{
return item != null
&& item.Id != Id
&& string.Equals(
string.IsNullOrWhiteSpace(item.Name) ? string.Empty : item.Name.Trim(),
normalizedName,
StringComparison.OrdinalIgnoreCase);
});
if (duplicate != null)
{
ValidationMessage = string.Format("Наименование типа СИ \"{0}\" уже существует в справочнике.", normalizedName);
return;
}
ValidationMessage = string.Empty;
RaiseCloseRequested(true);
}
private void RaiseCloseRequested(bool? dialogResult)
{
var handler = CloseRequested;
if (handler != null)
{
handler(this, dialogResult);
}
}
}
}

View File

@@ -108,6 +108,24 @@
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
<Compile Include="SelectInstrumentTypeWindowViewModel.cs" /> <Compile Include="SelectInstrumentTypeWindowViewModel.cs" />
<Page Include="SpnmtpDirectoryWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Compile Include="SpnmtpDirectoryWindow.xaml.cs">
<DependentUpon>SpnmtpDirectoryWindow.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="SpnmtpDirectoryWindowViewModel.cs" />
<Page Include="SpnmtpEditWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Compile Include="SpnmtpEditWindow.xaml.cs">
<DependentUpon>SpnmtpEditWindow.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="SpnmtpEditWindowViewModel.cs" />
<Page Include="VerificationEditWindow.xaml"> <Page Include="VerificationEditWindow.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>