Implemented MultiFormatDataView for SentData,

separated Display settings in SendReceiveView
implemented Send Data Textbox not cleared when sending was not successful,
worked on formatting,
removed selector in MultiFormatDataView,
cleanup
master
Jonas Arnold 3 years ago
parent e7164953bb
commit 7061568cda
  1. 16
      MultiTerm.Core/ViewModel/SendReceiveViewModel.cs
  2. 7
      MultiTerm.Core/ViewModel/TerminalViewModel.cs
  3. 19
      MultiTerm.Protocols/CommunicationProtocol.cs
  4. 3
      MultiTerm.Protocols/ICommunicationProtocol.cs
  5. 4
      MultiTerm.Wpf.CustomControl/ExtendedTabControl/ExtendedTabControl.xaml
  6. 71
      MultiTerm.Wpf.CustomControl/MultiFormatDataView/MultiFormatDataView.cs
  7. 23
      MultiTerm.Wpf.CustomControl/MultiFormatDataView/MultiFormatDataView.xaml
  8. 29
      MultiTerm.Wpf.CustomControl/MultiFormatTextBox/MultiFormatTextBox.xaml
  9. 3
      MultiTerm.Wpf/MainWindow.xaml
  10. 52
      MultiTerm.Wpf/View/SendReceiveView.xaml
  11. 16
      MultiTerm.Wpf/View/SettingsView/SerialSettingsView.xaml

@ -5,7 +5,6 @@ using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using MultiTerm.Core.Model;
using MultiTerm.Core.Types;
using System.Diagnostics;
namespace MultiTerm.Core.ViewModel;
@ -19,12 +18,6 @@ public partial class SendReceiveViewModel : TerminalViewModel
[ObservableProperty]
private MultiFormatString sendableData = new();
/// <summary>
/// Temporary sent data property, for testing purposes.
/// </summary>
[ObservableProperty]
private string tempSentDataString = string.Empty;
/// <summary>
/// Constructor.
/// </summary>
@ -37,14 +30,13 @@ public partial class SendReceiveViewModel : TerminalViewModel
[RelayCommand]
private void Send()
{
// Temp
//var items = this.CommunicationData.SelectedReceivedData;
//Debugger.Break();
this.TempSentDataString = this.SendableData.ToAsciiEncodedString();
// send data
this.SendToCommunicationProtocol(this.SendableData.GetBytes());
bool successfullySent = this.SendToCommunicationProtocol(this.SendableData.GetBytes());
if (successfullySent)
{
// clear textbox
this.SendableData.Clear();
}
}
}

@ -155,7 +155,8 @@ public abstract partial class TerminalViewModel : ObservableObject, ITerminalVie
/// Appends configured <see cref="SendNewlineSeparatorType"/> to the end of string.
/// </summary>
/// <param name="data">data in form of enumerable (e.g. array)</param>
protected void SendToCommunicationProtocol(IEnumerable<byte> data)
/// <returns>when data could be sent successfully</returns>
protected bool SendToCommunicationProtocol(IEnumerable<byte> data)
{
// guard null values
if(this.CommunicationProtocol == null) { throw new NullReferenceException($"'{nameof(SendToCommunicationProtocol)}()' was called but {nameof(CommunicationProtocol)} is null."); }
@ -164,7 +165,7 @@ public abstract partial class TerminalViewModel : ObservableObject, ITerminalVie
if (this.CommunicationProtocol.IsConnected == false)
{
this.messenger.Send<IUserInterfaceMessage>(new ProtocolNotConnectedMessage("Cannot send message"));
return;
return false;
}
// add newline sequence to end of data
@ -182,7 +183,7 @@ public abstract partial class TerminalViewModel : ObservableObject, ITerminalVie
var dataWithNewlineSequence = data.Concat(newlineSequence);
// send
this.CommunicationProtocol.SendBytes(dataWithNewlineSequence.ToArray());
return this.CommunicationProtocol.SendBytes(dataWithNewlineSequence.ToArray());
}
#region Protocol Settings

