EonaCat.Port.Monitor/EonaCat.PortMonitor/MainWindow.xaml.cs

700 lines
30 KiB
C#

using EonaCat.Json;
using System.Diagnostics;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Xml.Serialization;
using System.Collections.ObjectModel;
using System.Text.RegularExpressions;
using System.ComponentModel;
using System.Windows.Input;
using EonaCat.Helpers.Commands;
using System.Text;
using MessageBox = System.Windows.MessageBox;
using System.Net.Http;
using System.Windows.Media;
using Application = System.Windows.Application;
using Brushes = System.Drawing.Brushes;
using System.Windows.Threading;
using Color = System.Drawing.Color;
namespace EonaCat.PortMonitor
{
public partial class MainWindow : Window
{
public ObservableCollection<ConnectionInfo> Connections { get; set; } = new ObservableCollection<ConnectionInfo>();
private List<ConnectionInfo> _previousConnections = new List<ConnectionInfo>();
private BackgroundWorker _monitorWorker;
private int _interval = 1 * 1000;
WPF.Tray.TrayIcon _trayIcon = new WPF.Tray.TrayIcon();
public bool MinimizeToTray { get; private set; }
public bool MinimizeOnStartup { get; private set; }
public bool ShowPopups { get; private set; } = true;
public bool FirstRun { get; private set; } = true;
public MainWindow()
{
InitializeComponent();
DataContext = this;
NetworkDataGrid.Items.Clear();
NetworkDataGrid.ItemsSource = Connections;
this.StateChanged += MainWindow_StateChanged;
SetupTrayIcon();
LoadSettings();
_monitorWorker = new BackgroundWorker();
_monitorWorker.DoWork += MonitorWorker_DoWork;
_monitorWorker.RunWorkerAsync();
}
protected override void OnActivated(EventArgs e)
{
base.OnActivated(e);
if (FirstRun)
{
if (MinimizeToTray || MinimizeOnStartup)
{
// Minimize to tray on first run
Hide();
FirstRun = false;
}
}
}
private void LoadSettings()
{
if (File.Exists("settings.json"))
{
var json = File.ReadAllText("settings.json");
var settings = JsonHelper.ToObject<dynamic>(json);
if (settings != null)
{
if (settings.MinimizeToTray != null)
{
MinimizeToTray = (bool)settings.MinimizeToTray;
}
if (settings.MinimizeOnStartup != null)
{
MinimizeOnStartup = (bool)settings.MinimizeOnStartup;
chkMinimize.IsChecked = MinimizeOnStartup;
}
if (settings.ShowPopups != null)
{
ShowPopups = (bool)settings.ShowPopups;
chkShowPopups.IsChecked = ShowPopups;
}
}
}
}
private void MainWindow_StateChanged(object? sender, EventArgs e)
{
if (WindowState == WindowState.Minimized)
{
// Minimize to tray and hide the window
this.Hide();
}
}
private void SetupTrayIcon()
{
// Load the icon from embedded resource
var icon = System.Drawing.Icon.ExtractAssociatedIcon(System.Reflection.Assembly.GetExecutingAssembly().Location);
_trayIcon.Icon = icon;
_trayIcon.TrayLeftMouseUp += (sender, args) =>
{
// Show the main window when the tray icon is clicked
WindowState = WindowState.Normal;
Show();
Activate();
};
_trayIcon.TrayRightMouseUp += (sender, args) =>
{
// Generate a context menu for the tray icon
var contextMenu = new System.Windows.Controls.ContextMenu();
contextMenu.Items.Add(new MenuItem { Header = "Show", Command = new GiveCommand(() => { Show(); WindowState = WindowState.Normal; }) });
contextMenu.Items.Add(new MenuItem
{
Header = "Exit",
Command = new GiveCommand(() =>
{
Close();
})
});
_trayIcon.ContextMenu = contextMenu;
};
}
protected override void OnClosing(CancelEventArgs e)
{
// Save settings before closing
var settings = new
{
MinimizeToTray = this.WindowState == WindowState.Minimized,
MinimizeOnStartup = chkMinimize.IsChecked == true,
ShowPopups = chkShowPopups.IsChecked == true
};
File.WriteAllBytes("settings.json", Encoding.UTF8.GetBytes(JsonHelper.ToJson(settings)));
base.OnClosing(e);
}
private void MonitorWorker_DoWork(object sender, DoWorkEventArgs e)
{
while (true)
{
MonitorConnections(null);
Thread.Sleep(_interval);
}
}
private async Task NotifyNewConnectionAsync(ConnectionInfo conn)
{
if (!ShowPopups)
{
return;
}
// Create a new notification manager
var notificationManager = new WPF.Popup.NotificationManager();
// Set up the popup's title and content
string popupTitle = $"New Connection: {conn.Protocol} {conn.LocalAddress}:{conn.LocalPort} -> {conn.RemoteAddress}:{conn.RemotePort}";
string popupContent = $"Process: {conn.ProcessName}\nState: {conn.State}";
// Set up the popup
var notification = new WPF.Popup.NotificationContent
{
Title = popupTitle,
Message = popupContent,
Type = WPF.Popup.NotificationType.Information,
};
// Show the popup
await notificationManager.ShowAsync(notification, expirationTime: TimeSpan.FromSeconds(3), onClick: new Action(() => { ShowConnectionDetails(conn); }));
}
private void ShowConnectionDetails(ConnectionInfo conn)
{
// Play a sound when the notification is clicked
System.Media.SystemSounds.Asterisk.Play();
var detailsWindow = new ConnectionDetailsWindow(conn);
detailsWindow.ShowDialog();
}
private void DataGrid_PreviewMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
// Get the DataGrid that triggered the event
DataGrid dataGrid = sender as DataGrid;
if (dataGrid.SelectedItem != null)
{
ShowConnectionDetails(dataGrid.SelectedItem as ConnectionInfo);
}
}
private List<ConnectionInfo> GetActiveConnections()
{
var connectionList = new List<ConnectionInfo>();
foreach (var conn in RunNetstatCommand())
{
if (string.IsNullOrEmpty(conn)) continue;
var match = ParseNetstatLine(conn);
if (match.Success)
{
var connInfo = new ConnectionInfo
{
Protocol = match.Groups["protocol"].Value,
LocalAddress = match.Groups["localAddress"].Value,
LocalPort = int.Parse(match.Groups["localPort"].Value),
RemoteAddress = match.Groups["remoteAddress"].Value,
RemotePort = int.Parse(match.Groups["remotePort"].Value),
State = string.IsNullOrEmpty(match.Groups["state"].Value) ? string.Empty : match.Groups["state"].Value, // Handle missing state for UDP
ProcessId = string.IsNullOrEmpty(match.Groups["pid"].Value) ? 0 : int.Parse(match.Groups["pid"].Value), // Default PID to 0 if missing
ProcessName = string.IsNullOrEmpty(match.Groups["pid"].Value) ? string.Empty : GetProcessName(int.Parse(match.Groups["pid"].Value)),
ConnectionTime = DateTime.Now,
ConnectionStatus = string.Empty
};
connectionList.Add(connInfo);
}
}
return connectionList;
}
private async void MonitorConnections(object state)
{
try
{
ConnectionInfo selectedConnection = null;
// Store the currently selected connection
Dispatcher.Invoke(() =>
{
selectedConnection = NetworkDataGrid.SelectedItem as ConnectionInfo;
});
var currentConnections = await Task.Run(GetActiveConnections);
var inboundConnections = currentConnections.Where(IsInboundConnection).ToList();
var outboundConnections = currentConnections.Where(IsOutboundConnection).ToList();
int totalConnections = currentConnections.Count;
// Identify new connections
var newConnections = currentConnections
.Where(c => !_previousConnections.Any(p => p.LocalAddress == c.LocalAddress && p.LocalPort == c.LocalPort))
.ToList();
// Identify closed connections
var closedConnections = _previousConnections
.Where(p => !currentConnections.Any(c => c.LocalAddress == p.LocalAddress && c.LocalPort == p.LocalPort))
.ToList();
// Identify state-changed connections
var stateChangedConnections = currentConnections
.Where(c => _previousConnections.Any(p =>
p.LocalAddress == c.LocalAddress &&
p.LocalPort == c.LocalPort &&
p.State != c.State))
.ToList();
await Dispatcher.InvokeAsync(() =>
{
foreach (var newConn in newConnections)
{
var existingConn = Connections.FirstOrDefault(c => c.LocalAddress == newConn.LocalAddress && c.LocalPort == newConn.LocalPort);
if (existingConn == null)
{
if (newConn.FirstConnectionTime == DateTime.MinValue)
newConn.FirstConnectionTime = DateTime.Now;
newConn.ConnectionStatus = "New";
newConn.ConnectionColor = new SolidColorBrush(AdjustColorBrightness(Colors.LightGreen)); // Softer green for new connections
Connections.Add(newConn);
}
else
{
existingConn.ConnectionStatus = "Updated";
existingConn.ConnectionColor = new SolidColorBrush(AdjustColorBrightness(Colors.LightGreen)); // Softer green for updated connections
}
}
foreach (var closedConn in closedConnections)
{
var conn = Connections.FirstOrDefault(c => c.LocalAddress == closedConn.LocalAddress && c.LocalPort == closedConn.LocalPort);
if (conn != null)
{
conn.ConnectionStatus = "Closed";
conn.ConnectionColor = new SolidColorBrush(AdjustColorBrightness(Colors.Gray)); // Softer gray for closed connections
Task.Delay(2000).ContinueWith(_ =>
{
Dispatcher.Invoke(() => Connections.Remove(conn));
});
}
}
foreach (var stateChangedConn in stateChangedConnections)
{
var conn = Connections.FirstOrDefault(c => c.LocalAddress == stateChangedConn.LocalAddress && c.LocalPort == stateChangedConn.LocalPort);
if (conn != null)
{
conn.ConnectionStatus = "State Changed";
conn.ConnectionColor = new SolidColorBrush(AdjustColorBrightness(Colors.Yellow)); // Softer yellow for state changes
var timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) };
timer.Tick += (sender, args) =>
{
conn.ConnectionColor = new SolidColorBrush(Colors.Transparent);
timer.Stop();
};
timer.Start();
}
}
// Apply colors based on connection state with softer variants
foreach (var conn in Connections)
{
switch (conn.State)
{
case "IDLE":
conn.ConnectionColor = new SolidColorBrush(AdjustColorBrightness(Colors.Red)); // Softer red
break;
case "LISTENING":
conn.ConnectionColor = new SolidColorBrush(AdjustColorBrightness(Colors.Blue)); // Softer blue
break;
case "SYN_SENT":
conn.ConnectionColor = new SolidColorBrush(AdjustColorBrightness(Colors.Orange)); // Softer orange
break;
case "SYN_RECEIVED":
conn.ConnectionColor = new SolidColorBrush(AdjustColorBrightness(Colors.Purple)); // Softer purple
break;
case "ESTABLISHED":
conn.ConnectionColor = new SolidColorBrush(AdjustColorBrightness(Colors.DarkGreen)); // Softer dark green
break;
case "CLOSE_WAIT":
conn.ConnectionColor = new SolidColorBrush(AdjustColorBrightness(Colors.Brown)); // Softer brown
break;
case "TIME_WAIT":
conn.ConnectionColor = new SolidColorBrush(AdjustColorBrightness(Colors.DarkGray)); // Softer dark gray
break;
case "FIN_WAIT_1":
case "FIN_WAIT_2":
conn.ConnectionColor = new SolidColorBrush(AdjustColorBrightness(Colors.LightYellow)); // Softer light yellow
break;
case "LAST_ACK":
conn.ConnectionColor = new SolidColorBrush(AdjustColorBrightness(Colors.DarkOrange)); // Softer dark orange
break;
default:
conn.ConnectionColor = new SolidColorBrush(Colors.Transparent); // Default (no color)
break;
}
}
// Update DataGrids
InboundDataGrid.ItemsSource = Connections.Where(IsInboundConnection).ToList();
OutboundDataGrid.ItemsSource = Connections.Where(IsOutboundConnection).ToList();
NetworkDataGrid.ItemsSource = Connections;
FilterConnections_KeyUp(null, null);
UpdateConnectionStats(totalConnections, inboundConnections.Count, outboundConnections.Count);
});
}
catch (Exception ex)
{
// Handle any exceptions appropriately
Console.WriteLine($"Error monitoring connections: {ex.Message}");
}
}
// Method to adjust the color brightness and make it softer
private System.Windows.Media.Color AdjustColorBrightness(System.Windows.Media.Color color)
{
byte r = (byte)(color.R + (255 - color.R) * 0.5); // Lighten the red component
byte g = (byte)(color.G + (255 - color.G) * 0.5); // Lighten the green component
byte b = (byte)(color.B + (255 - color.B) * 0.5); // Lighten the blue component
return System.Windows.Media.Color.FromArgb(180, r, g, b); // alpha = 180 for a softer color
}
private void UpdateConnectionStats(int total, int inbound, int outbound)
{
var tcpConnections = Connections.Count(c => c.Protocol == "TCP");
var udpConnections = Connections.Count(c => c.Protocol == "UDP");
var avgConnectionTime = Connections.Average(c => (DateTime.Now - c.ConnectionTime).TotalSeconds);
TcpUdpStatsText.Text = $"TCP Connections: {tcpConnections}\nUDP Connections: {udpConnections}\nAverage Connection Time: {avgConnectionTime:F2} seconds\nTotal: {total}\nInbound: {inbound}\nOutbound: {outbound}";
}
private bool IsInboundConnection(ConnectionInfo connection)
{
// Inbound: LocalAddress is valid, and the machine is accepting the connection (server-side).
return IsValidIpAddress(connection.LocalAddress) &&
connection.LocalAddress != "0.0.0.0" &&
connection.LocalAddress != "::" &&
!string.IsNullOrEmpty(connection.RemoteAddress) &&
connection.RemoteAddress != "0.0.0.0" &&
connection.RemoteAddress != "::" &&
connection.RemoteAddress != "N/A" &&
connection.RemotePort != 0;
}
private bool IsOutboundConnection(ConnectionInfo connection)
{
// Outbound: LocalAddress is valid, and the machine is initiating the connection (client-side).
return IsValidIpAddress(connection.LocalAddress) &&
connection.LocalAddress != "0.0.0.0" &&
connection.LocalAddress != "::" &&
IsValidIpAddress(connection.RemoteAddress) &&
connection.RemoteAddress != "0.0.0.0" &&
connection.RemoteAddress != "::" &&
connection.RemoteAddress != "N/A" &&
connection.LocalPort != 0;
}
private bool IsValidIpAddress(string ipAddress)
{
return !string.IsNullOrEmpty(ipAddress) && (IsValidIPv4(ipAddress) || IsValidIPv6(ipAddress));
}
private bool IsValidIPv4(string ipAddress)
{
return System.Net.IPAddress.TryParse(ipAddress, out var ip) && ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork;
}
private bool IsValidIPv6(string ipAddress)
{
return System.Net.IPAddress.TryParse(ipAddress, out var ip) && ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6;
}
private List<string> RunNetstatCommand()
{
var result = new List<string>();
try
{
ProcessStartInfo startInfo = new ProcessStartInfo("netstat", "-ano")
{
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
};
using (Process process = Process.Start(startInfo))
{
using (var reader = process.StandardOutput)
{
string line;
while ((line = reader.ReadLine()) != null)
{
result.Add(line);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine($"Error running netstat: {ex.Message}");
}
return result;
}
private Match ParseNetstatLine(string line)
{
// Regular expression for both TCP and UDP connections, IPv4 and IPv6.
var regex = new Regex(@"(?<protocol>TCP|UDP)\s+(?<localAddress>[\d\.]+|\[?[A-F0-9:]+\]?):(?<localPort>\d+)\s+(?<remoteAddress>[\d\.]+|\[?[A-F0-9:]+\]?):(?<remotePort>\d+)\s*(?<state>\S*)?\s*(?<pid>\d+)?");
var match = regex.Match(line);
return match;
}
private string GetProcessName(int pid)
{
try
{
var process = Process.GetProcessById(pid);
return process.ProcessName;
}
catch
{
return "N/A";
}
}
private void FilterConnections_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
{
// Get the filter keyword from the input box
var keywordFilter = KeywordFilter.Text.ToLower();
// Filter the connections based on the keyword
var filteredConnections = Connections?.Where(c =>
(string.IsNullOrEmpty(keywordFilter) ||
c.ProcessName.ToLower().Contains(keywordFilter) ||
c.RemoteAddress.ToLower().Contains(keywordFilter) ||
c.LocalAddress.ToLower().Contains(keywordFilter) ||
c.State.ToLower().Contains(keywordFilter) ||
c.Protocol.ToLower().Contains(keywordFilter) ||
c.LocalPort.ToString().Contains(keywordFilter) ||
c.RemotePort.ToString().Contains(keywordFilter)
)
).ToList();
// Clear the existing items and add filtered ones to the ObservableCollection
Connections.Clear();
if (filteredConnections != null)
{
foreach (var conn in filteredConnections)
{
Connections.Add(conn);
}
}
}
private void FilterConnections_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
FilterConnections_KeyUp(sender, null);
}
private void ExportToJsonButton_Click(object sender, RoutedEventArgs e)
{
var json = JsonHelper.ToJson(Connections, Json.Formatting.Indented);
File.WriteAllText("connections.json", json);
MessageBox.Show("Connections exported to connections.json", "Export Successful", MessageBoxButton.OK, MessageBoxImage.Information);
}
private void ExportToXmlButton_Click(object sender, RoutedEventArgs e)
{
var xmlSerializer = new XmlSerializer(typeof(List<ConnectionInfo>));
using (var writer = new StreamWriter("connections.xml"))
{
xmlSerializer.Serialize(writer, Connections);
}
MessageBox.Show("Connections exported to connections.xml", "Export Successful", MessageBoxButton.OK, MessageBoxImage.Information);
}
private void ExportToCsvButton_Click(object sender, RoutedEventArgs e)
{
var csvLines = new List<string> { "Protocol,LocalAddress,LocalPort,RemoteAddress,RemotePort,State,ProcessName,ConnectionTime" };
csvLines.AddRange(Connections.Select(c => $"{c.Protocol},{c.LocalAddress},{c.LocalPort},{c.RemoteAddress},{c.RemotePort},{c.State},{c.ProcessName},{c.ConnectionTime}"));
File.WriteAllLines("connections.csv", csvLines);
MessageBox.Show("Connections exported to connections.csv", "Export Successful", MessageBoxButton.OK, MessageBoxImage.Information);
}
private void SaveFilterPresetButton_Click(object sender, RoutedEventArgs e)
{
var filters = new
{
Keywords = KeywordFilter.Text,
};
var json = JsonHelper.ToJson(filters);
File.WriteAllText("filterPreset.json", json);
}
private void LoadFilterPresetButton_Click(object sender, RoutedEventArgs e)
{
if (File.Exists("filterPreset.json"))
{
var json = File.ReadAllText("filterPreset.json");
var filters = JsonHelper.ToObject<dynamic>(json);
KeywordFilter.Text = filters.Keywords;
FilterConnections_KeyUp(null, null);
}
}
private void ExportToHtmlButton_Click(object sender, RoutedEventArgs e)
{
var htmlContent = "<html><body><table border='1'><tr><th>Protocol</th><th>LocalAddress</th><th>LocalPort</th><th>RemoteAddress</th><th>RemotePort</th><th>State</th><th>ProcessName</th><th>ConnectionTime</th></tr>";
foreach (var conn in Connections)
{
htmlContent += $"<tr><td>{conn.Protocol}</td><td>{conn.LocalAddress}</td><td>{conn.LocalPort}</td><td>{conn.RemoteAddress}</td><td>{conn.RemotePort}</td><td>{conn.State}</td><td>{conn.ProcessName}</td><td>{conn.ConnectionTime}</td></tr>";
}
htmlContent += "</table></body></html>";
File.WriteAllText("connections.html", htmlContent);
MessageBox.Show("Connections exported to connections.html", "Export Successful", MessageBoxButton.OK, MessageBoxImage.Information);
}
private void chkMinimize_Checked(object sender, RoutedEventArgs e)
{
MinimizeOnStartup = chkMinimize.IsChecked == true;
}
private void chkShowPopups_Checked(object sender, RoutedEventArgs e)
{
ShowPopups = chkShowPopups.IsChecked == true;
}
private void KillProcessButton_Click(object sender, RoutedEventArgs e)
{
if (NetworkDataGrid.SelectedItem is ConnectionInfo selectedConnection)
{
try
{
var process = Process.GetProcessById(selectedConnection.ProcessId);
process.Kill();
MessageBox.Show($"Process {selectedConnection.ProcessName} (PID: {selectedConnection.ProcessId}) terminated.", "Process Killed", MessageBoxButton.OK, MessageBoxImage.Warning);
}
catch (Exception ex)
{
MessageBox.Show($"Failed to terminate process: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
private async void IpLookupButton_Click(object sender, RoutedEventArgs e)
{
if (NetworkDataGrid.SelectedItem is ConnectionInfo selectedConnection)
{
string apiUrl = $"https://reallyfreegeoip.org/json/{selectedConnection.RemoteAddress}";
using (HttpClient client = new HttpClient())
{
var response = await client.GetStringAsync(apiUrl);
MessageBox.Show(response, "IP Lookup", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
}
private void BlockIpButton_Click(object sender, RoutedEventArgs e)
{
// Check if an item is selected in the DataGrid
if (NetworkDataGrid.SelectedItem is ConnectionInfo selectedConnection)
{
// Ask the user for confirmation
var result = MessageBox.Show(
$"Are you sure you want to block the IP address: {selectedConnection.RemoteAddress}?",
"Confirm Block",
MessageBoxButton.YesNo,
MessageBoxImage.Question
);
// If the user clicks 'Yes', proceed with blocking the IP
if (result == MessageBoxResult.Yes)
{
string command = $"netsh advfirewall firewall add rule name=\"Blocked {selectedConnection.RemoteAddress}\" dir=in action=block remoteip={selectedConnection.RemoteAddress}";
Process.Start(new ProcessStartInfo("cmd.exe", "/C " + command) { CreateNoWindow = true });
MessageBox.Show($"Blocked IP: {selectedConnection.RemoteAddress}", "Firewall Rule Added", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
}
private void btnTheme_Click(object sender, RoutedEventArgs e)
{
// Ensure the resources exist
if (!Application.Current.Resources.Contains("DynamicBackgroundColor") ||
!Application.Current.Resources.Contains("DynamicTextColor"))
{
MessageBox.Show("Theme resources are missing!", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
// Retrieve current background color
System.Windows.Media.SolidColorBrush currentBg = (System.Windows.Media.SolidColorBrush)Application.Current.Resources["DynamicBackgroundColor"];
if (currentBg.Color == System.Windows.Media.Colors.Black) // Dark Mode → Light Mode
{
Application.Current.Resources["DynamicBackgroundColor"] = new System.Windows.Media.SolidColorBrush(Colors.White);
Application.Current.Resources["DynamicTextColor"] = new System.Windows.Media.SolidColorBrush(Colors.Black);
btnTheme.Content = "🌙"; // Set icon to moon for dark mode
}
else // Light Mode → Dark Mode
{
Application.Current.Resources["DynamicBackgroundColor"] = new System.Windows.Media.SolidColorBrush(Colors.Black);
Application.Current.Resources["DynamicTextColor"] = new System.Windows.Media.SolidColorBrush(Colors.White);
btnTheme.Content = "🌞"; // Set icon to sun for light mode
}
}
private void ProcessPathButton_Click(object sender, RoutedEventArgs e)
{
// Check if an item is selected in the DataGrid
if (NetworkDataGrid.SelectedItem is ConnectionInfo selectedConnection)
{
try
{
var process = Process.GetProcessById(selectedConnection.ProcessId);
if (process != null && process.MainModule != null)
{
Process.Start("explorer.exe", $"/select, \"{process.MainModule.FileName}\"");
}
}
catch (Exception ex)
{
MessageBox.Show($"Failed to get process path: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
}
}