implemented first version of console view,

newline separator on received data not implemented yet,
some changes on the send receive model
master
Jonas Arnold 3 years ago
parent 973043a270
commit 4247614035
  1. 2
      MultiTerm.Core/Helpers/ServiceExtensions.cs
  2. 35
      MultiTerm.Core/Types/EncodingType.cs
  3. 149
      MultiTerm.Core/ViewModel/ConsoleViewModel.cs
  4. 6
      MultiTerm.Core/ViewModel/SendReceiveViewModel.cs
  5. 119
      MultiTerm.Wpf/View/ConsoleView.xaml
  6. 48
      MultiTerm.Wpf/View/ConsoleView.xaml.cs
  7. 3
      MultiTerm.Wpf/View/SendReceiveView.xaml
  8. 3
      MultiTerm.Wpf/View/ShellView.xaml

@ -15,7 +15,7 @@ public static class ServiceExtensions
{ {
// add all implementations to the services collection // add all implementations to the services collection
services.AddTransient<ITerminalViewModel, SendReceiveViewModel>(); services.AddTransient<ITerminalViewModel, SendReceiveViewModel>();
// TODO extend services.AddTransient<ITerminalViewModel, ConsoleViewModel>();
// add a function to the services collection, which is used by the TerminalViewModelFactory // add a function to the services collection, which is used by the TerminalViewModelFactory
// to get a list of all registered implementations of ITerminalViewModel // to get a list of all registered implementations of ITerminalViewModel

@ -0,0 +1,35 @@
using System.ComponentModel;
namespace MultiTerm.Core.Types;
/// <summary>
/// Encoding types that are supported by this application.
/// Numbers correspond to the codepage number from the <see cref="Encoding"/> class by Microsoft.
/// </summary>
public enum EncodingType
{
/* Special Case Code Pages */
[Description("UTF-16")]
UTF16 = 1200,
[Description("UTF-16 Big Endian")]
UTF16_BE = 1201,
/* Latin 1 & ASCII Code Pages */
[Description("ASCII")]
ASCII = 20127,
[Description("Latin1 (ISO 8859-1)")]
Latin1 = 28591,
/* Special code pages */
[Description("UTF-7")]
UTF7 = 65000,
[Description("UTF-8")]
UTF8 = 65001,
[Description("UTF-32")]
UTF32 = 12000,
[Description("UTF-32 Big Endian")]
UTF32_BE = 12001
}

@ -0,0 +1,149 @@
using Common.AppSettings;
using Common.Logging;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using MultiTerm.Core.Types;
using MultiTerm.Protocols.Model;
using System.Text;
namespace MultiTerm.Core.ViewModel;
public partial class ConsoleViewModel : TerminalViewModel
{
public override TerminalViewType ViewType => TerminalViewType.Console;
private readonly ILogger logger;
private Encoding currentEncoding;
/* settings */
private readonly Encoding defaultEncoding = Encoding.ASCII; // default encoding instance
private const EncodingType defaultEncodingType = EncodingType.ASCII; // default encoding type
private const int DataMaxLength = 5000; // maximum amount of characters stored int the data textbox
#region Observable Properties
/// <summary>
/// Holds Console content.
/// </summary>
[ObservableProperty]
private string data = string.Empty;
/// <summary>
/// Encoding type of the sent and received messages.
/// </summary>
[ObservableProperty]
private EncodingType selectedEncodingType = defaultEncodingType;
/// <summary>
/// Holds message that is to be sent next. E.g. currently being typed in.
/// </summary>
[ObservableProperty]
private string sendableMessage = string.Empty;
#endregion
public ConsoleViewModel(IAppSettingsProvider appSettings, IMessenger messenger, ILogger logger) : base(appSettings, messenger)
{
this.logger = logger;
this.currentEncoding = this.GetEncoding(this.SelectedEncodingType);
}
public override void DisplayNewReceivedData(IEnumerable<ExtendedByte> newData)
{
// extract bytes
var bytes = newData.Select(b => b.Byte).ToArray();
string newDataDecoded = currentEncoding.GetString(bytes);
// calculate new legth
int newTotalLength = this.Data.Length + newDataDecoded.Length;
// trim current data at the beginning if new length is too long
if (newTotalLength > DataMaxLength)
{
// trim overshoot from start of string
this.Data = this.Data[(newTotalLength - DataMaxLength)..];
}
// append data
this.Data += newDataDecoded;
}
/// <summary>
/// Command to send a prepared message.
/// </summary>
[RelayCommand]
public void Send()
{
// encode
var encodedBytes = this.currentEncoding.GetBytes(this.SendableMessage);
// send
if (this.SendToCommunicationProtocol(encodedBytes))
{
// clear if sending was successful
this.SendableMessage = string.Empty;
}
}
/// <summary>
/// Command to clear the data that was received.
/// </summary>
[RelayCommand]
public void ClearData()
{
this.Data = string.Empty;
}
/// <summary>
/// Handles changing of encoding type.
/// </summary>
/// <param name="value">new value</param>
partial void OnSelectedEncodingTypeChanged(EncodingType value)
{
this.currentEncoding = this.GetEncoding(value);
}
#region Unused Methods
public override void DisplayNewSentData(IEnumerable<ExtendedByte> newData)
{
// no display of sent data
}
protected override void ActOnDataDisplayNewlineSeparatorTypeChanged(NewlineSeparatorType newType)
{
// No change when data display newline separator type changed
}
#endregion
#region Helpers
/// <summary>
/// Returns <see cref="Encoding"/> instance of <paramref name="encodingType"/>.
/// If the <paramref name="encodingType"/> is not supported or any issue arrived, the <see cref="SelectedEncodingType"/> is set to ASCII and ASCII <see cref="Encoding"/> is returned.
/// </summary>
/// <param name="encodingType">type of encoding</param>
/// <returns>encoding instance</returns>
private Encoding GetEncoding(EncodingType encodingType)
{
Encoding? encoding;
try
{
// Encoding Type value corresponds to codepage number, therefore can be get by cast to int
encoding = Encoding.GetEncoding(Convert.ToInt32(encodingType));
}
catch (Exception)
{
// log exception and set encoding to null
this.logger.LogWarn($"'{nameof(GetEncoding)}()' failed to resolve encoding type {encodingType} (codepage {Convert.ToInt32(encodingType)}). Reverted to default.", nameof(ConsoleViewModel));
encoding = null;
}
// failed to get encoding or got null encoding
if (encoding == null)
{
// set selected encoding type to default and return default encoding
this.SelectedEncodingType = defaultEncodingType;
encoding = defaultEncoding;
}
return encoding!;
}
#endregion
}

@ -51,11 +51,9 @@ public partial class SendReceiveViewModel : TerminalViewModel
private void Send() private void Send()
{ {
// send data // send data
bool successfullySent = this.SendToCommunicationProtocol(this.SendableData.GetBytes()); if (this.SendToCommunicationProtocol(this.SendableData.GetBytes()))
if (successfullySent)
{ {
// clear textbox // clear textbox if send was successful
this.SendableData.Clear(); this.SendableData.Clear();
} }
} }

@ -0,0 +1,119 @@
<UserControl x:Class="MultiTerm.Wpf.View.ConsoleView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:MultiTerm.Wpf.View"
xmlns:conv="clr-namespace:MultiTerm.Wpf.ValueConverters"
xmlns:protocol_serial="clr-namespace:MultiTerm.Protocols.Serial;assembly=MultiTerm.Protocols"
xmlns:protocol_usbhid="clr-namespace:MultiTerm.Protocols.UsbHid;assembly=MultiTerm.Protocols"
xmlns:protocol_udp="clr-namespace:MultiTerm.Protocols.Udp;assembly=MultiTerm.Protocols"
xmlns:protocol_tcp="clr-namespace:MultiTerm.Protocols.Tcp;assembly=MultiTerm.Protocols"
xmlns:types="clr-namespace:MultiTerm.Core.Types;assembly=MultiTerm.Core"
xmlns:settings_view="clr-namespace:MultiTerm.Wpf.View.SettingsView"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Loaded="UserControl_Loaded">
<UserControl.Resources>
<!-- Value Converters -->
<conv:EnumValueToEnumDescriptionConverter x:Key="EnumValToDescConverter"/>
<!-- Data Sources -->
<ObjectDataProvider x:Key="NewlineSeparatorTypeValues"
ObjectType="{x:Type sys:Enum}"
MethodName="GetValues">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="types:NewlineSeparatorType" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<ObjectDataProvider x:Key="EncodingTypeValues"
ObjectType="{x:Type sys:Enum}"
MethodName="GetValues">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="types:EncodingType" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<!-- Styling for all data items -->
<Style x:Key="DataContainerFontStyle" TargetType="{x:Type Control}">
<Setter Property="FontFamily" Value="Consolas"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="FontWeight" Value="ExtraLight"/>
</Style>
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Settings View Model -->
<GroupBox Header="Protocol Settings" Grid.Row="0" Grid.Column="0" Padding="5 1">
<!-- Register additional Settings ViewModels here -->
<ContentControl Content="{Binding ProtocolSettings}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type protocol_serial:SerialProtocolSettingsViewModel}">
<settings_view:SerialSettingsView/>
</DataTemplate>
<DataTemplate DataType="{x:Type protocol_usbhid:UsbHidProtocolSettingsViewModel}">
<settings_view:UsbHidSettingsView/>
</DataTemplate>
<DataTemplate DataType="{x:Type protocol_udp:UdpProtocolSettingsViewModel}">
<settings_view:UdpSettingsView/>
</DataTemplate>
<DataTemplate DataType="{x:Type protocol_tcp:TcpClientProtocolSettingsViewModel}">
<settings_view:TcpClientSettingsView/>
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
</GroupBox>
<!-- Settings -->
<GroupBox Header="Settings" Grid.Row="1" Grid.Column="0" Padding="5 1">
<DockPanel LastChildFill="False">
<Label DockPanel.Dock="Left" Margin="0 0 5 0" VerticalContentAlignment="Center" Content="Newline Separator"/>
<ComboBox DockPanel.Dock="Left" VerticalAlignment="Stretch" VerticalContentAlignment="Center" Width="Auto"
ItemsSource="{Binding Source={StaticResource NewlineSeparatorTypeValues}, Converter={StaticResource EnumValToDescConverter}}"
SelectedItem="{Binding Path=DataDisplayNewlineSeparatorType, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged,
Converter={StaticResource EnumValToDescConverter}}"/>
<Label DockPanel.Dock="Left" Margin="20 0 5 0" VerticalContentAlignment="Center" Content="Newline on Send"/>
<ComboBox DockPanel.Dock="Left" VerticalAlignment="Stretch" VerticalContentAlignment="Center" Width="Auto"
ItemsSource="{Binding Source={StaticResource NewlineSeparatorTypeValues}, Converter={StaticResource EnumValToDescConverter}}"
SelectedItem="{Binding Path=SendNewlineSeparatorType, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged,
Converter={StaticResource EnumValToDescConverter}}"/>
<Label DockPanel.Dock="Left" Margin="20 0 5 0" VerticalContentAlignment="Center" Content="Encoding"/>
<ComboBox DockPanel.Dock="Left" VerticalAlignment="Stretch" VerticalContentAlignment="Center" Width="Auto"
ItemsSource="{Binding Source={StaticResource EncodingTypeValues}, Converter={StaticResource EnumValToDescConverter}}"
SelectedItem="{Binding Path=SelectedEncodingType, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged,
Converter={StaticResource EnumValToDescConverter}}"/>
<Button Content="Clear" DockPanel.Dock="Right" Padding="10 3" Command="{Binding ClearDataCommand}"/>
</DockPanel>
</GroupBox>
<!-- Data view -->
<TextBox Grid.Row="2" Padding="5" Margin="0 5"
IsReadOnly="True"
TextWrapping="Wrap"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.CanContentScroll="true"
ScrollViewer.PanningMode="VerticalOnly"
Text="{Binding Path=Data}"
TextChanged="DataTextBox_TextChanged"
Style="{StaticResource DataContainerFontStyle}"/>
<!-- Sendable Message Input -->
<TextBox Grid.Row="3" Padding="5 3" Margin="0 5"
AllowDrop="False"
Text="{Binding Path=SendableMessage, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource DataContainerFontStyle}"
PreviewKeyDown="SendableMessageTextBox_PreviewKeyDown"/>
</Grid>
</UserControl>