@ -66,17 +66,28 @@ public abstract class CommunicationProtocol : ICommunicationProtocol
/// <returns>true if the data was sent successfully</returns>
protected abstract bool InternalSendBytes(byte[] bytes);
public void SendBytes(byte[] bytes)
public bool SendBytes(byte[] bytes)
{
bool success = true;
// guard is not connected => log warning. user of this function shall only use SendBytes if IsConnected is true
if (this.IsConnected == false) { this.logger.LogWarn($"'{nameof(SendBytes)}()' was reached with {nameof(IsConnected)} being false", nameof(CommunicationProtocol)); }
if (this.IsConnected == false)
{
this.logger.LogWarn($"'{nameof(SendBytes)}()' was reached with {nameof(IsConnected)} being false", nameof(CommunicationProtocol));
return false; // return and do not send
}
// send bytes and if the sending was cancelled report error
if (this.InternalSendBytes(bytes) == false)
// send bytes
success = this.InternalSendBytes(bytes);
// if the sending was cancelled report error
if (success == false)
{
this.logger.LogError($"'{nameof(SendBytes)}()' failed to send during {nameof(InternalSendBytes)}.", nameof(CommunicationProtocol));
this.messenger.Send<IUserInterfaceMessage>(new GenericUserInterfaceMessage("Failed to send message", MessageImportance.High));
}
return success;
}
/// <summary>

@ -57,5 +57,6 @@ public interface ICommunicationProtocol
/// Send data to the connected device.
/// </summary>
/// <param name="bytes">data to send, as an array of bytes</param>
void SendBytes(byte[] bytes);
/// <returns>true if the sending was successful</returns>
bool SendBytes(byte[] bytes);
}

@ -26,7 +26,9 @@
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Grid.Row="0" Grid.Column="0">
<TabPanel x:Name="headerPanel" Background="Transparent" IsItemsHost="true" Margin="2,2,2,0" KeyboardNavigation.TabIndex="1" Panel.ZIndex="1"/>
<Button x:Name="addButton" FontSize="16" Height="{Binding ElementName=headerPanel, Path=Height}" Width="{Binding ElementName=headerPanel, Path=Height}"> + </Button>
<Button x:Name="addButton" FontSize="16" Padding="5 0" VerticalContentAlignment="Center" VerticalAlignment="Stretch"
Height="{Binding ElementName=headerPanel, Path=Height}"
Width="{Binding ElementName=headerPanel, Path=Height}" >+</Button>
</StackPanel>
<Border x:Name="contentPanel" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Grid.Column="0" KeyboardNavigation.DirectionalNavigation="Contained" Grid.Row="1" KeyboardNavigation.TabIndex="2" KeyboardNavigation.TabNavigation="Local">
<ContentPresenter x:Name="PART_SelectedContentHost" ContentSource="SelectedContent" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>

