Code cleanup and try to use the RowDetailsTemplate

This commit is contained in:
Holger Börchers 2023-11-24 11:33:24 +01:00
parent 26281bbb5a
commit fd054d854b
8 changed files with 98 additions and 79 deletions

View File

@ -1,4 +1,3 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;

View File

@ -1,48 +1,56 @@
using System.Drawing; namespace SddpViewer;
using System.Linq;
namespace SddpViewer using System;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using Rssdp;
public partial class DiscoveredDeviceViewModel : ObservableObject
{ {
using System; private readonly DiscoveredSsdpDevice _device;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using Rssdp;
public partial class DiscoveredDeviceViewModel : ObservableObject
{
private readonly DiscoveredSsdpDevice device;
public DiscoveredDeviceViewModel(DiscoveredSsdpDevice device) public DiscoveredDeviceViewModel(DiscoveredSsdpDevice device)
{ {
this.device = device; _device = device;
ResponseHeader = GetResponseHeader();
} }
private string GetResponseHeader()
{
return string.Join(
"," + Environment.NewLine,
_device.ResponseHeaders.Select(x => $"{{{x.Key} : {string.Join(";", x.Value)}}}")
);
}
public string ResponseHeader { get; }
/// <summary> /// <summary>
/// Sets or returns the type of notification, being either a uuid, device type, service type or upnp:rootdevice. /// Sets or returns the type of notification, being either a uuid, device type, service type or upnp:rootdevice.
/// </summary> /// </summary>
public string NotificationType => this.device.NotificationType; public string NotificationType => _device.NotificationType;
/// <summary> /// <summary>
/// Sets or returns the universal service name (USN) of the device. /// Sets or returns the universal service name (USN) of the device.
/// </summary> /// </summary>
public string Usn => this.device.Usn; public string Usn => _device.Usn;
/// <summary> /// <summary>
/// Sets or returns a URL pointing to the device description document for this device. /// Sets or returns a URL pointing to the device description document for this device.
/// </summary> /// </summary>
public Uri DescriptionLocation => this.device.DescriptionLocation; public Uri DescriptionLocation => _device.DescriptionLocation;
/// <summary> /// <summary>
/// Sets or returns the length of time this information is valid for (from the <see cref="P:Rssdp.DiscoveredSsdpDevice.AsAt" /> time). /// Sets or returns the length of time this information is valid for (from the <see cref="P:Rssdp.DiscoveredSsdpDevice.AsAt" /> time).
/// </summary> /// </summary>
public TimeSpan CacheLifetime => this.device.CacheLifetime; public TimeSpan CacheLifetime => _device.CacheLifetime;
/// <summary> /// <summary>
/// Sets or returns the date and time this information was received. /// Sets or returns the date and time this information was received.
/// </summary> /// </summary>
public DateTimeOffset AsAt => this.device.AsAt; public DateTimeOffset AsAt => _device.AsAt;
[ObservableProperty] [ObservableProperty]
private string _friendlyName = ""; private string _friendlyName = "";
@ -54,16 +62,14 @@ namespace SddpViewer
private Uri? _presentationUrl; private Uri? _presentationUrl;
[ObservableProperty] [ObservableProperty]
private string _modelNumber; private string _modelNumber = "";
public async Task GetFurtherInformationAsync() public async Task GetFurtherInformationAsync()
{ {
var ssdpDevice = await this.device.GetDeviceInfo().ConfigureAwait(false); var ssdpDevice = await _device.GetDeviceInfo().ConfigureAwait(false);
FriendlyName = ssdpDevice.FriendlyName; FriendlyName = ssdpDevice.FriendlyName;
Icon = ssdpDevice.Icons.MinBy(x=> x.Height); Icon = ssdpDevice.Icons.MinBy(x => x.Height);
PresentationUrl = ssdpDevice.PresentationUrl; PresentationUrl = ssdpDevice.PresentationUrl;
ModelNumber = ssdpDevice.ModelNumber; ModelNumber = ssdpDevice.ModelNumber;
}
} }
} }

3
src/GlobalUsings.cs Normal file
View File

@ -0,0 +1,3 @@
// Global using directives
global using Avalonia;

View File