@ -0,0 +1,48 @@
using MultiTerm.Core.ViewModel;
using System;
using System.Windows.Controls;
using System.Windows.Input;
namespace MultiTerm.Wpf.View;
public partial class ConsoleView : UserControl
{
private ConsoleViewModel? viewModel;
public ConsoleView()
{
InitializeComponent();
}
/// <summary>
/// After user control finished loading, the data context is casted and stored locally.
/// </summary>
private void UserControl_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
// cast to correct type
if (this.DataContext is not ConsoleViewModel dataContextVm)
{
throw new InvalidOperationException($"{nameof(ConsoleView)} got wrong type of DataContext.");
}
// store locally
viewModel = dataContextVm;
}
private void SendableMessageTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
// send if the key was enter
if (e.Key == Key.Enter)
{
// do not forward key => is handled
e.Handled = true;
this.viewModel!.Send();
}
}
private void DataTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
// scroll to end
if (sender is not TextBox textBox) { return; }
textBox.ScrollToEnd();
}
}

@ -70,8 +70,7 @@
ItemsSource="{Binding Source={StaticResource NewlineSeparatorTypeValues}, Converter={StaticResource EnumValToDescConverter}}" ItemsSource="{Binding Source={StaticResource NewlineSeparatorTypeValues}, Converter={StaticResource EnumValToDescConverter}}"
SelectedItem="{Binding Path=DataDisplayNewlineSeparatorType, Mode=TwoWay, SelectedItem="{Binding Path=DataDisplayNewlineSeparatorType, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged, UpdateSourceTrigger=PropertyChanged,
Converter={StaticResource EnumValToDescConverter}}"> Converter={StaticResource EnumValToDescConverter}}"/>
</ComboBox>
</DockPanel> </DockPanel>
</GroupBox> </GroupBox>

@ -153,6 +153,9 @@
<DataTemplate DataType="{x:Type vm:SendReceiveViewModel}"> <DataTemplate DataType="{x:Type vm:SendReceiveViewModel}">
<v:SendReceiveView/> <v:SendReceiveView/>
</DataTemplate> </DataTemplate>
<DataTemplate DataType="{x:Type vm:ConsoleViewModel}">
<v:ConsoleView/>
</DataTemplate>
</custom_controls:ExtendedTabControl.Resources> </custom_controls:ExtendedTabControl.Resources>
<!-- Content of tab with special caching behaviour --> <!-- Content of tab with special caching behaviour -->

Loading…
Cancel
Save