Skip to content

Commit

Permalink
Much More MVVM.
Browse files Browse the repository at this point in the history
  • Loading branch information
nitz committed Jan 19, 2023
1 parent 670745c commit 151c310
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 170 deletions.
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ dotnet_naming_style.first_upper.word_separator =
dotnet_naming_style.first_upper.capitalization = first_word_upper

# XML Files
[*.{csproj,props,xml}]
[*.{csproj,props,xml,xaml}]

# Indentation and spacing
indent_size = 2
Expand Down
2 changes: 2 additions & 0 deletions HyperVPeek.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@
<ItemGroup>
<PackageReference Include="cmdwtf.Toolkit" Version="1.0.0" />
<PackageReference Include="CommunityToolkit.Common" Version="8.1.0" />
<PackageReference Include="CommunityToolkit.Diagnostics" Version="8.1.0" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.1.0" />
<PackageReference Include="DotNetProjects.Extended.Wpf.Toolkit" Version="5.0.103" />
<PackageReference Include="Microsoft.Management.Infrastructure" Version="2.0.0" />
<PackageReference Include="Microsoft.Management.Infrastructure.Runtime.Win" Version="2.0.0" />
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.39" />
</ItemGroup>

</Project>
167 changes: 93 additions & 74 deletions MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,81 +5,100 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:HyperVPeek"
xmlns:tk="clr-namespace:Xceed.Wpf.Toolkit;assembly=DotNetProjects.Wpf.Extended.Toolkit"
xmlns:System="clr-namespace:System;assembly=System.Runtime" x:Class="HyperVPeek.MainWindow"
xmlns:System="clr-namespace:System;assembly=System.Runtime"
xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
x:Class="HyperVPeek.MainWindow"
mc:Ignorable="d"
Title="Hyper-V Peek" Height="450" Width="800" Loaded="Window_Loaded" Closed="Window_Closed">
<Window.Resources>
<local:InverseBooleanConverter x:Key="InverseBooleanConverter"/>
<Style TargetType="{x:Type Button}" x:Key="VisibleWhenConnected">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Value="True" Binding="{Binding IsConnected}">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type Button}" x:Key="VisibleWhenDisconnected">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Value="True" Binding="{Binding IsDisconnected}">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type Button}" x:Key="VisibleWhenNoAutoRefresh">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Value="False" Binding="{Binding AutoRefresh}">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type Button}" x:Key="VisibleWhenExceededMaxEnvelopeSize">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Value="True" Binding="{Binding ExceededMaxEnvelopeSize}">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>

