implemented newline handling in CommunicationDataViewModel,

implemented handling when newline separator is changed,
removed newline sequence variables from IProtocolSettings,
changed display style of RealizedItems in MultiFormatDataView,
introduced Selector for NewlineSeparator in MultiFormatDataView
master
Jonas Arnold 3 years ago
parent 907dac42f8
commit 493260dac7
  1. 211
      MultiTerm.Core/ViewModel/CommunicationDataViewModel.cs
  2. 9
      MultiTerm.Core/ViewModel/SendReceiveViewModel.cs
  3. 99
      MultiTerm.Core/ViewModel/TerminalViewModel.cs
  4. 10
      MultiTerm.Protocols/IProtocolSettings.cs
  5. 6
      MultiTerm.Protocols/Serial/SerialProtocolSettingsViewModel.cs
  6. 73
      MultiTerm.Wpf.CustomControl/MultiFormatDataView/MultiFormatDataView.cs
  7. 94
      MultiTerm.Wpf.CustomControl/MultiFormatDataView/MultiFormatDataView.xaml
  8. 25
      MultiTerm.Wpf/View/SendReceiveView.xaml

@ -1,5 +1,6 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using MultiTerm.Core.Types;
using MultiTerm.Protocols; using MultiTerm.Protocols;
using MultiTerm.Protocols.Model; using MultiTerm.Protocols.Model;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
@ -9,6 +10,8 @@ namespace MultiTerm.Core.ViewModel;
public partial class CommunicationDataViewModel : ObservableObject public partial class CommunicationDataViewModel : ObservableObject
{ {
private ICommunicationProtocol? communicationProtocol; private ICommunicationProtocol? communicationProtocol;
private NewlineSeparatorType currentReceiveNewlineSeparatorType = NewlineSeparatorType.None;
private NewlineSeparatorType currentSentNewlineSeparatorType = NewlineSeparatorType.None;
/// <summary> /// <summary>
/// Represents the collection of received characters from a communication protocol. /// Represents the collection of received characters from a communication protocol.
@ -76,22 +79,220 @@ public partial class CommunicationDataViewModel : ObservableObject
} }
} }
private void CommunicationProtocol_ReceivedDataEvent(object? sender, ReceivedDataEventArgs e) /// <summary>
/// Overwrites local variables for current newline separators and triggers recration of datacollections according to new newline separators (only if they changed).
/// </summary>
/// <param name="receiveNewlineSeparatorType">new receive newline separator type or null</param>
/// <param name="sendNewlineSeparatorType">new send newline separator type or null</param>
public void ConfigureNewlineSeparators(NewlineSeparatorType? receiveNewlineSeparatorType, NewlineSeparatorType? sendNewlineSeparatorType)
{ {
foreach (var receivedChar in e.ReceivedCharacters) if (receiveNewlineSeparatorType != null)
{ {
//this.ReceivedData.Add(receivedChar); // TODO Fix // if it is the same as the current newline separator type => ignore
if(receiveNewlineSeparatorType == this.currentReceiveNewlineSeparatorType) { return; }
// set new newline separator type and overwrite data collection (triggers property changed)
this.currentReceiveNewlineSeparatorType = (NewlineSeparatorType)receiveNewlineSeparatorType;
this.ReceivedData = ReorderDataCollection(this.ReceivedData, this.currentReceiveNewlineSeparatorType);
}
if (sendNewlineSeparatorType != null)
{
// if it is the same as the current newline separator type => ignore
if (sendNewlineSeparatorType == this.currentSentNewlineSeparatorType) { return; }
// set new newline separator type and overwrite data collection (triggers property changed)
this.currentSentNewlineSeparatorType = (NewlineSeparatorType)sendNewlineSeparatorType;
this.SentData = ReorderDataCollection(this.SentData, this.currentSentNewlineSeparatorType);
} }
} }
/// <summary>
/// Function that reorders a given collection using the given <paramref name="newlineSeparatorType"/>.
/// Reordered collection is returned, but LineIdentifier is also overwritten in the <paramref name="currentCollection"/> parameter.
/// </summary>
/// <param name="currentCollection">items in this collection will be reordered</param>
/// <param name="newlineSeparatorType">separator between lines</param>
/// <returns>reordered collection</returns>
private static ObservableCollection<DataViewModel> ReorderDataCollection(ObservableCollection<DataViewModel> currentCollection, NewlineSeparatorType newlineSeparatorType)
{
ObservableCollection<DataViewModel> newCollection = new();
int lineCounter = 0;
List<char>? previousCharacters = null;
foreach (var item in currentCollection)
{
item.LineIdentifier = lineCounter;
newCollection.Add(item);
switch (ShouldIntroduceNewlineAfterThisCharacter(item.Character.Character, previousCharacters, newlineSeparatorType))
{
case IntroduceNewlineAfterThisCharacterResult.NoNewline:
// a decision could be made, previousCharacters can be cleared
if(previousCharacters != null) { previousCharacters = null; }
// nothing to do
break;
case IntroduceNewlineAfterThisCharacterResult.IntroduceNewline:
// a decision could be made, previousCharacters can be cleared
if (previousCharacters != null) { previousCharacters = null; }
// increase line count and break
lineCounter++;
break;
case IntroduceNewlineAfterThisCharacterResult.RequiresMoreCharacters:
// first time that more characters are required => create new list
previousCharacters ??= new List<char>();
// add current character to list
previousCharacters.Add(item.Character.Character);
break;
default:
throw new Exception($"'{nameof(ReorderDataCollection)}()' failed because of error when checking if a newline should be introduced.");
}
}
return newCollection;
}
/// <summary>
/// Result type for <see cref="ShouldIntroduceNewlineAfterThisCharacter"/>
/// </summary>
public enum IntroduceNewlineAfterThisCharacterResult
{
/// <summary>
/// No newline is required.
/// </summary>
NoNewline,
/// <summary>
/// A newline shall be introduced after this character.
/// </summary>
IntroduceNewline,
/// <summary>
/// Following characters are required to finalize result wether a newline shall be introduced or not.
/// </summary>
RequiresMoreCharacters
}
/// <summary>
/// Function to check wether a newline shall be introduced after the given character.
/// Since some newline sequences will require multiple characters in correct order, a more complex handling is required, which is possible using this function.
/// </summary>
/// <param name="character">the current character in the collection (or a single character)</param>
/// <param name="previousCharacters">list of previous character, newest at the last position of the list, null if not required</param>
/// <param name="newlineSeparatorType">separator type</param>
/// <returns>complex result of type <see cref="IntroduceNewlineAfterThisCharacterResult"/></returns>
/// <exception cref="NotImplementedException">if the handling for the <paramref name="newlineSeparatorType"/> is not implemented</exception>
private static IntroduceNewlineAfterThisCharacterResult ShouldIntroduceNewlineAfterThisCharacter(char character, List<char>? previousCharacters, NewlineSeparatorType newlineSeparatorType)
{
var result = IntroduceNewlineAfterThisCharacterResult.NoNewline;
switch (newlineSeparatorType)
{
case NewlineSeparatorType.None:
break;
case NewlineSeparatorType.CR:
if(character == '\r')
{
result = IntroduceNewlineAfterThisCharacterResult.IntroduceNewline;
}
break;
case NewlineSeparatorType.LF:
if (character == '\n')
{
result = IntroduceNewlineAfterThisCharacterResult.IntroduceNewline;
}
break;
case NewlineSeparatorType.CR_LF:
if (character == '\r')
{
result = IntroduceNewlineAfterThisCharacterResult.RequiresMoreCharacters;
}
if (character == '\n')
{
if(previousCharacters != null && previousCharacters.Last() == '\r')
{
result = IntroduceNewlineAfterThisCharacterResult.IntroduceNewline;
}
}
break;
default:
throw new NotImplementedException($"'{nameof(ShouldIntroduceNewlineAfterThisCharacter)}()' does not implement handling for {nameof(NewlineSeparatorType)} {newlineSeparatorType}");
}
return result;
}
private int receivedDataCharacterCount = 0;
private List<char>? listOfPreviouslyReceivedCharacters = null;
private void CommunicationProtocol_ReceivedDataEvent(object? sender, ReceivedDataEventArgs e)
{
HandleNewCharacters(this.ReceivedData, ref this.receivedDataCharacterCount, ref this.listOfPreviouslyReceivedCharacters, this.currentReceiveNewlineSeparatorType, e.ReceivedCharacters);
}
private int sentDataCharacterCount = 0;
private List<char>? listOfPreviouslySentCharacters = null;
private void CommunicationProtocol_SentDataEvent(object? sender, SentDataEventArgs e) private void CommunicationProtocol_SentDataEvent(object? sender, SentDataEventArgs e)
{ {
foreach (var sentChar in e.SentCharacters) HandleNewCharacters(this.SentData, ref this.sentDataCharacterCount, ref this.listOfPreviouslySentCharacters, this.currentSentNewlineSeparatorType, e.SentCharacters);
}
/// <summary>
/// Function that handles a list of new characters that should end up in the collection <paramref name="dataCollection"/>.
/// In case a new line is required, according to the given <paramref name="newlineSeparatorType"/>, it is automatically introduced.
/// Following parameters need to be referenced and stored outside: <paramref name="collectionLineCounter"/> and <paramref name="previousCharacters"/>.
/// </summary>
/// <param name="dataCollection">collection to add the characters to</param>
/// <param name="collectionLineCounter">current line count</param>
/// <param name="previousCharacters">list of previous character, newest at the last position of the list, null if nothing is stored</param>
/// <param name="newlineSeparatorType">separator between seperate lines</param>
/// <param name="characters">characters to add to the <paramref name="dataCollection"/></param>
/// <exception cref="Exception">in case of any error</exception>
private void HandleNewCharacters(ObservableCollection<DataViewModel> dataCollection,
ref int collectionLineCounter,
ref List<char>? previousCharacters,
NewlineSeparatorType newlineSeparatorType,
IEnumerable<ExtendedChar> characters)
{
// go through every character
foreach (var newExtdChar in characters)
{ {
//this.SentCharacters.Add(sentChar); // TODO Fix // add to collection with the current counter
dataCollection.Add(new DataViewModel(newExtdChar, collectionLineCounter));
switch (ShouldIntroduceNewlineAfterThisCharacter(newExtdChar.Character, previousCharacters, newlineSeparatorType))
{
case IntroduceNewlineAfterThisCharacterResult.NoNewline:
// a decision could be made, previousCharacters can be cleared
if (previousCharacters != null) { previousCharacters = null; }
// nothing to do
break;
case IntroduceNewlineAfterThisCharacterResult.IntroduceNewline:
// a decision could be made, previousCharacters can be cleared
if (previousCharacters != null) { previousCharacters = null; }
// increase line count and break
collectionLineCounter++;
break;
case IntroduceNewlineAfterThisCharacterResult.RequiresMoreCharacters:
// first time that more characters are required => create new list
previousCharacters ??= new List<char>();
// add current character to list
previousCharacters.Add(newExtdChar.Character);
break;
default:
throw new Exception($"'{nameof(HandleNewCharacters)}()' failed because of error when checking if a newline should be introduced.");
}
} }
} }
[RelayCommand] [RelayCommand]
private void ClearReceivedData() private void ClearReceivedData()
{ {

@ -1,4 +1,5 @@
using CommunityToolkit.Mvvm.ComponentModel; using Common.AppSettings;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using MultiTerm.Core.Model; using MultiTerm.Core.Model;
using MultiTerm.Core.Types; using MultiTerm.Core.Types;
@ -30,6 +31,12 @@ public partial class SendReceiveViewModel : TerminalViewModel
[ObservableProperty] [ObservableProperty]
private string sentData = string.Empty; private string sentData = string.Empty;
/// <summary>
/// Constructor.
/// </summary>
/// <param name="appSettings"></param>
public SendReceiveViewModel(IAppSettingsProvider appSettings) : base(appSettings) { }
/// <summary> /// <summary>
/// Send command. /// Send command.
/// </summary> /// </summary>

@ -1,4 +1,6 @@
using CommunityToolkit.Mvvm.ComponentModel; using Common.AppSettings;
using Common.Helpers;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using MultiTerm.Core.Types; using MultiTerm.Core.Types;
using MultiTerm.Protocols; using MultiTerm.Protocols;
@ -8,15 +10,32 @@ namespace MultiTerm.Core.ViewModel;
public abstract partial class TerminalViewModel : ObservableObject, ITerminalViewModel public abstract partial class TerminalViewModel : ObservableObject, ITerminalViewModel
{ {
private const string defaultReceiveNewlineSeparatorAppSettingsKey = "DefaultReceiveNewlineSeparator";
private const string defaultSendNewlineSeparatorAppSettingsKey = "DefaultSendNewlineSeparator";
private readonly IAppSettingsProvider appSettings;
public abstract string Title { get; } public abstract string Title { get; }
public abstract TerminalViewType ViewType { get; } public abstract TerminalViewType ViewType { get; }
public ProtocolType ProtocolType { get; set; } public ProtocolType ProtocolType { get; set; }
public event EventHandler? ClosingEvent; /// <summary>
/// Holds communication data, meaning data that was sent to or received over the communication protocol.
/// </summary>
[ObservableProperty] [ObservableProperty]
private CommunicationDataViewModel communicationData = new(null); private CommunicationDataViewModel communicationData = new(null);
/// <summary>
/// Newline Separator Type that is selected for receival data of this Terminal. Defaults to none.
/// </summary>
[ObservableProperty]
private NewlineSeparatorType receiveNewlineSeparatorType = NewlineSeparatorType.None;
/// <summary>
/// Newline Separator Type that is selected for sent out data of this Terminal. Defaults to none.
/// </summary>
[ObservableProperty]
private NewlineSeparatorType sendNewlineSeparatorType = NewlineSeparatorType.None;
private ICommunicationProtocol? communicationProtocol; private ICommunicationProtocol? communicationProtocol;
public ICommunicationProtocol? CommunicationProtocol public ICommunicationProtocol? CommunicationProtocol
{ {
@ -26,6 +45,9 @@ public abstract partial class TerminalViewModel : ObservableObject, ITerminalVie
communicationProtocol = value; communicationProtocol = value;
// register communication protocol in the Communication Data View Model // register communication protocol in the Communication Data View Model
//this.CommunicationData = new CommunicationDataViewModel(communicationProtocol); // TEMP //this.CommunicationData = new CommunicationDataViewModel(communicationProtocol); // TEMP
// initialize both newline Separators. initializes with null if they are not available.
this.CommunicationData.ConfigureNewlineSeparators(this.ReceiveNewlineSeparatorType, this.SendNewlineSeparatorType);
} }
} }
@ -39,12 +61,39 @@ public abstract partial class TerminalViewModel : ObservableObject, ITerminalVie
// register event handler for connection request from viewmodel // register event handler for connection request from viewmodel
if(value != null) if(value != null)
{ {
protocolSettings!.ConnectRequested += OnViewModelRequestedConnect; ; ; protocolSettings!.ConnectRequested += OnViewModelRequestedConnect;
protocolSettings!.DisconnectRequested += OnViewModelRequestedDisconnect; protocolSettings!.DisconnectRequested += OnViewModelRequestedDisconnect;
} }
} }
} }
public TerminalViewModel(IAppSettingsProvider appSettings)
{
this.appSettings = appSettings;
this.InitializeNewlineSeparatorsFromAppSettings();
}
#region Connect/Disconnect
private void OnViewModelRequestedConnect(object? sender, ConnectionRequestEventArgs e)
{
// guard uninitialized CommunicationProtocol
if (CommunicationProtocol == null) { throw new Exception($"To call '{nameof(OnViewModelRequestedConnect)}()', CommunicationProtocol must not be null!"); }
e.Success = this.CommunicationProtocol.Connect(e.Settings);
}
private void OnViewModelRequestedDisconnect(object? sender, EventArgs e)
{
// guard uninitialized CommunicationProtocol
if (CommunicationProtocol == null) { throw new Exception($"To call '{nameof(OnViewModelRequestedConnect)}()', CommunicationProtocol must not be null!"); }
this.CommunicationProtocol.Disconnect();
}
#endregion
#region Closing handling
public event EventHandler? ClosingEvent;
/// <summary> /// <summary>
/// Method to override if any closing actions are required. /// Method to override if any closing actions are required.
/// Closing can be cancelled using the return value. /// Closing can be cancelled using the return value.
@ -60,20 +109,46 @@ public abstract partial class TerminalViewModel : ObservableObject, ITerminalVie
ClosingEvent?.Invoke(this, EventArgs.Empty); ClosingEvent?.Invoke(this, EventArgs.Empty);
} }
} }
#endregion
private void OnViewModelRequestedConnect(object? sender, ConnectionRequestEventArgs e) #region Newline Separator handling
/// <summary>
/// Gets the default settings for both newline separators from the AppSettings Provider.
/// Writes them to the local variables <see cref="ReceiveNewlineSeparatorType"/> and <see cref="SendNewlineSeparatorType"/>.
/// In case there are no default settings, it does not overwrite the local variables.
/// In case the Communication Data is null, nothing is done.
/// </summary>
private void InitializeNewlineSeparatorsFromAppSettings()
{ {
// guard uninitialized CommunicationProtocol // guard null objects
if (CommunicationProtocol == null) { throw new Exception($"To call '{nameof(OnViewModelRequestedConnect)}()', CommunicationProtocol must not be null!"); } if(this.CommunicationData == null) { return; }
e.Success = this.CommunicationProtocol.Connect(e.Settings); // get newline separators from persistent settings, if not available catch exceptions
this.appSettings.TryReadSetting(defaultReceiveNewlineSeparatorAppSettingsKey, out string settingValueReceiveNLSep);
try
{
this.ReceiveNewlineSeparatorType = EnumHelpers.ParseEnum<NewlineSeparatorType>(settingValueReceiveNLSep);
}
catch { }
this.appSettings.TryReadSetting(defaultSendNewlineSeparatorAppSettingsKey, out string settingValueSendNLSep);
try
{
this.SendNewlineSeparatorType = EnumHelpers.ParseEnum<NewlineSeparatorType>(settingValueSendNLSep);
}
catch { }
} }
private void OnViewModelRequestedDisconnect(object? sender, EventArgs e) partial void OnReceiveNewlineSeparatorTypeChanged(NewlineSeparatorType value)
{ {
// guard uninitialized CommunicationProtocol // triggers rearranging the received data with the new NewlineSeparatorType
if (CommunicationProtocol == null) { throw new Exception($"To call '{nameof(OnViewModelRequestedConnect)}()', CommunicationProtocol must not be null!"); } this.CommunicationData.ConfigureNewlineSeparators(value, null);
}
this.CommunicationProtocol.Disconnect(); partial void OnSendNewlineSeparatorTypeChanged(NewlineSeparatorType value)
{
// triggers rearranging the sent data with the new NewlineSeparatorType
this.CommunicationData.ConfigureNewlineSeparators(null, value);
} }
#endregion
} }