@ -17,8 +17,7 @@ public class MultiFormatDataView : Control
{
private static readonly Dictionary<StackPanel, MultiFormatDataView> itemParentPairs = new();
private const string itemsControlTemplateName = "PART_ItemsControl";
private const string buttonClearTemplateName = "btnClear";
private const string selectorTemplateName = "comboBoxSelector";
private const string buttonClearTemplateName = "PART_ButtonClear";
private const string textBoxCharOnlyViewTemplateName = "PART_CharOnlyTextBox";
private ListBox? itemsControl;
private TextBox? tbCharOnlyView;
@ -30,21 +29,6 @@ public class MultiFormatDataView : Control
typeof(ICommunicationDataViewModel<ByteDataViewModel, ExtendedByte>), typeof(MultiFormatDataView),
new PropertyMetadata(null, OnDataSourcePropertyChanged));
public static readonly DependencyProperty SelectorItemsSourceProperty =
DependencyProperty.Register("SelectorItemsSource",
typeof(IEnumerable), typeof(MultiFormatDataView),
new PropertyMetadata(null, OnSelectorItemsSourceChanged));
public static readonly DependencyProperty SelectorSelectedItemProperty =
DependencyProperty.Register("SelectorSelectedItem",
typeof(object), typeof(MultiFormatDataView),
new PropertyMetadata(null, OnSelectorSelectedItemChanged));
public static readonly DependencyProperty SelectorDescriptionProperty =
DependencyProperty.Register("SelectorDescription",
typeof(string), typeof(MultiFormatDataView),
new PropertyMetadata(string.Empty, OnSelectorDescriptionChanged));
public static readonly DependencyProperty RealizedItemsCountProperty =
DependencyProperty.Register("RealizedItemsCount",
typeof(uint), typeof(MultiFormatDataView),
@ -75,36 +59,6 @@ public class MultiFormatDataView : Control
set { SetValue(DataSourceProperty, value); }
}
/// <summary>
/// .NET Property for SelectorItemsSource.
/// </summary>
[Bindable(true)]
public IEnumerable SelectorItemsSource
{
get { return (IEnumerable)GetValue(SelectorItemsSourceProperty); }
set { SetValue(SelectorItemsSourceProperty, value); }
}
/// <summary>
/// .NET Property for SelectorSelectedItem.
/// </summary>
[Bindable(true)]
public string SelectorSelectedItem
{
get { return (string)GetValue(SelectorSelectedItemProperty); }
set { SetValue(SelectorSelectedItemProperty, value); }
}
/// <summary>
/// .NET Property for SelectorDescription.
/// </summary>
[Bindable(true)]
public string SelectorDescription
{
get { return (string)GetValue(SelectorDescriptionProperty); }
set { SetValue(SelectorDescriptionProperty, value); }
}
/// <summary>
/// .NET Property for RealizedItemsCount.
/// </summary>
@ -312,29 +266,6 @@ public class MultiFormatDataView : Control
}
#endregion
#region Selector (ComboBox) handling
private static void OnSelectorItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// extract instance and guard null
//if (d is not MultiFormatDataView mfdv) { return; }
// extract instance of new Value and guard null
//if (e.NewValue is not IEnumerable enumerable) { return; }
// nothing to do
}
private static void OnSelectorDescriptionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// nothing to do
}
private static void OnSelectorSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// nothing to do
}
#endregion
#region Realized Item Count
private static void OnRealizedItemsCountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{

@ -128,22 +128,13 @@
<DockPanel>
<GroupBox Header="Display Formats" DockPanel.Dock="Top">
<!-- Top row with options and buttons -->
<DockPanel LastChildFill="True">
<DockPanel LastChildFill="False" Margin="5 0">
<!-- Format selectors -->
<CheckBox DockPanel.Dock="Left" Content="Character" VerticalAlignment="Center" x:Name="cbCharacter" Padding="20 0" IsChecked="True"/>
<CheckBox DockPanel.Dock="Left" Content="Hexadecimal" VerticalAlignment="Center" x:Name="cbHex" Padding="20 0"/>
<CheckBox DockPanel.Dock="Left" Content="Binary" VerticalAlignment="Center" x:Name="cbBin" Padding="20 0"/>
<CheckBox DockPanel.Dock="Left" Content="Character" VerticalAlignment="Center" VerticalContentAlignment="Center" x:Name="cbCharacter" Padding="10 0 30 0" IsChecked="True"/>
<CheckBox DockPanel.Dock="Left" Content="Hexadecimal" VerticalAlignment="Center" VerticalContentAlignment="Center" x:Name="cbHex" Padding="10 0 30 0"/>
<CheckBox DockPanel.Dock="Left" Content="Binary" VerticalAlignment="Center" VerticalContentAlignment="Center" x:Name="cbBin" Padding="10 0 30 0"/>
<!-- Clear Button -->
<Button DockPanel.Dock="Right" x:Name="btnClear" Content="Clear" Margin="2" />
<!-- ComboBox -->
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center" >
<Label Content="{TemplateBinding SelectorDescription}" VerticalAlignment="Center" Margin="0 0 5 0"/>
<!-- ComboBox using "two way TemplateBinding" for SelectedItem -->
<ComboBox x:Name="comboBoxSelector"
ItemsSource="{TemplateBinding SelectorItemsSource}"
SelectedItem="{Binding Path=SelectorSelectedItem, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
VerticalAlignment="Center" Width="80"/>
</StackPanel>
<Button DockPanel.Dock="Right" x:Name="PART_ButtonClear" Width="Auto" Padding="7 2" Content="Clear" />
</DockPanel>
</GroupBox>
<!-- Grid that contains main control and a realited items count on top -->
@ -243,8 +234,8 @@
<!-- Realized Items Display (on top of ListView because if comes later in the list) -->
<StackPanel Grid.Column="0" Grid.Row="0" Margin="5 5 25 5" Grid.ZIndex="1" Background="Azure"
Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Right">
<Label Content="Realized Items: " Margin="0 0 5 0"/>
<Label Width="30" Content="{TemplateBinding RealizedItemsCount}"/>
<Label Content="Realized Items: " HorizontalAlignment="Left" Margin="0 0 0 0"/>
<Label Width="40" HorizontalContentAlignment="Right" Content="{TemplateBinding RealizedItemsCount}"/>
</StackPanel>
</Grid>

@ -3,16 +3,6 @@
xmlns:local="clr-namespace:MultiTerm.Wpf.CustomControl">
<Style TargetType="{x:Type local:MultiFormatTextBox}">
<!--<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
<Setter Property="BorderBrush" Value="{StaticResource TextBox.Static.Border}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="AllowDrop" Value="true"/>
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>-->
<Style.Resources>
<SolidColorBrush x:Key="MultiFormatTextBox.CHAR.Background" Color="#B4EBEB"/>
<SolidColorBrush x:Key="MultiFormatTextBox.HEX.Background" Color="#C8C8FF"/>
@ -22,26 +12,11 @@
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MultiFormatTextBox}">
<DockPanel LastChildFill="True">
<ComboBox DockPanel.Dock="Left" x:Name="PART_ComboBox" Width="60"></ComboBox>
<RichTextBox DockPanel.Dock="Right" x:Name="PART_RichTextBox"></RichTextBox>
<ComboBox DockPanel.Dock="Left" x:Name="PART_ComboBox" Width="70"></ComboBox>
<RichTextBox DockPanel.Dock="Right" VerticalContentAlignment="Center" x:Name="PART_RichTextBox"></RichTextBox>
</DockPanel>
<!--<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Opacity" TargetName="border" Value="0.56"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="{StaticResource TextBox.MouseOver.Border}"/>
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="{StaticResource TextBox.Focus.Border}"/>
</Trigger>
</ControlTemplate.Triggers>-->
</ControlTemplate>
</Setter.Value>
</Setter>
<!--<Setter Property="MinWidth" Value="10"/>
<Style.BasedOn>
<StaticResource ResourceKey="{x:Type RichTextBox}"/>
</Style.BasedOn>-->
</Style>
</ResourceDictionary>