@ -1,25 +1,25 @@
<Window <Window
Title="SDDP viewer"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d"
x:Class="SddpViewer.MainWindow" x:Class="SddpViewer.MainWindow"
x:CompileBindings="True"
x:DataType="sddpViewer:MainWindowViewModel"
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sddpViewer="clr-namespace:SddpViewer" xmlns:sddpViewer="clr-namespace:SddpViewer"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> Title="SDDP viewer"
d:DesignHeight="450"
d:DesignWidth="800"
x:CompileBindings="True"
x:DataType="sddpViewer:MainWindowViewModel"
mc:Ignorable="d">
<Window.DataContext> <Window.DataContext>
<sddpViewer:MainWindowViewModel /> <sddpViewer:MainWindowViewModel />
</Window.DataContext> </Window.DataContext>
<Grid RowDefinitions="Auto, *"> <Grid ColumnDefinitions="*,*" RowDefinitions="Auto, *">
<StackPanel Margin="10" Spacing="5"> <StackPanel Margin="10" Spacing="5">
<TextBlock Text="Device IP Address" /> <TextBlock Text="Device IP Address" />
<ComboBox <ComboBox
DisplayMemberBinding="{Binding DisplayName}"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
DisplayMemberBinding="{Binding DisplayName}"
ItemsSource="{Binding NetworkAdapters}" ItemsSource="{Binding NetworkAdapters}"
SelectedItem="{Binding SelectedNetworkAdapter}" /> SelectedItem="{Binding SelectedNetworkAdapter}" />
<TextBlock Text="Notification filter" /> <TextBlock Text="Notification filter" />
@ -27,10 +27,22 @@
<Button Command="{Binding SearchDevicesNowCommand}" Content="Search now" /> <Button Command="{Binding SearchDevicesNowCommand}" Content="Search now" />
</StackPanel> </StackPanel>
<DataGrid <DataGrid
AutoGenerateColumns="True" Grid.Row="1" Grid.ColumnSpan="2"
Grid.Row="1" Grid.Column="0"
AutoGenerateColumns="False"
IsReadOnly="True" IsReadOnly="True"
ItemsSource="{Binding SddpDevices}"> ItemsSource="{Binding SddpDevices}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Usn}" Header="Usn" />
<DataGridTextColumn Binding="{Binding FriendlyName}" Header="Name" />
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate DataType="sddpViewer:DiscoveredDeviceViewModel">
<StackPanel>
<TextBlock Text="{Binding ResponseHeader}" />
</StackPanel>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid> </DataGrid>
</Grid> </Grid>
</Window> </Window>

View File

@ -1,11 +1,7 @@
using System; namespace SddpViewer;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Threading; using Avalonia.Threading;
namespace SddpViewer;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -14,7 +10,7 @@ using CommunityToolkit.Mvvm.Input;
using Rssdp; using Rssdp;
public partial class MainWindowViewModel : ObservableObject public sealed partial class MainWindowViewModel : ObservableObject, IDisposable
{ {
public MainWindowViewModel() public MainWindowViewModel()
{ {
@ -24,9 +20,11 @@ public partial class MainWindowViewModel : ObservableObject
private void LocatorOnDeviceUnavailable(object? sender, DeviceUnavailableEventArgs e) private void LocatorOnDeviceUnavailable(object? sender, DeviceUnavailableEventArgs e)
{ {
var existingDevice = SddpDevices.FirstOrDefault(x => var existingDevice = SddpDevices.FirstOrDefault(
string.Equals(x.Usn, e.DiscoveredDevice.Usn, StringComparison.Ordinal)); x => string.Equals(x.Usn, e.DiscoveredDevice.Usn, StringComparison.Ordinal)
if(existingDevice is null) return; );
if (existingDevice is null)
return;
Dispatcher.UIThread.Invoke(() => SddpDevices.Remove(existingDevice)); Dispatcher.UIThread.Invoke(() => SddpDevices.Remove(existingDevice));
} }
@ -79,4 +77,9 @@ public partial class MainWindowViewModel : ObservableObject
_locator.StartListeningForNotifications(); _locator.StartListeningForNotifications();
await _locator.SearchAsync(); await _locator.SearchAsync();
} }
public void Dispose()
{
_locator?.Dispose();
}
} }

View File

@ -1,5 +1,3 @@
using System.Collections.Generic;
using System.Linq;
using System.Net; using System.Net;
using System.Net.NetworkInformation; using System.Net.NetworkInformation;
using System.Net.Sockets; using System.Net.Sockets;

View File

@ -1,9 +1,6 @@
using Avalonia; namespace SddpViewer;
using System;
namespace SddpViewer; internal static class Program
class Program
{ {
// Initialization code. Don't use any Avalonia, third-party APIs or any // Initialization code. Don't use any Avalonia, third-party APIs or any
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized // SynchronizationContext-reliant code before AppMain is called: things aren't initialized

View File

@ -3,6 +3,7 @@
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport> <BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<ApplicationManifest>app.manifest</ApplicationManifest> <ApplicationManifest>app.manifest</ApplicationManifest>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault> <AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>