</Window.Resources>
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<DockPanel>
<StackPanel Orientation="Vertical" DockPanel.Dock="Top">
<StackPanel Orientation="Horizontal" Margin="0, 3, 0, 3">
<StackPanel Orientation="Horizontal" IsEnabled="{Binding IsConnected, Converter={StaticResource InverseBooleanConverter}}">
<tk:WatermarkTextBox Margin="3,3,3,3" TextWrapping="NoWrap" Width="120" Watermark="Domain Name" Text="{Binding TargetDomain}"/>
<tk:WatermarkTextBox Margin="3,3,3,3" TextWrapping="NoWrap" Width="120" Watermark="Hyper-V Hostname" Text="{Binding TargetHostname}"/>
<tk:WatermarkTextBox Margin="3,3,3,3" TextWrapping="NoWrap" Width="120" Watermark="Username" Text="{Binding Username}"/>
<tk:WatermarkPasswordBox x:Name="PasswordTextBox" Margin="3,3,3,3" TextWrapping="NoWrap" Width="120" Watermark="Password"/>
</StackPanel>
<Button Content="Connect" Margin="3,3,3,3" Width="80" Click="Connect_Click" Style="{Binding Mode=OneWay, Source={StaticResource VisibleWhenDisconnected}}" />
<Button Content="Disconnect" Margin="3,3,3,3" Width="80" Click="Disconnect_Click" Style="{Binding Mode=OneWay, Source={StaticResource VisibleWhenConnected}}" />
<Button Content="Refresh" Margin="3,3,3,3" Width="80" Click="Refresh_Click" Style="{Binding Mode=OneWay, Source={StaticResource VisibleWhenNoAutoRefresh}}"/>
</StackPanel>
</StackPanel>
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal">
<Label Content="{Binding Status}"></Label>
<Button Content="Try Making it 2MB" Margin="3,3,3,3" Width="80" Style="{Binding Mode=OneWay, Source={StaticResource VisibleWhenExceededMaxEnvelopeSize}}" Click="EnlargeMaxEnvelopeSize_Click" />
Title="Hyper-V Peek" Height="450" Width="800">
<Window.Resources>
<local:InverseBooleanConverter x:Key="InverseBooleanConverter"/>
<Style TargetType="{x:Type Button}" x:Key="VisibleWhenConnected">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Value="True" Binding="{Binding IsConnected}">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type Button}" x:Key="VisibleWhenDisconnected">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Value="True" Binding="{Binding IsDisconnected}">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type Button}" x:Key="VisibleWhenNoAutoRefresh">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Value="False" Binding="{Binding AutoRefresh}">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type Button}" x:Key="VisibleWhenExceededMaxEnvelopeSize">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Value="True" Binding="{Binding ExceededMaxEnvelopeSize}">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<b:Interaction.Triggers>
<b:EventTrigger EventName="Loaded">
<b:InvokeCommandAction Command="{Binding LoadSettingsCommand}"/>
</b:EventTrigger>
<b:EventTrigger EventName="Closed">
<b:InvokeCommandAction Command="{Binding SaveSettingsCommand}"/>
</b:EventTrigger>
</b:Interaction.Triggers>
<DockPanel>
<StackPanel Orientation="Vertical" DockPanel.Dock="Top">
<StackPanel Orientation="Horizontal" Margin="0, 3, 0, 3">
<StackPanel Orientation="Horizontal" IsEnabled="{Binding IsConnected, Converter={StaticResource InverseBooleanConverter}}">
<tk:WatermarkTextBox Margin="3,3,3,3" TextWrapping="NoWrap" Width="120" Watermark="Domain Name" Text="{Binding TargetDomain}"/>
<tk:WatermarkTextBox Margin="3,3,3,3" TextWrapping="NoWrap" Width="120" Watermark="Hyper-V Hostname" Text="{Binding TargetHostname}"/>
<tk:WatermarkTextBox Margin="3,3,3,3" TextWrapping="NoWrap" Width="120" Watermark="Username" Text="{Binding Username}"/>
<tk:WatermarkPasswordBox x:Name="PasswordTextBox" Margin="3,3,3,3" TextWrapping="NoWrap" Width="120" Watermark="Password"/>
</StackPanel>
<DockPanel>
<Grid Width="100
" IsEnabled="{Binding IsConnected}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label Content="VMs"></Label>
<ListBox Margin="3,3,3,3" Grid.Row="1" SelectionChanged="ListBox_SelectionChanged" ItemsSource="{Binding VirtualMachines}"/>
<CheckBox Content="Auto-Refresh" Grid.Row="2" IsChecked="{Binding AutoRefresh}"></CheckBox>
</Grid>
<StackPanel DockPanel.Dock="Right" x:Name="VmImagePanel" Margin="3,3,3,3">
<Image x:Name="VmImage" Stretch="Fill" />
</StackPanel>
</DockPanel>
<Button Content="Connect" Margin="3,3,3,3" Width="80" Style="{Binding Mode=OneWay, Source={StaticResource VisibleWhenDisconnected}}">
<b:Interaction.Triggers>
<b:EventTrigger EventName="Click">
<b:InvokeCommandAction Command="{Binding ConnectCommand}" CommandParameter="{Binding ElementName=PasswordTextBox}"/>
<b:InvokeCommandAction Command="{Binding UpdateVirtualMachineListCommand}" />
</b:EventTrigger>
</b:Interaction.Triggers>
</Button>
<Button Content="Disconnect" Margin="3,3,3,3" Width="80" Style="{Binding Mode=OneWay, Source={StaticResource VisibleWhenConnected}}" Command="{Binding DisconnectCommand, Mode=OneWay}" />
<Button Content="Refresh" Margin="3,3,3,3" Width="80" Style="{Binding Mode=OneWay, Source={StaticResource VisibleWhenNoAutoRefresh}}" Command="{Binding RefreshVirtualMachineImageCommand, Mode=OneWay}"/>
</StackPanel>
</StackPanel>
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal">
<Label Content="{Binding Status}"></Label>
<Button Content="Try Making it 2MB" Margin="3,3,3,3" Width="80" Style="{Binding Mode=OneWay, Source={StaticResource VisibleWhenExceededMaxEnvelopeSize}}" Command="{Binding SetMaxEnvelopeSizeCommand, Mode=OneWay}">
<Button.CommandParameter>
<System:Int32>2048</System:Int32>
</Button.CommandParameter>
</Button>
</StackPanel>
<DockPanel>
<Grid Width="100" IsEnabled="{Binding IsConnected}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label Content="VMs"></Label>
<ListBox Margin="3,3,3,3" Grid.Row="1" ItemsSource="{Binding VirtualMachines}" SelectedValue="{Binding SelectedVirtualMachine}"/>
<CheckBox Content="Auto-Refresh" Grid.Row="2" IsChecked="{Binding AutoRefresh}"></CheckBox>
</Grid>
<StackPanel DockPanel.Dock="Right" x:Name="VmImagePanel" Margin="3,3,3,3" SizeChanged="VmImagePanel_SizeChanged">
<Image x:Name="VmImage" Stretch="Fill" Source="{Binding LastVirtualMachineImage}" />
</StackPanel>
</DockPanel>
</DockPanel>
</Window>
76 changes: 11 additions & 65 deletions MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,9 @@
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Xml.Linq;


