Many cool stuff
This commit is contained in:
parent
fd054d854b
commit
0e75065ff7
@ -14,9 +14,9 @@ indent_size = 2
|
||||
#### Core EditorConfig Options ####
|
||||
|
||||
# Indentation and spacing
|
||||
indent_size = 4
|
||||
tab_width = 4
|
||||
|
||||
indent_size = 2
|
||||
tab_width = 2
|
||||
max_line_length = 140
|
||||
# New line preferences
|
||||
end_of_line = crlf
|
||||
insert_final_newline = false
|
||||
|
@ -5,18 +5,18 @@ namespace SddpViewer;
|
||||
|
||||
public partial class App : Application
|
||||
{
|
||||
public override void Initialize()
|
||||
public override void Initialize()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public override void OnFrameworkInitializationCompleted()
|
||||
{
|
||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
desktop.MainWindow = new MainWindow();
|
||||
}
|
||||
|
||||
public override void OnFrameworkInitializationCompleted()
|
||||
{
|
||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
desktop.MainWindow = new MainWindow();
|
||||
}
|
||||
|
||||
base.OnFrameworkInitializationCompleted();
|
||||
}
|
||||
base.OnFrameworkInitializationCompleted();
|
||||
}
|
||||
}
|
||||
|
@ -1,75 +1,106 @@
|
||||
namespace SddpViewer;
|
||||
using System.Net;
|
||||
|
||||
namespace SddpViewer;
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
using Rssdp;
|
||||
|
||||
public partial class DiscoveredDeviceViewModel : ObservableObject
|
||||
{
|
||||
private readonly DiscoveredSsdpDevice _device;
|
||||
private readonly DiscoveredSsdpDevice _device;
|
||||
private SsdpDevice? _ssdpDevice;
|
||||
|
||||
public DiscoveredDeviceViewModel(DiscoveredSsdpDevice device)
|
||||
{
|
||||
_device = device;
|
||||
ResponseHeader = GetResponseHeader();
|
||||
}
|
||||
public DiscoveredDeviceViewModel(DiscoveredSsdpDevice device)
|
||||
{
|
||||
_device = device;
|
||||
ResponseHeader = GetResponseHeader();
|
||||
DiscoveredAt = DateTime.Now;
|
||||
IpAddress = EvaluateIpAddress(device);
|
||||
MacAddress = EvaluateMacAddress();
|
||||
HostName = EvaluateHostName();
|
||||
}
|
||||
|
||||
private string GetResponseHeader()
|
||||
{
|
||||
return string.Join(
|
||||
"," + Environment.NewLine,
|
||||
_device.ResponseHeaders.Select(x => $"{{{x.Key} : {string.Join(";", x.Value)}}}")
|
||||
);
|
||||
}
|
||||
public string HostName { get; set; }
|
||||
|
||||
public string ResponseHeader { get; }
|
||||
public string MacAddress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets or returns the type of notification, being either a uuid, device type, service type or upnp:rootdevice.
|
||||
/// </summary>
|
||||
public string NotificationType => _device.NotificationType;
|
||||
public string IpAddress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets or returns the universal service name (USN) of the device.
|
||||
/// </summary>
|
||||
public string Usn => _device.Usn;
|
||||
public DateTime DiscoveredAt { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets or returns a URL pointing to the device description document for this device.
|
||||
/// </summary>
|
||||
public Uri DescriptionLocation => _device.DescriptionLocation;
|
||||
[ObservableProperty]
|
||||
public partial string ResponseHeader { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets or returns the length of time this information is valid for (from the <see cref="P:Rssdp.DiscoveredSsdpDevice.AsAt" /> time).
|
||||
/// </summary>
|
||||
public TimeSpan CacheLifetime => _device.CacheLifetime;
|
||||
/// <summary>
|
||||
/// Sets or returns the type of notification, being either a uuid, device type, service type or upnp:rootdevice.
|
||||
/// </summary>
|
||||
public string NotificationType => _device.NotificationType;
|
||||
|
||||
/// <summary>
|
||||
/// Sets or returns the date and time this information was received.
|
||||
/// </summary>
|
||||
public DateTimeOffset AsAt => _device.AsAt;
|
||||
/// <summary>
|
||||
/// Sets or returns the universal service name (USN) of the device.
|
||||
/// </summary>
|
||||
public string Usn => _device.Usn;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _friendlyName = "";
|
||||
/// <summary>
|
||||
/// Sets or returns a URL pointing to the device description document for this device.
|
||||
/// </summary>
|
||||
public Uri DescriptionLocation => _device.DescriptionLocation;
|
||||
|
||||
[ObservableProperty]
|
||||
private SsdpDeviceIcon? _icon;
|
||||
/// <summary>
|
||||
/// Sets or returns the length of time this information is valid for (from the <see cref="P:Rssdp.DiscoveredSsdpDevice.AsAt" /> time).
|
||||
/// </summary>
|
||||
public TimeSpan CacheLifetime => _device.CacheLifetime;
|
||||
|
||||
[ObservableProperty]
|
||||
private Uri? _presentationUrl;
|
||||
/// <summary>
|
||||
/// Sets or returns the date and time this information was received.
|
||||
/// </summary>
|
||||
public DateTimeOffset AsAt => _device.AsAt;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _modelNumber = "";
|
||||
[ObservableProperty]
|
||||
public partial string FriendlyName { get; set; } = "";
|
||||
|
||||
public async Task GetFurtherInformationAsync()
|
||||
{
|
||||
var ssdpDevice = await _device.GetDeviceInfo().ConfigureAwait(false);
|
||||
FriendlyName = ssdpDevice.FriendlyName;
|
||||
Icon = ssdpDevice.Icons.MinBy(x => x.Height);
|
||||
PresentationUrl = ssdpDevice.PresentationUrl;
|
||||
ModelNumber = ssdpDevice.ModelNumber;
|
||||
}
|
||||
[ObservableProperty]
|
||||
public partial SsdpDeviceIcon? Icon { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial Uri? PresentationUrl { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial string ModelNumber { get; set; } = "";
|
||||
|
||||
[ObservableProperty]
|
||||
public partial string Version { get; set; } = "";
|
||||
|
||||
public async Task GetFurtherInformationAsync()
|
||||
{
|
||||
_ssdpDevice = await _device.GetDeviceInfo().ConfigureAwait(false);
|
||||
FriendlyName = _ssdpDevice.ModelDescription;
|
||||
Icon = _ssdpDevice.Icons.MinBy(x => x.Height);
|
||||
PresentationUrl = _ssdpDevice.PresentationUrl;
|
||||
ModelNumber = _ssdpDevice.ModelNumber;
|
||||
Version = _ssdpDevice.SerialNumber?.Split(',').Last() ?? new Version().ToString();
|
||||
HttpClient client = new HttpClient();
|
||||
var response = await client.GetAsync(_ssdpDevice.ModelUrl).ConfigureAwait(false);
|
||||
ResponseHeader = await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
|
||||
private string EvaluateHostName() => Dns.GetHostEntry(IpAddress).HostName;
|
||||
|
||||
private string GetResponseHeader() =>
|
||||
string.Join("," + Environment.NewLine, _device.ResponseHeaders.Select(x => $"{{{x.Key} : {string.Join(";", x.Value)}}}"));
|
||||
|
||||
private string EvaluateIpAddress(DiscoveredSsdpDevice device) => device.DescriptionLocation.Host;
|
||||
|
||||
private string EvaluateMacAddress()
|
||||
{
|
||||
var lookupResult = ArpLookup.Arp.Lookup(IPAddress.Parse(IpAddress));
|
||||
return lookupResult is null ? "Unknown" : string.Join(":", lookupResult.GetAddressBytes().Select(b => $"{b:x2}"));
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is DiscoveredDeviceViewModel viewModel && Equals(viewModel.Usn, Usn);
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
<Window.DataContext>
|
||||
<sddpViewer:MainWindowViewModel />
|
||||
</Window.DataContext>
|
||||
<Grid ColumnDefinitions="*,*" RowDefinitions="Auto, *">
|
||||
<Grid ColumnDefinitions="*,*" RowDefinitions="180, *">
|
||||
<StackPanel Margin="10" Spacing="5">
|
||||
<TextBlock Text="Device IP Address" />
|
||||
<ComboBox
|
||||
@ -24,25 +24,36 @@
|
||||
SelectedItem="{Binding SelectedNetworkAdapter}" />
|
||||
<TextBlock Text="Notification filter" />
|
||||
<TextBox Text="{Binding NotificationFilter}" />
|
||||
<Button Command="{Binding SearchDevicesNowCommand}" Content="Search now" />
|
||||
<StackPanel Orientation="Horizontal" Spacing="5">
|
||||
<Button Command="{Binding StartListeningCommand}" Content="Listen for devices" />
|
||||
<Button Command="{Binding SearchDevicesNowCommand}" Content="Search now" />
|
||||
<Button Command="{Binding ResearchCommand}" Content="Search more" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<Grid Grid.Row="0" Grid.Column="1">
|
||||
<TextBox
|
||||
IsReadOnly="True"
|
||||
Text="{ReflectionBinding SelectedItem.ResponseHeader,
|
||||
ElementName=DevicesDataGrid}"
|
||||
TextWrapping="Wrap" />
|
||||
</Grid>
|
||||
<DataGrid
|
||||
Grid.Row="1" Grid.ColumnSpan="2"
|
||||
x:Name="DevicesDataGrid"
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2"
|
||||
AutoGenerateColumns="False"
|
||||
IsReadOnly="True"
|
||||
ItemsSource="{Binding SddpDevices}">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Binding="{Binding Usn}" Header="Usn" />
|
||||
<DataGridTextColumn Binding="{Binding FriendlyName}" Header="Name" />
|
||||
<DataGridTextColumn Binding="{Binding Usn}" Header="Usn" />
|
||||
<DataGridTextColumn Binding="{Binding IpAddress}" Header="Ip address" />
|
||||
<DataGridTextColumn Binding="{Binding HostName}" Header="Hostname" />
|
||||
<DataGridTextColumn Binding="{Binding MacAddress}" Header="Mac address" />
|
||||
<DataGridTextColumn Binding="{Binding Version}" Header="Version" />
|
||||
<DataGridTextColumn Binding="{Binding DiscoveredAt}" Header="Discovered at" />
|
||||
</DataGrid.Columns>
|
||||
<DataGrid.RowDetailsTemplate>
|
||||
<DataTemplate DataType="sddpViewer:DiscoveredDeviceViewModel">
|
||||
<StackPanel>
|
||||
<TextBlock Text="{Binding ResponseHeader}" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</DataGrid.RowDetailsTemplate>
|
||||
</DataGrid>
|
||||
</Grid>
|
||||
</Window>
|
||||
|
@ -4,8 +4,8 @@ namespace SddpViewer;
|
||||
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
|
@ -1,85 +1,129 @@
|
||||
namespace SddpViewer;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
using Avalonia.Threading;
|
||||
namespace SddpViewer;
|
||||
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Avalonia.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
|
||||
using Rssdp;
|
||||
|
||||
public sealed partial class MainWindowViewModel : ObservableObject, IDisposable
|
||||
{
|
||||
public MainWindowViewModel()
|
||||
{
|
||||
NetworkAdapters = NetworkAdapter.GetAvailableNetworkAdapter().ToArray();
|
||||
SelectedNetworkAdapter = NetworkAdapters.FirstOrDefault();
|
||||
}
|
||||
public MainWindowViewModel()
|
||||
{
|
||||
NetworkAdapters = NetworkAdapter.GetAvailableNetworkAdapter().ToArray();
|
||||
SelectedNetworkAdapter = NetworkAdapters.FirstOrDefault();
|
||||
SddpDevices = new ObservableCollection<DiscoveredDeviceViewModel>();
|
||||
SddpDevices.CollectionChanged += SddpDevices_OnCollectionChanged;
|
||||
}
|
||||
|
||||
private void LocatorOnDeviceUnavailable(object? sender, DeviceUnavailableEventArgs e)
|
||||
private static async void SddpDevices_OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var existingDevice = SddpDevices.FirstOrDefault(
|
||||
x => string.Equals(x.Usn, e.DiscoveredDevice.Usn, StringComparison.Ordinal)
|
||||
);
|
||||
if (existingDevice is null)
|
||||
return;
|
||||
Dispatcher.UIThread.Invoke(() => SddpDevices.Remove(existingDevice));
|
||||
}
|
||||
|
||||
private async void LocatorOnDeviceAvailable(object? sender, DeviceAvailableEventArgs e)
|
||||
{
|
||||
if (!e.IsNewlyDiscovered)
|
||||
foreach (object eNewItem in e.NewItems ?? Array.Empty<object>())
|
||||
{
|
||||
if (eNewItem is DiscoveredDeviceViewModel discoveredDeviceViewModel)
|
||||
{
|
||||
return;
|
||||
await discoveredDeviceViewModel.GetFurtherInformationAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw; // TODO handle exception
|
||||
}
|
||||
}
|
||||
|
||||
var discoveredDeviceViewModel = new DiscoveredDeviceViewModel(e.DiscoveredDevice);
|
||||
private void LocatorOnDeviceUnavailable(object? sender, DeviceUnavailableEventArgs e)
|
||||
{
|
||||
var existingDevice = SddpDevices.FirstOrDefault(x => string.Equals(x.Usn, e.DiscoveredDevice.Usn, StringComparison.Ordinal));
|
||||
if (existingDevice is null)
|
||||
return;
|
||||
Dispatcher.UIThread.Invoke(() => SddpDevices.Remove(existingDevice));
|
||||
}
|
||||
|
||||
private void LocatorOnDeviceAvailable(object? sender, DeviceAvailableEventArgs e)
|
||||
{
|
||||
if (!e.IsNewlyDiscovered)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var discoveredDeviceViewModel = new DiscoveredDeviceViewModel(e.DiscoveredDevice);
|
||||
if (!SddpDevices.Contains(discoveredDeviceViewModel))
|
||||
Dispatcher.UIThread.Invoke(() => SddpDevices.Add(discoveredDeviceViewModel));
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
private IReadOnlyList<NetworkAdapter> _networkAdapters;
|
||||
|
||||
[ObservableProperty]
|
||||
private NetworkAdapter? _selectedNetworkAdapter;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _deviceIpAddress = "192.168.42.193";
|
||||
|
||||
[ObservableProperty]
|
||||
private string _notificationFilter = "upnp:rootdevice";
|
||||
|
||||
private SsdpDeviceLocator? _locator;
|
||||
|
||||
public ObservableCollection<DiscoveredDeviceViewModel> SddpDevices { get; }
|
||||
|
||||
[RelayCommand]
|
||||
private async Task SearchDevicesNowAsync()
|
||||
{
|
||||
SddpDevices.Clear();
|
||||
if (_locator is not null)
|
||||
{
|
||||
_locator.StopListeningForNotifications();
|
||||
_locator.DeviceAvailable -= LocatorOnDeviceAvailable;
|
||||
_locator.DeviceUnavailable -= LocatorOnDeviceUnavailable;
|
||||
}
|
||||
|
||||
await StartListening(async locator =>
|
||||
{
|
||||
foreach (var discoveredSsdpDevice in await locator.SearchAsync())
|
||||
{
|
||||
var discoveredDeviceViewModel = new DiscoveredDeviceViewModel(discoveredSsdpDevice);
|
||||
Dispatcher.UIThread.Invoke(() => SddpDevices.Add(discoveredDeviceViewModel));
|
||||
await discoveredDeviceViewModel.GetFurtherInformationAsync();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
private IReadOnlyList<NetworkAdapter> _networkAdapters;
|
||||
|
||||
[ObservableProperty]
|
||||
private NetworkAdapter? _selectedNetworkAdapter;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _deviceIpAddress = "192.168.42.193";
|
||||
|
||||
[ObservableProperty]
|
||||
private string _notificationFilter = "upnp:rootdevice";
|
||||
|
||||
private SsdpDeviceLocator? _locator;
|
||||
|
||||
public ObservableCollection<DiscoveredDeviceViewModel> SddpDevices { get; } = new();
|
||||
|
||||
[RelayCommand]
|
||||
private async Task SearchDevicesNowAsync()
|
||||
[RelayCommand]
|
||||
private async Task ResearchAsync()
|
||||
{
|
||||
if (_locator is not null)
|
||||
{
|
||||
SddpDevices.Clear();
|
||||
if (_locator is not null)
|
||||
{
|
||||
_locator.StopListeningForNotifications();
|
||||
_locator.DeviceAvailable -= LocatorOnDeviceAvailable;
|
||||
_locator.DeviceUnavailable -= LocatorOnDeviceUnavailable;
|
||||
}
|
||||
_locator = new SsdpDeviceLocator(SelectedNetworkAdapter?.IpAddress.ToString());
|
||||
if (!string.IsNullOrWhiteSpace(NotificationFilter))
|
||||
{
|
||||
_locator.NotificationFilter = NotificationFilter;
|
||||
}
|
||||
|
||||
_locator.DeviceAvailable += LocatorOnDeviceAvailable;
|
||||
_locator.DeviceUnavailable += LocatorOnDeviceUnavailable;
|
||||
_locator.StartListeningForNotifications();
|
||||
await _locator.SearchAsync();
|
||||
await _locator.SearchAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
[RelayCommand]
|
||||
private async Task StartListening(Func<SsdpDeviceLocator, Task>? action = null)
|
||||
{
|
||||
_locator = new SsdpDeviceLocator(SelectedNetworkAdapter?.IpAddress.ToString());
|
||||
if (!string.IsNullOrWhiteSpace(NotificationFilter))
|
||||
{
|
||||
_locator?.Dispose();
|
||||
_locator.NotificationFilter = NotificationFilter;
|
||||
}
|
||||
|
||||
if (action is not null)
|
||||
{
|
||||
await action.Invoke(_locator);
|
||||
}
|
||||
|
||||
_locator.DeviceAvailable += LocatorOnDeviceAvailable;
|
||||
_locator.DeviceUnavailable += LocatorOnDeviceUnavailable;
|
||||
_locator.StartListeningForNotifications();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_locator?.Dispose();
|
||||
}
|
||||
}
|
||||
|
@ -6,24 +6,22 @@ namespace SddpViewer;
|
||||
|
||||
public record NetworkAdapter(string Name, IPAddress IpAddress)
|
||||
{
|
||||
public string DisplayName => $"{Name} - {IpAddress}";
|
||||
public string DisplayName => $"{Name} - {IpAddress}";
|
||||
|
||||
public static IEnumerable<NetworkAdapter> GetAvailableNetworkAdapter()
|
||||
public static IEnumerable<NetworkAdapter> GetAvailableNetworkAdapter()
|
||||
{
|
||||
foreach (var nic in NetworkInterface.GetAllNetworkInterfaces())
|
||||
{
|
||||
foreach (var nic in NetworkInterface.GetAllNetworkInterfaces())
|
||||
{
|
||||
if (nic.OperationalStatus != OperationalStatus.Up)
|
||||
continue;
|
||||
if (nic.NetworkInterfaceType == NetworkInterfaceType.Loopback)
|
||||
continue;
|
||||
var physicalAddress = nic.GetIPProperties()
|
||||
.UnicastAddresses.FirstOrDefault(
|
||||
x => x.Address.AddressFamily == AddressFamily.InterNetwork
|
||||
);
|
||||
if (physicalAddress is not null)
|
||||
{
|
||||
yield return new NetworkAdapter(nic.Name, physicalAddress.Address);
|
||||
}
|
||||
}
|
||||
if (nic.OperationalStatus != OperationalStatus.Up)
|
||||
continue;
|
||||
if (nic.NetworkInterfaceType == NetworkInterfaceType.Loopback)
|
||||
continue;
|
||||
var physicalAddress = nic.GetIPProperties()
|
||||
.UnicastAddresses.FirstOrDefault(x => x.Address.AddressFamily == AddressFamily.InterNetwork);
|
||||
if (physicalAddress is not null)
|
||||
{
|
||||
yield return new NetworkAdapter(nic.Name, physicalAddress.Address);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,14 +2,12 @@
|
||||
|
||||
internal static class Program
|
||||
{
|
||||
// Initialization code. Don't use any Avalonia, third-party APIs or any
|
||||
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
|
||||
// yet and stuff might break.
|
||||
[STAThread]
|
||||
public static void Main(string[] args) =>
|
||||
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
|
||||
// Initialization code. Don't use any Avalonia, third-party APIs or any
|
||||
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
|
||||
// yet and stuff might break.
|
||||
[STAThread]
|
||||
public static void Main(string[] args) => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
|
||||
|
||||
// Avalonia configuration, don't remove; also used by visual designer.
|
||||
public static AppBuilder BuildAvaloniaApp() =>
|
||||
AppBuilder.Configure<App>().UsePlatformDetect().WithInterFont().LogToTrace();
|
||||
// Avalonia configuration, don't remove; also used by visual designer.
|
||||
public static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure<App>().UsePlatformDetect().WithInterFont().LogToTrace();
|
||||
}
|
||||
|
@ -7,17 +7,23 @@
|
||||
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
|
||||
<LangVersion>preview</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="11.0.5" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="11.0.5" />
|
||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.5" />
|
||||
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.0.5" />
|
||||
<PackageReference Include="ArpLookup" Version="2.0.3" />
|
||||
<PackageReference Include="Avalonia" Version="11.2.5" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="11.2.5" />
|
||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.2.5" />
|
||||
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.2.5" />
|
||||
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.5" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.2.5" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||
<PackageReference Include="CSharpier.MsBuild" Version="0.30.6">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="rssdp" Version="4.0.4" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
Loading…
x
Reference in New Issue
Block a user