@ -9,6 +9,7 @@
mc:Ignorable="d"
Height="900" Width="1600"
MinHeight="900" MinWidth="1600"
Title="MultiTerm">
Title="MultiTerm"
FontSize="14">
<v:ShellView/>
</Window>

@ -32,7 +32,8 @@
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="50px"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="5*"/>
<RowDefinition Height="3*"/>
</Grid.RowDefinitions>
@ -48,14 +49,24 @@
</ContentControl.Resources>
</ContentControl>
</GroupBox>
<GroupBox Header="Receive" Grid.Row="1" Padding="5">
<custom_controls:MultiFormatDataView x:Name="mfdvReceive"
DataSource="{Binding ReceivedData}"
SelectorItemsSource="{Binding Source={StaticResource NewlineSeparatorTypeValues}, Converter={StaticResource EnumValToDescConverter}}"
SelectorSelectedItem="{Binding Path=ReceivedData.NewlineSeparator, Mode=TwoWay,
<!-- Display settings -->
<GroupBox Header="Display" 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}}"
SelectorDescription="Newline Separator">
Converter={StaticResource EnumValToDescConverter}}">
</ComboBox>
</DockPanel>
</GroupBox>
<!-- Data View -->
<GroupBox Header="Receive" Grid.Row="2" Padding="5">
<!-- Received Data View -->
<custom_controls:MultiFormatDataView x:Name="mfdvReceive" DataSource="{Binding ReceivedData}">
<behaviors:Interaction.Triggers>
<behaviors:EventTrigger EventName="ClearRequested" SourceObject="{Binding ElementName=mfdvReceive}">
@ -63,13 +74,20 @@
</behaviors:EventTrigger>
</behaviors:Interaction.Triggers>
</custom_controls:MultiFormatDataView>
</GroupBox>
<GroupBox Header="Send" Grid.Row="2" Padding="5">
<GroupBox Header="Send" Grid.Row="3" Padding="5">
<DockPanel>
<!-- Send text box -->
<DockPanel DockPanel.Dock="Top" LastChildFill="True">
<Button DockPanel.Dock="Right" Content="Send" Command="{Binding SendCommand}"/>
<ComboBox DockPanel.Dock="Right" 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}}">
</ComboBox>
<Label DockPanel.Dock="Right" Content="Send on Enter:"/>
<Button DockPanel.Dock="Right" Content="Send" Margin="0 0 10 0" Padding="10 0" Command="{Binding SendCommand}"/>
<custom_controls:MultiFormatTextBox x:Name="sendTextBox" DockPanel.Dock="Left"
CurrentMultiFormatString="{Binding Path=SendableData, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
@ -81,9 +99,17 @@
</custom_controls:MultiFormatTextBox>
</DockPanel>
<Separator/>
<TextBox DockPanel.Dock="Bottom" Text="{Binding TempSentDataString}">
</TextBox>
<!-- Sent Data View -->
<custom_controls:MultiFormatDataView x:Name="mfdvSend" DataSource="{Binding SentData}">
<behaviors:Interaction.Triggers>
<behaviors:EventTrigger EventName="ClearRequested" SourceObject="{Binding ElementName=mfdvSend}">
<behaviors:InvokeCommandAction Command="{Binding SentData.ClearCommand}" />
</behaviors:EventTrigger>
</behaviors:Interaction.Triggers>
</custom_controls:MultiFormatDataView>
</DockPanel>
</GroupBox>
</Grid>

