implemented first version of send history,

implemented IHistoryElement, StringHistoryElement,
ICloneable for MultiFormatString,
fixed MultiFormatTextBox not resetting when overwritten
master
Jonas Arnold 3 years ago
parent 4247614035
commit 3539ecb0da
  1. 14
      MultiTerm.Core/Model/IHistoryElement.cs
  2. 17
      MultiTerm.Core/Model/MultiFormatString.cs
  3. 23
      MultiTerm.Core/Model/StringHistoryElement.cs
  4. 10
      MultiTerm.Core/ViewModel/ConsoleViewModel.cs
  5. 10
      MultiTerm.Core/ViewModel/SendReceiveViewModel.cs
  6. 42
      MultiTerm.Core/ViewModel/TerminalViewModel.cs
  7. 8
      MultiTerm.Wpf.CustomControl/MultiFormatTextBox/MultiFormatTextBox.cs
  8. BIN
      MultiTerm.Wpf/Assets/mdi-import.png
  9. 2
      MultiTerm.Wpf/MultiTerm.Wpf.csproj
  10. 61
      MultiTerm.Wpf/View/ShellView.xaml

@ -0,0 +1,14 @@
namespace MultiTerm.Core.Model;
/// <summary>
/// Represents an element of a history.
/// Required to have a common <see cref="DisplayText"/> property to access.
/// Must also implement <see cref="ICloneable"/> in order to clone the object before saving it to the history.
/// </summary>
public interface IHistoryElement : ICloneable
{
/// <summary>
/// Displayable text of this element.
/// </summary>
string DisplayText { get; }
}

@ -7,11 +7,18 @@ namespace MultiTerm.Core.Model;
/// <summary>
/// A string that contains symbols of multiple formats such as a standard character as well as hexadecimal or binary parts.
/// </summary>
public class MultiFormatString: ObservableCollection<IFormattedCharacter>
public class MultiFormatString: ObservableCollection<IFormattedCharacter>, IHistoryElement, ICloneable
{
private const int charsPerHexBundle = 2;
private const int charsPerBinBundle = 8;
#region IHistoryElement implementation
public string DisplayText => this.ToAsciiEncodedString();
#endregion
public MultiFormatString() { }
public MultiFormatString(IEnumerable<IFormattedCharacter> collection) : base(collection) { }
#region Insertion / Removal of Characters
/// <summary>
/// Inserts item at given index after checking if its valid using <see cref="ValidateValue(FormatType, string)"/>.
@ -341,5 +348,13 @@ public class MultiFormatString: ObservableCollection<IFormattedCharacter>
var bytes = GetBytesFromString(binaryString, 2, charsPerBinBundle);
return Encoding.ASCII.GetString(bytes);
}
#endregion
#region ICloneable implementation
public object Clone()
{
return new MultiFormatString(this);
}
#endregion
}

@ -0,0 +1,23 @@
namespace MultiTerm.Core.Model;
/// <summary>
/// Generic history element, which allows to use a string as history element.
/// </summary>
public class StringHistoryElement : IHistoryElement, ICloneable
{
public string Text { get; }
public string DisplayText => this.Text;
public StringHistoryElement(string text)
{
Text = text;
}
#region ICloneable implementation
public object Clone()
{
return this.MemberwiseClone();
}
#endregion
}

@ -3,6 +3,7 @@ using Common.Logging;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using MultiTerm.Core.Model;
using MultiTerm.Core.Types;
using MultiTerm.Protocols.Model;
using System.Text;
@ -76,11 +77,20 @@ public partial class ConsoleViewModel : TerminalViewModel
// send
if (this.SendToCommunicationProtocol(encodedBytes))
{
// add to history
this.AddToSendHistory(new StringHistoryElement(this.SendableMessage));
// clear if sending was successful
this.SendableMessage = string.Empty;
}
}
protected override void HandleInsertElementFromHistory(object element)
{
if(element is not StringHistoryElement elementString) { throw new Exception($"'{HandleInsertElementFromHistory}()' in {nameof(ConsoleViewModel)} got wrong type of element. Got Type: {element.GetType()}"); }
// overwrite sendable message with history element
this.SendableMessage = elementString.Text;
}
/// <summary>
/// Command to clear the data that was received.
/// </summary>