@ -2,16 +2,6 @@
public interface IProtocolSettings public interface IProtocolSettings
{ {
/// <summary>
/// Newline sequence to add on the end when sending a message.
/// </summary>
string NewlineSequenceOnSend { get; }
/// <summary>
/// When this sequence is detected while reading, a new line is introduced.
/// </summary>
string NewlineOnReceivedSequence { get; }
/// <summary> /// <summary>
/// Checks if the entered settings are valid and a connection can be made. /// Checks if the entered settings are valid and a connection can be made.
/// </summary> /// </summary>

@ -37,12 +37,6 @@ public partial class SerialProtocolSettingsViewModel : ProtocolSettingsViewModel
this.ComPorts = SerialProtocol.GetPortNames(); this.ComPorts = SerialProtocol.GetPortNames();
} }
// TODO
public string NewlineSequenceOnSend => throw new NotImplementedException();
public string NewlineOnReceivedSequence => throw new NotImplementedException();
public SerialProtocolSettingsViewModel(IMessenger messenger) : base(messenger) public SerialProtocolSettingsViewModel(IMessenger messenger) : base(messenger)
{ {
// load com ports initially // load com ports initially

@ -14,8 +14,8 @@ public class MultiFormatDataView : Control
private static readonly Dictionary<StackPanel, MultiFormatDataView> itemParentPairs = new(); private static readonly Dictionary<StackPanel, MultiFormatDataView> itemParentPairs = new();
private const string itemsControlTemplateName = "itemsControl"; private const string itemsControlTemplateName = "itemsControl";
private const string buttonClearTemplateName = "btnClear"; private const string buttonClearTemplateName = "btnClear";
private const string selectorTemplateName = "comboBoxSelector";
private ListBox? itemsControl; private ListBox? itemsControl;
private List<DataViewModel> currentSelectedItems = new();
#region Dependency Properties #region Dependency Properties
public static readonly DependencyProperty DataSourceProperty = public static readonly DependencyProperty DataSourceProperty =
@ -31,6 +31,21 @@ public class MultiFormatDataView : Control
BindsTwoWayByDefault = false BindsTwoWayByDefault = false
}); });
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 = public static readonly DependencyProperty RealizedItemsCountProperty =
DependencyProperty.Register("RealizedItemsCount", DependencyProperty.Register("RealizedItemsCount",
typeof(uint), typeof(MultiFormatDataView), typeof(uint), typeof(MultiFormatDataView),
@ -70,6 +85,36 @@ public class MultiFormatDataView : Control
set { SetValue(SelectedItemsProperty, value); } set { SetValue(SelectedItemsProperty, 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> /// <summary>
/// .NET Property for RealizedItemsCount. /// .NET Property for RealizedItemsCount.
/// </summary> /// </summary>
@ -169,9 +214,6 @@ public class MultiFormatDataView : Control
#region Selected Items handling #region Selected Items handling
private void ItemsControl_SelectionChanged(object sender, SelectionChangedEventArgs e) private void ItemsControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{ {
if(this.currentSelectedItems == null)
{ this.currentSelectedItems = new List<DataViewModel>(); }
// if there are no changes => return // if there are no changes => return
if (e.AddedItems.Count <= 0 && e.RemovedItems.Count <= 0) { return; } if (e.AddedItems.Count <= 0 && e.RemovedItems.Count <= 0) { return; }
@ -192,6 +234,29 @@ public class MultiFormatDataView : Control
} }
#endregion #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 #region Realized Item Count
private static void OnRealizedItemsCountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) private static void OnRealizedItemsCountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{ {

@ -115,42 +115,82 @@
<ControlTemplate TargetType="{x:Type local:MultiFormatDataView}"> <ControlTemplate TargetType="{x:Type local:MultiFormatDataView}">
<DockPanel> <DockPanel>
<GroupBox Header="Display Formats" DockPanel.Dock="Top"> <GroupBox Header="Display Formats" DockPanel.Dock="Top">
<DockPanel LastChildFill="False"> <!-- Top row with options and buttons -->
<DockPanel LastChildFill="True">
<!-- Format selectors -->
<CheckBox DockPanel.Dock="Left" Content="Character" VerticalAlignment="Center" x:Name="cbCharacter" Padding="20 0" IsChecked="True"/> <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="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="Binary" VerticalAlignment="Center" x:Name="cbBin" Padding="20 0"/>
<Button x:Name="btnClear" Content="Clear" Margin="5" /> <!-- Clear Button -->
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal" VerticalAlignment="Center"> <Button DockPanel.Dock="Right" x:Name="btnClear" Content="Clear" Margin="2" />
<Label Content="Realized Items: " Margin="0 0 5 0"/> <!-- ComboBox -->
<Label Width="30" Content="{TemplateBinding RealizedItemsCount}"/> <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> </StackPanel>
</DockPanel> </DockPanel>
</GroupBox> </GroupBox>
<ListView Name="itemsControl" <!-- Grid that contains main control and a realited items count on top -->
ItemsSource="{TemplateBinding DataSource}" <Grid>
ScrollViewer.VerticalScrollBarVisibility="Auto" <Grid.RowDefinitions>
ScrollViewer.CanContentScroll="true" <RowDefinition Height="40"/>
ScrollViewer.PanningMode="VerticalOnly" <RowDefinition Height="*"/>
HorizontalAlignment="Left" </Grid.RowDefinitions>
VirtualizingPanel.IsVirtualizingWhenGrouping="True" <Grid.ColumnDefinitions>
VirtualizingPanel.CacheLengthUnit="Item" <ColumnDefinition Width="*"/>
ItemTemplate="{StaticResource dataContainerTemplate}" </Grid.ColumnDefinitions>
SelectionMode="Extended">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemsPanel> <!-- Main Items Control -->
<ItemsPanelTemplate> <ListView Name="itemsControl"
<wpftk:VirtualizingWrapPanel Orientation="Vertical" /> Grid.Column="0" Grid.Row="0" Grid.RowSpan="2"
</ItemsPanelTemplate> ItemsSource="{TemplateBinding DataSource}"
</ListView.ItemsPanel> ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.CanContentScroll="true"
ScrollViewer.PanningMode="VerticalOnly"
HorizontalAlignment="Left"
VirtualizingPanel.IsVirtualizingWhenGrouping="True"
VirtualizingPanel.CacheLengthUnit="Item"
ItemTemplate="{StaticResource dataContainerTemplate}"
SelectionMode="Extended">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<wpftk:VirtualizingWrapPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.GroupStyle>
<GroupStyle HidesIfEmpty="True">
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Separator></Separator>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
<!-- 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}"/>
</StackPanel>
</ListView> </Grid>
</DockPanel> </DockPanel>
</ControlTemplate> </ControlTemplate>
</Setter.Value> </Setter.Value>

@ -3,13 +3,30 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:MultiTerm.Wpf.View" 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_serial="clr-namespace:MultiTerm.Protocols.Serial;assembly=MultiTerm.Protocols"
xmlns:types="clr-namespace:MultiTerm.Core.Types;assembly=MultiTerm.Core"
xmlns:settings_view="clr-namespace:MultiTerm.Wpf.View.SettingsView" xmlns:settings_view="clr-namespace:MultiTerm.Wpf.View.SettingsView"
xmlns:custom_controls="clr-namespace:MultiTerm.Wpf.CustomControl;assembly=MultiTerm.Wpf.CustomControl" xmlns:custom_controls="clr-namespace:MultiTerm.Wpf.CustomControl;assembly=MultiTerm.Wpf.CustomControl"
xmlns:behaviors="http://schemas.microsoft.com/xaml/behaviors" xmlns:behaviors="http://schemas.microsoft.com/xaml/behaviors"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"> d:DesignHeight="450" d:DesignWidth="800">
<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>
</UserControl.Resources>
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
@ -34,7 +51,12 @@
<GroupBox Header="Receive" Grid.Row="1" Padding="5"> <GroupBox Header="Receive" Grid.Row="1" Padding="5">
<custom_controls:MultiFormatDataView x:Name="mfdvReceive" <custom_controls:MultiFormatDataView x:Name="mfdvReceive"
DataSource="{Binding Path=CommunicationData.ReceivedData, Mode=OneWay}" DataSource="{Binding Path=CommunicationData.ReceivedData, Mode=OneWay}"
SelectedItems="{Binding Path=CommunicationData.SelectedReceivedData, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"> SelectedItems="{Binding Path=CommunicationData.SelectedReceivedData, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"
SelectorItemsSource="{Binding Source={StaticResource NewlineSeparatorTypeValues}, Converter={StaticResource EnumValToDescConverter}}"
SelectorSelectedItem="{Binding Path=ReceiveNewlineSeparatorType, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged,
Converter={StaticResource EnumValToDescConverter}}"
SelectorDescription="Newline Separator">
<behaviors:Interaction.Triggers> <behaviors:Interaction.Triggers>
<behaviors:EventTrigger EventName="ClearRequested" SourceObject="{Binding ElementName=mfdvReceive}"> <behaviors:EventTrigger EventName="ClearRequested" SourceObject="{Binding ElementName=mfdvReceive}">
@ -48,7 +70,6 @@
<DockPanel> <DockPanel>
<!-- Send text box --> <!-- Send text box -->
<DockPanel DockPanel.Dock="Top" LastChildFill="True"> <DockPanel DockPanel.Dock="Top" LastChildFill="True">
<!--<TextBox Width="700"></TextBox>-->
<Button DockPanel.Dock="Right" Content="Send" Command="{Binding SendCommand}"/> <Button DockPanel.Dock="Right" Content="Send" Command="{Binding SendCommand}"/>
<custom_controls:MultiFormatTextBox DockPanel.Dock="Left" CurrentMultiFormatString="{Binding Path=SendData, Mode=TwoWay}"></custom_controls:MultiFormatTextBox> <custom_controls:MultiFormatTextBox DockPanel.Dock="Left" CurrentMultiFormatString="{Binding Path=SendData, Mode=TwoWay}"></custom_controls:MultiFormatTextBox>
</DockPanel> </DockPanel>

Loading…
Cancel
Save