@ -43,27 +43,29 @@
</StackPanel.Resources>
<Label Margin="10 0" VerticalAlignment="Center">Port: </Label>
<ComboBox Width="75" ItemsSource="{Binding Path=ComPorts}" SelectedItem="{Binding Path=PortName, Mode=TwoWay}"/>
<Button Margin="5 0" Content="R" Command="{Binding ReloadComPortsCommand}" Width="25"/>
<ComboBox Width="100" ItemsSource="{Binding Path=ComPorts}" SelectedItem="{Binding Path=PortName, Mode=TwoWay}"
VerticalContentAlignment="Center"/>
<Button Margin="5 0" Content="R" VerticalContentAlignment="Center"
Command="{Binding ReloadComPortsCommand}" Width="25"/>
<Label Margin="20 0 10 0" VerticalAlignment="Center">Baud Rate:</Label>
<TextBox Width="100" Text="{Binding Path=BaudRate, Converter={StaticResource IntToStringConverter}, Mode=TwoWay}" />
<TextBox Width="100" VerticalContentAlignment="Center" Text="{Binding Path=BaudRate, Converter={StaticResource IntToStringConverter}, Mode=TwoWay}" />
<Label Margin="10 0 10 0" VerticalAlignment="Center">Data Bits:</Label>
<TextBox Width="40" Text="{Binding Path=DataBits, Converter={StaticResource IntToStringConverter}, Mode=TwoWay}" />
<TextBox Width="40" VerticalContentAlignment="Center" Text="{Binding Path=DataBits, Converter={StaticResource IntToStringConverter}, Mode=TwoWay}" />
<Label Margin="10 0 10 0" VerticalAlignment="Center">Parity:</Label>
<ComboBox Width="60"
<ComboBox Width="Auto" VerticalContentAlignment="Center"
ItemsSource="{Binding Source={StaticResource ParityValues}, Converter={StaticResource EnumValToDescConverter}}"
SelectedItem="{Binding Path=Parity, Converter={StaticResource EnumValToDescConverter}, Mode=TwoWay}"/>
<Label Margin="10 0 10 0" VerticalAlignment="Center">Stop Bits:</Label>
<ComboBox Width="50"
<ComboBox Width="Auto" VerticalContentAlignment="Center"
ItemsSource="{Binding Source={StaticResource StopBitsValues}, Converter={StaticResource EnumValToDescConverter}}"
SelectedItem="{Binding Path=StopBits, Converter={StaticResource EnumValToDescConverter}, Mode=TwoWay}"/>
<Label Margin="10 0 10 0" VerticalAlignment="Center">Handshake:</Label>
<ComboBox Width="100"
<ComboBox Width="Auto" VerticalContentAlignment="Center"
ItemsSource="{Binding Source={StaticResource HandshakeValues}, Converter={StaticResource EnumValToDescConverter}}"
SelectedItem="{Binding Path=Handshake, Converter={StaticResource EnumValToDescConverter}, Mode=TwoWay}"/>

Loading…
Cancel
Save