@ -53,11 +53,21 @@ public partial class SendReceiveViewModel : TerminalViewModel
// send data
if (this.SendToCommunicationProtocol(this.SendableData.GetBytes()))
{
// add to history
this.AddToSendHistory(this.SendableData);
// clear textbox if send was successful
this.SendableData.Clear();
}
}
protected override void HandleInsertElementFromHistory(object element)
{
if(element is not MultiFormatString mfs) { throw new Exception($"'{HandleInsertElementFromHistory}()' in {nameof(SendReceiveViewModel)} got wrong type of element. Got Type: {element.GetType()}"); }
// overwrite
this.SendableData = mfs;
}
public override void DisplayNewReceivedData(IEnumerable<ExtendedByte> newData)
{
this.ReceivedData!.HandleNewData(newData);

@ -4,11 +4,13 @@ using Common.Messaging;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using MultiTerm.Core.Model;
using MultiTerm.Core.Types;
using MultiTerm.Protocols;
using MultiTerm.Protocols.Model;
using MultiTerm.Protocols.Types;
using System.Collections.Concurrent;
using System.Collections.ObjectModel;
namespace MultiTerm.Core.ViewModel;
@ -94,6 +96,11 @@ public abstract partial class TerminalViewModel : ObservableObject, ITerminalVie
[ObservableProperty]
private string communicationProtocolLongIdentifier = string.Empty;
/// <summary>
/// Collection of sent messages as <see cref="IHistoryElement"/>.
/// </summary>
[ObservableProperty]
ObservableCollection<IHistoryElement> sentMessagesHistory = new();
#endregion
@ -331,4 +338,39 @@ public abstract partial class TerminalViewModel : ObservableObject, ITerminalVie
}
#endregion
#region History
/// <summary>
/// Creates a clone of the passed element and saves that to the <see cref="SentMessagesHistory"/>.
/// </summary>
/// <param name="element">element to add to the history</param>
protected void AddToSendHistory(IHistoryElement element)
{
// shallow copy of element (so that element can be changed externally)
var copy = (IHistoryElement)element.Clone();
// add to list
this.SentMessagesHistory.Add(copy);
}
/// <summary>
/// Shall handle the insertion of an element from the <see cref="SentMessagesHistory"/>
/// into the current sendable message area.
/// </summary>
/// <param name="element">copy of element as object</param>
protected abstract void HandleInsertElementFromHistory(object element);
/// <summary>
/// Allows to insert an element from the history into the current send message.
/// Creates a copy of the object, so it can be changed externally.
/// </summary>
[RelayCommand]
public void InsertElementFromHistory(IHistoryElement element)
{
// shallow copy of element (so that element can be changed externally)
var copy = (IHistoryElement)element.Clone();
// let inheriting classes handle it
this.HandleInsertElementFromHistory(copy);
}
#endregion
}

@ -137,10 +137,14 @@ public class MultiFormatTextBox : Control
incc.CollectionChanged += mftb.MultiFormatString_CollectionChanged;
}
}
else if(mftb.CurrentMultiFormatString == null)
// reset textbox and insert items if there are any
mftb.ResetTextBox();
if(mftb.CurrentMultiFormatString.Count > 0)
{
mftb.ResetTextBox();
mftb.AddItemsToTextBox(mftb.CurrentMultiFormatString, 0);
}
}
/// <summary>

Binary file not shown.

After

Width:  |  Height:  |  Size: 506 B

@ -9,6 +9,7 @@
<ItemGroup>
<None Remove="Assets\mdi-console-white.png" />
<None Remove="Assets\mdi-import.png" />
<None Remove="Assets\mdi-keyboard.png" />
<None Remove="Assets\mdi-network.png" />
<None Remove="Assets\mdi-serial-port.png" />
@ -29,6 +30,7 @@
<ItemGroup>
<Resource Include="Assets\mdi-console-white.png" />
<Resource Include="Assets\mdi-import.png" />
<Resource Include="Assets\mdi-keyboard.png" />
<Resource Include="Assets\mdi-network.png" />
<Resource Include="Assets\mdi-serial-port.png" />

@ -97,11 +97,6 @@
</StatusBar>
<Separator DockPanel.Dock="Bottom" Margin="0 5 0 5"/>
<!-- Left Panel that holds History and Command Sets -->
<StackPanel Orientation="Vertical" DockPanel.Dock="Left" Width="150">
</StackPanel>
<!-- Grid to contain hidden add Tab Button, and the tab control -->
<Grid DockPanel.Dock="Right">
<Grid.RowDefinitions>
@ -109,8 +104,8 @@
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="6*" MinWidth="1200"/>
</Grid.ColumnDefinitions>
<Grid.Resources>
@ -141,6 +136,56 @@
</Button.ContextMenu>
</Button>
<!-- Left Panel that holds History and Command Sets -->
<StackPanel Orientation="Vertical" Grid.Row="1" Grid.Column="0" Width="Auto">
<GroupBox Header="Send History">
<!-- History -->
<ListView x:Name="historyListView"
ItemsSource="{Binding Path=SelectedTerminalViewModel.SentMessagesHistory}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Auto">
<!-- Hide ListView if there are no items -->
<ListView.Style>
<Style TargetType="{x:Type ListView}">
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<!-- Collapsed when no history available (0 items, fallback 1, to not hide it on error) -->
<DataTrigger Binding="{Binding Path=SelectedTerminalViewModel.SentMessagesHistory.Count, FallbackValue=1, TargetNullValue=1}" Value="0">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.Style>
<!-- Make items non-selectable and stretch -->
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Focusable" Value="false"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
<!-- Template for item -->
<ListView.ItemTemplate>
<DataTemplate>
<DockPanel LastChildFill="False" HorizontalAlignment="Stretch">
<TextBlock DockPanel.Dock="Left" Text="{Binding DisplayText}"
VerticalAlignment="Center"/>
<!-- Button Command binds with DataContext of historyListView, CommandParameter binds with the item -->
<Button DockPanel.Dock="Right" ToolTip="Insert"
Command="{Binding Path=DataContext.SelectedTerminalViewModel.InsertElementFromHistoryCommand, ElementName=historyListView}"
CommandParameter="{Binding}">
<Image Source="/Assets/mdi-import.png" Width="20" Margin="0"/>
</Button>
</DockPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</GroupBox>
</StackPanel>
<!-- Splitter between two parts of UI -->
<GridSplitter Grid.Column="0" Grid.RowSpan="2" Width="2.5" Background="LightGray"/>
<!-- Tab control -->
<custom_controls:ExtendedTabControl Grid.Row="1" Grid.Column="1"
x:Name="terminalTabControl"
IsSynchronizedWithCurrentItem="True"
@ -164,7 +209,7 @@
<ContentControl Content="{Binding}"/>
</DataTemplate>
</behaviours:TabContent.Template>
<!-- Tab Header Template -->
<custom_controls:ExtendedTabControl.ItemTemplate>
<DataTemplate>

Loading…
Cancel
Save