using static cmdwtf.Toolkit.IEnumerable;
Expand All @@ -32,93 +27,44 @@ namespace HyperVPeek
public partial class MainWindow : Window
{
private readonly MainWindowViewModel _viewModel;
private string _lastVmTargeted = string.Empty;
private readonly DispatcherTimer _timer;

public MainWindow()
{
InitializeComponent();
_viewModel = DataContext as MainWindowViewModel ?? throw new Exception("Bad data context.");
SetPreviewSizes(VmImagePanel.ActualWidth, VmImagePanel.ActualHeight);

_timer = new DispatcherTimer
{
Interval = TimeSpan.FromSeconds(1)
};
_timer.Tick += Timer_Tick;
_timer.Start();
}

private void Window_Loaded(object sender, RoutedEventArgs e) => _viewModel.LoadSettings();

private void Window_Closed(object sender, EventArgs e) => _viewModel.SaveSettings();

private void Timer_Tick(object? sender, EventArgs e) => UpdateVirtualSystemImage();

private void Refresh_Click(object sender, RoutedEventArgs e) => UpdateVirtualSystemImage();

private void Connect_Click(object sender, RoutedEventArgs e)
{
_viewModel.Connect(PasswordTextBox.SecurePassword);
_viewModel.UpdateVirtualMachineList();
}

private void Disconnect_Click(object sender, RoutedEventArgs e)
{
_timer.Stop();
_viewModel.Disconnect();
UpdateVirtualSystemImage(null);
}

private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
_lastVmTargeted = (e.Source as ListBox)?.SelectedItem as string ?? string.Empty;
UpdateVirtualSystemImage();
}

private void UpdateVirtualSystemImage()
{
if (!_viewModel.AutoRefresh && _timer.IsEnabled)
if ((!_viewModel.AutoRefresh || !_viewModel.IsConnected))
{
_timer.Stop();
return;
}

UpdateVirtualSystemImage(_lastVmTargeted);

if (_viewModel.AutoRefresh && _timer.IsEnabled == false)
{
_timer.Start();
}
_viewModel.RefreshVirtualMachineImageCommand.Execute(null);
}

private void UpdateVirtualSystemImage(string? vmName)
private void VmImagePanel_SizeChanged(object sender, SizeChangedEventArgs e)
{
_lastVmTargeted = vmName ?? string.Empty;

if (string.IsNullOrEmpty(vmName))
{
VmImage.Source = null;
return;
}

BitmapSource? img = _viewModel.GetVirtualMachinePreview(vmName, VmImagePanel.ActualWidth, VmImagePanel.ActualHeight);
VmImage.Source = img;
FrameworkElement? element = sender as FrameworkElement;
SetPreviewSizes(element?.ActualWidth ?? 0, element?.ActualHeight ?? 0);
}

public static BitmapImage ToImage(byte[] array)
private void SetPreviewSizes(double width, double height)
{
using var ms = new System.IO.MemoryStream(array);
var image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = ms;
image.EndInit();
return image;
_viewModel.VirtualMachinePreviewWidth = width;
_viewModel.VirtualMachinePreviewHeight = height;
}

private void Debug_Click(object sender, RoutedEventArgs e) => _viewModel.SetMaxEnvelopeSize(51200);

private void EnlargeMaxEnvelopeSize_Click(object sender, RoutedEventArgs e) =>
// hopefully 2mb is big enough?
_viewModel.SetMaxEnvelopeSize(2048);
}
}
Loading

0 comments on commit 151c310

Please sign in to comment.