Skip to content
Snippets Groups Projects
Commit 401ba858 authored by Frederic A's avatar Frederic A
Browse files

init

parent ac9c8748
No related branches found
No related tags found
No related merge requests found
Showing
with 1411 additions and 0 deletions
# Entfernen Sie die folgende Zeile, wenn Sie EDITORCONFIG-Einstellungen von höheren Verzeichnissen vererben möchten.
root = true
# C#-Dateien
[*.cs]
#### Wichtige EditorConfig-Optionen ####
# Einzüge und Abstände
indent_size = 4
indent_style = space
tab_width = 4
# Einstellungen für neue Zeilen
end_of_line = crlf
insert_final_newline = false
#### .NET-Codierungskonventionen ####
# Using-Direktiven organisieren
dotnet_separate_import_directive_groups = true
dotnet_sort_system_directives_first = true
file_header_template = unset
# this.- und Me.-Einstellungen
dotnet_style_qualification_for_event = false:silent
dotnet_style_qualification_for_field = false:silent
dotnet_style_qualification_for_method = false:silent
dotnet_style_qualification_for_property = false:silent
# Einstellungen für Sprachschlüsselwörter und BCL-Typen
dotnet_style_predefined_type_for_locals_parameters_members = true:silent
dotnet_style_predefined_type_for_member_access = false:silent
# Einstellungen für Klammern
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
# Einstellungen für Modifizierer
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
# Einstellungen für Ausdrucksebene
dotnet_style_coalesce_expression = true:warning
dotnet_style_collection_initializer = true:suggestion
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_null_propagation = true:warning
dotnet_style_object_initializer = true:suggestion
dotnet_style_operator_placement_when_wrapping = beginning_of_line
dotnet_style_prefer_auto_properties = true:silent
dotnet_style_prefer_compound_assignment = true:suggestion
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
dotnet_style_prefer_conditional_expression_over_return = true:silent
dotnet_style_prefer_inferred_anonymous_type_member_names = false:suggestion
dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
dotnet_style_prefer_simplified_interpolation = true:suggestion
# Einstellungen für Felder
dotnet_style_readonly_field = true:suggestion
# Einstellungen für Parameter
dotnet_code_quality_unused_parameters = all:suggestion
# Unterdrückungseinstellungen
dotnet_remove_unnecessary_suppression_exclusions = none
#### C#-Codierungskonventionen ####
# Var-Einstellungen
csharp_style_var_elsewhere = true:silent
csharp_style_var_for_built_in_types = true:silent
csharp_style_var_when_type_is_apparent = true:silent
# Ausdruckskörpermember
csharp_style_expression_bodied_accessors = true:silent
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_indexers = true:silent
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_expression_bodied_local_functions = true:silent
csharp_style_expression_bodied_methods = true:silent
csharp_style_expression_bodied_operators = true:silent
csharp_style_expression_bodied_properties = true:silent
# Einstellungen für den Musterabgleich
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_prefer_not_pattern = true:suggestion
csharp_style_prefer_pattern_matching = true:silent
csharp_style_prefer_switch_expression = false:suggestion
# Einstellungen für NULL-Überprüfung
csharp_style_conditional_delegate_call = true:warning
# Einstellungen für Modifizierer
csharp_prefer_static_local_function = true:suggestion
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent
# Einstellungen für Codeblöcke
csharp_prefer_braces = when_multiline:silent
csharp_prefer_simple_using_statement = false:suggestion
# Einstellungen für Ausdrucksebene
csharp_prefer_simple_default_expression = true:suggestion
csharp_style_deconstructed_variable_declaration = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
csharp_style_pattern_local_over_anonymous_function = true:suggestion
csharp_style_prefer_index_operator = true:suggestion
csharp_style_prefer_range_operator = false:suggestion
csharp_style_throw_expression = true:suggestion
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
# Einstellungen für using-Anweisungen
csharp_using_directive_placement = outside_namespace:silent
#### C#-Formatierungsregeln ####
# Einstellungen für neue Zeilen
csharp_new_line_before_catch = true
csharp_new_line_before_else = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_open_brace = all
csharp_new_line_between_query_expression_clauses = true
# Einstellungen für Einrückung
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = false
csharp_indent_labels = one_less_than_current
csharp_indent_switch_labels = true
# Einstellungen für Abstände
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
csharp_space_before_open_square_brackets = false
csharp_space_before_semicolon_in_for_statement = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
# Umbrucheinstellungen
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = false
#### Benennungsstile ####
# Benennungsregeln
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.enum_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.enum_should_be_pascal_case.symbols = enum
dotnet_naming_rule.enum_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.class_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.class_should_be_pascal_case.symbols = class
dotnet_naming_rule.class_should_be_pascal_case.style = pascal_case
# Symbolspezifikationen
dotnet_naming_symbols.class.applicable_kinds = class
dotnet_naming_symbols.class.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.class.required_modifiers =
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.enum.applicable_kinds = enum
dotnet_naming_symbols.enum.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.enum.required_modifiers =
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =
# Benennungsstile
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
</startup>
</configuration>
\ No newline at end of file
<Application x:Class="Cocktailmaker.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Cocktailmaker"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace Cocktailmaker
{
/// <summary>
/// Interaktionslogik für "App.xaml"
/// </summary>
public partial class App : Application
{
}
}

using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace Cocktailmaker
{
internal class Cocktail : INotifyPropertyChanged
{
private int number;
public int Number
{
get => number;
set
{
number = value;
RaisePropertyChange();
}
}
private string text;
public string Text
{
get => text;
set
{
text = value;
RaisePropertyChange();
}
}
internal void RaisePropertyChange([CallerMemberName] string property = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
public event PropertyChangedEventHandler PropertyChanged;
}
}
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{FA188F5C-B51D-428A-804C-B6C28FA93718}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>Cocktailmaker</RootNamespace>
<AssemblyName>Cocktailmaker</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Compile Include="Cocktail.cs" />
<Compile Include="SerialComWrapper.cs" />
<Compile Include="UI\Helper\UIErrorManager.cs" />
<Compile Include="UI\ViewModels\MainWindowViewModel.cs" />
<Compile Include="UI\Helper\ViewModel.cs" />
<Page Include="MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="MainWindow.xaml.cs">
<DependentUpon>MainWindow.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
\ No newline at end of file

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31702.278
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cocktailmaker", "Cocktailmaker.csproj", "{FA188F5C-B51D-428A-804C-B6C28FA93718}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{BA50ACA6-8033-4412-9BE0-751F081DDE73}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{FA188F5C-B51D-428A-804C-B6C28FA93718}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FA188F5C-B51D-428A-804C-B6C28FA93718}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FA188F5C-B51D-428A-804C-B6C28FA93718}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FA188F5C-B51D-428A-804C-B6C28FA93718}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {CA0CC057-5797-459E-B253-B9091C234449}
EndGlobalSection
EndGlobal
<Window x:Class="Cocktailmaker.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Cocktailmaker"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition Height="20"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button x:Name="But_StatTask" Grid.Row="0" Grid.ColumnSpan="2" Content="Start Task" Click="But_StatTask_Click"/>
<TextBox x:Name="Tb_Result" Grid.Row="1" Grid.Column="0" Text="{Binding Result}" />
<DataGrid x:Name="dataGrid" Grid.Row="1" Grid.Column="1" ItemsSource="{Binding Liste, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="True" CanUserReorderColumns="True" CanUserResizeColumns="True" CanUserResizeRows="True" CanUserSortColumns="True" RowBackground="White" AlternatingRowBackground="GhostWhite">
<DataGrid.Columns>
<DataGridTextColumn Header="Number" Binding="{Binding Number}" Width="SizeToHeader" IsReadOnly="True"/>
<DataGridTextColumn Header="Text" Binding="{Binding Text}" Width="*" IsReadOnly="True"/>
</DataGrid.Columns>
</DataGrid>
<ProgressBar x:Name="progressDecoding" Grid.Row="2" Grid.ColumnSpan="2" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Minimum="{Binding ProgressMin, Mode=OneWay, UpdateSourceTrigger=PropertyChanged }" Maximum="{Binding ProgressMax, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" Value="{Binding ProgressValue, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</Window>
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using Cocktailmaker.UI.ViewModels;
namespace Cocktailmaker
{
/// <summary>
/// Interaktionslogik für MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private readonly MainWindowViewModel vm = new MainWindowViewModel();
public MainWindow()
{
InitializeComponent();
Tb_Result.DataContext = vm;
dataGrid.DataContext = vm;
progressDecoding.DataContext = vm;
}
private async void But_StatTask_Click(object sender, RoutedEventArgs e)
{
var steps = 30;
vm.Result = "start";
vm.ProgressValue = 0;
vm.ProgressMax = 100;
vm.ProgressMin = 0;
var progress = new Action<float>((p) =>
{
Dispatcher.Invoke(() =>
{
vm.ProgressValue = (int)p;
});
});
//var longtask = await LongRunnerBool(steps, progress);
//vm.Result = longtask ? "done" : "failed";
var progressListe = new Action<float, Cocktail>((p, r) =>
{
Dispatcher.Invoke(() =>
{
vm.ProgressValue = (int)p;
vm.Liste.Add(r);
});
});
vm.Liste.Clear();
var longtaskListe = await LongRunnerListe(steps, progressListe);
foreach (var r in longtaskListe)
{
vm.Liste.Add(r);
}
}
private Task<bool> LongRunnerBool(int steps, Action<float> progress) => Task.Run(() => LongRunnerBoolFunc(steps, progress));
private bool LongRunnerBoolFunc(int steps, Action<float> progress)
{
for (var i = 1; i <= steps; i++)
{
Thread.Sleep(1 * 1000);
progress(100f / (float)steps * (float)i);
}
return true;
}
private Task<List<Cocktail>> LongRunnerListe(int steps, Action<float, Cocktail> progress) => Task.Run(() => LongRunnerListeFunc(steps, progress));
private List<Cocktail> LongRunnerListeFunc(int steps, Action<float, Cocktail> progress)
{
var liste = new List<Cocktail>();
for (var i = 1; i <= steps; i++)
{
var r = new Cocktail() { Number = i, Text = $"Cocktail {i} loaded" };
liste.Add(r);
Thread.Sleep(1 * 1);
progress(100f / (float)steps * (float)i, r);
}
return liste;
}
}
}
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
// Allgemeine Informationen über eine Assembly werden über die folgenden
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
// die einer Assembly zugeordnet sind.
[assembly: AssemblyTitle("Cocktailmaker")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("HP Inc.")]
[assembly: AssemblyProduct("Cocktailmaker")]
[assembly: AssemblyCopyright("Copyright © HP Inc. 2021")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly
// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von
// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen.
[assembly: ComVisible(false)]
//Um mit dem Erstellen lokalisierbarer Anwendungen zu beginnen, legen Sie
//<UICulture>ImCodeVerwendeteKultur</UICulture> in der .csproj-Datei
//in einer <PropertyGroup> fest. Wenn Sie in den Quelldateien beispielsweise Deutsch
//(Deutschland) verwenden, legen Sie <UICulture> auf \"de-DE\" fest. Heben Sie dann die Auskommentierung
//des nachstehenden NeutralResourceLanguage-Attributs auf. Aktualisieren Sie "en-US" in der nachstehenden Zeile,
//sodass es mit der UICulture-Einstellung in der Projektdatei übereinstimmt.
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //Speicherort der designspezifischen Ressourcenwörterbücher
//(wird verwendet, wenn eine Ressource auf der Seite nicht gefunden wird,
// oder in den Anwendungsressourcen-Wörterbüchern nicht gefunden werden kann.)
ResourceDictionaryLocation.SourceAssembly //Speicherort des generischen Ressourcenwörterbuchs
//(wird verwendet, wenn eine Ressource auf der Seite nicht gefunden wird,
// designspezifischen Ressourcenwörterbuch nicht gefunden werden kann.)
)]
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
//
// Hauptversion
// Nebenversion
// Buildnummer
// Revision
//
// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,
// indem Sie "*" wie unten gezeigt eingeben:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
//------------------------------------------------------------------------------
// <auto-generated>
// Dieser Code wurde von einem Tool generiert.
// Laufzeitversion: 4.0.30319.42000
//
// Änderungen an dieser Datei können fehlerhaftes Verhalten verursachen und gehen verloren, wenn
// der Code erneut generiert wird.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Cocktailmaker.Properties
{
/// <summary>
/// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
/// </summary>
// Diese Klasse wurde von der StronglyTypedResourceBuilder-Klasse
// über ein Tool wie ResGen oder Visual Studio automatisch generiert.
// Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
// mit der Option /str erneut aus, oder erstellen Sie Ihr VS-Projekt neu.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources
{
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources()
{
}
/// <summary>
/// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager
{
get
{
if ((resourceMan == null))
{
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Cocktailmaker.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
/// Ressourcenlookups, die diese stark typisierte Ressourcenklasse verwenden.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture
{
get
{
return resourceCulture;
}
set
{
resourceCulture = value;
}
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
\ No newline at end of file
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Cocktailmaker.Properties
{
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
{
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default
{
get
{
return defaultInstance;
}
}
}
}
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>
\ No newline at end of file
using System;
using System.IO.Ports;
namespace Cocktailmaker
{ /// <summary>
/// Event is called when the serial com receives data.
/// </summary>
/// <param name="sender"></param>
/// <param name="e">argument containting the data</param>
public delegate void SerialComWrapperOnReceiveEventHandler(object sender, SerialComWrapperOnReceiveEventArgs e);
/// <summary>
/// Event argument for SerialComOnReceiveEventHandler containing the data.
/// </summary>
public class SerialComWrapperOnReceiveEventArgs : EventArgs
{
/// <summary>
/// Data that is received.
/// </summary>
public byte[] Data { get; set; }
}
/// <summary>
/// Connection status of the serial com.
/// </summary>
public enum SerialComWrapperConnectionStatus
{
/// <summary>
/// Serial com is connected.
/// </summary>
Connected,
/// <summary>
/// Serial com is diconnected.
/// </summary>
Disconnected,
/// <summary>
/// Serial com connection status is unknown.
/// </summary>
Unknown,
/// <summary>
/// Serial com connection status has an error.
/// </summary>
Error
}
/// <summary>
/// Event is called when the connection status of the serial com changes.
/// </summary>
/// <param name="sender"></param>
/// <param name="e">argument containing the new connection status</param>
public delegate void SerialComWrapperOnConnectionStatusChangedEventHandler(object sender, SerialComWrapperConnectionStatusChangedEventArgs e);
/// <summary>
/// Event argument for SerialComOnConnectionStatusChangedEventHandler containing the new status.
/// </summary>
public class SerialComWrapperConnectionStatusChangedEventArgs : EventArgs
{
/// <summary>
/// New status of the serial com connection.
/// </summary>
public SerialComWrapperConnectionStatus State { get; set; }
/// <summary>
/// Optional text showing more information.
/// </summary>
public string Text { get; set; }
}
internal class SerialComWrapper
{
/// <summary>
/// Event is called when the serial com receives data.
/// </summary>
private event SerialComWrapperOnReceiveEventHandler OnReceive;
/// <summary>
/// Event is called when the connection status of the physical layer changes.
/// </summary>
private event SerialComWrapperOnConnectionStatusChangedEventHandler OnConnectionStatusChanged;
/// <summary>
/// COM Port of the serial communication.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown if the caller tries to set the property and the wrapped SerialPort instance is already connected</exception>
public string ComPort
{
get => comPort;
set
{
if (comPort == value)
return;
if (IsConnected)
throw new InvalidOperationException($"Serial port already connected to {serialPort?.PortName}");
comPort = value;
ReconfigureSerialPort();
}
}
/// <summary>
/// Baudrate, i.e. number of transmitted symbols per second. Default: 115200.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown if the caller tries to set the property and the wrapped SerialPort instance is already connected</exception>
public int BaudRate
{
get => baudRate;
set
{
if (baudRate == value)
return;
if (IsConnected)
throw new InvalidOperationException("Can\'t change baud rate, serial port is open");
baudRate = value;
ReconfigureSerialPort();
}
}
/// <summary>
/// Parity of the serial communication. Default: None.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown if the caller tries to set the property and the wrapped SerialPort instance is already connected</exception>
public Parity Parity
{
get => parity;
set
{
if (parity == value)
return;
if (IsConnected)
throw new InvalidOperationException("Can\'t change parity, serial port is open");
parity = value;
ReconfigureSerialPort();
}
}
/// <summary>
/// Number of stop bits for the serial port. Default: One.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown if the caller tries to set the property and the wrapped SerialPort instance is already connected</exception>
public StopBits StopBits
{
get => stopBits;
set
{
if (stopBits == value)
return;
if (IsConnected)
throw new InvalidOperationException("Can\'t change stop bits, serial port is open");
stopBits = value;
ReconfigureSerialPort();
}
}
/// <summary>
/// Indicates whether the physical layer is connected or not.
/// </summary>
public bool IsConnected => serialPort?.IsOpen ?? false;
private SerialPort serialPort;
private SerialComWrapperConnectionStatus curConStatus = SerialComWrapperConnectionStatus.Unknown;
private readonly System.Timers.Timer conTimer = new System.Timers.Timer(100);
private string comPort;
private int baudRate;
private Parity parity;
private StopBits stopBits;
/// <summary>
/// Initialize a new instance of the SerialCom class.
/// </summary>
/// <param name="comPort">Name of the serial port to use</param>
/// <param name="baudRate">Baudrate to use</param>
/// <param name="parity">Parity to use</param>
/// <param name="stopBits">Stop bits to use</param>
public SerialComWrapper(string comPort, int baudRate = 115200, Parity parity = Parity.None, StopBits stopBits = StopBits.One)
{
ComPort = comPort;
BaudRate = baudRate;
Parity = parity;
StopBits = stopBits;
InitializeSerialPort();
}
private void NotifyConnectionStateChanged(SerialComWrapperConnectionStatus newConStatus, string errorText = "")
{
if (curConStatus == newConStatus)
return;
conTimer?.Start(); //Ein Aufrufder- Start Methode, wenn der Timer aktiviert ist, hat keine Auswirkungen.
var args = new SerialComWrapperConnectionStatusChangedEventArgs() { State = newConStatus, Text = errorText };
curConStatus = newConStatus;
OnConnectionStatusChanged?.Invoke(this, args);
}
/// <summary>
/// Opens the serial port. If opened, it should be closed when the application closes!
/// </summary>
/// <param name="comPort">ComPort of the serial port. If comPort=="", SerialCom.ComPort variable will be used</param>
public void Open(string comPort = "")
{
try
{
ComPort = comPort == "" ? ComPort : comPort;
if (serialPort == null)
InitializeSerialPort(); // Re-Initialise the port.
if (!serialPort.IsOpen)
{
serialPort.PortName = ComPort;
serialPort?.Open();
serialPort?.DiscardInBuffer();
serialPort?.DiscardOutBuffer();
Console.WriteLine("Open COM Port " + ComPort);
NotifyConnectionStateChanged(SerialComWrapperConnectionStatus.Connected);
}
else
{
Console.WriteLine($"COM Port {ComPort} is already opened");
}
}
catch (Exception e)
{
Error($"Failed to open the COM port (name: {ComPort}). Msg = {e.Message}", e);
}
}
/// <summary>
/// Closes the serial port.
/// </summary>
public void Close()
{
try
{
if (serialPort?.IsOpen ?? false)
{
serialPort?.DiscardInBuffer();
serialPort?.DiscardOutBuffer();
serialPort?.Close();
Console.WriteLine("Close COM Port " + ComPort);
NotifyConnectionStateChanged(SerialComWrapperConnectionStatus.Disconnected);
}
else
{
Console.WriteLine($"Close Port: { ComPort } is already closed");
}
DeinitSerialPort();
}
catch (Exception e)
{
Error("Failed to close the COM port. Msg = " + e.Message, e);
}
}
private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
var bytesCount = serialPort?.BytesToRead ?? 0;
var readBytes = new byte[bytesCount];
if (serialPort?.Read(readBytes, 0, bytesCount) != bytesCount)
{
// Error while reading the Serial Port
Error("Could not read all bytes that are in the buffer");
}
// Console.WriteLine(tag, $"RAW-RECV: {BitConverter.ToString(readBytes)}");
var args = new SerialComWrapperOnReceiveEventArgs() { Data = readBytes };
OnReceive?.Invoke(this, args);
}
/// <summary>
/// Transmits data on the serial port.
/// </summary>
/// <param name="data">data to be sent</param>
public void Send(byte[] data)
{
if (serialPort == null || !IsConnected)
{
Console.WriteLine("COM port not open, no data sent");
return;
}
//Console.WriteLine($"RAW-SEND: {BitConverter.ToString(data)}");
try
{
serialPort?.Write(data, 0, data.Length);
}
catch (Exception e)
{
Error("Send failed. Msg = " + e.ToString(), e);
}
}
/// <summary>
/// Error handling:
/// - log <paramref name="text"/> and optional <paramref name="e"/>
/// - raise OnConnectionStatusChanged event and
/// - close the serial port
/// </summary>
/// <param name="text">Error message</param>
/// <param name="e">Optional exception</param>
private void Error(string text, Exception e = null)
{
if (e == null)
Console.WriteLine(text);
else
Console.WriteLine(text + e.Message);
NotifyConnectionStateChanged(SerialComWrapperConnectionStatus.Error, text);
// Close the Port since we had an Error!
Close();
}
/// <summary>
/// First initialization of the wrapped SerialPort instance.
/// </summary>
private void InitializeSerialPort()
{
serialPort = new SerialPort()
{
PortName = ComPort,
BaudRate = BaudRate,
Parity = Parity,
DataBits = 8,
StopBits = StopBits,
ReadTimeout = 5000,
WriteTimeout = 5000,
RtsEnable = false,
DtrEnable = false
};
serialPort.DataReceived += SerialPort_DataReceived;
// Periodic update of the connection status
conTimer.Elapsed += ConTimer_Elapsed;
}
private void ConTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
var newConStatus = IsConnected ? SerialComWrapperConnectionStatus.Connected : SerialComWrapperConnectionStatus.Disconnected;
// Raise OnConnectionStatusChanged event if we have a new status
if (newConStatus != curConStatus)
{
Console.WriteLine($"connection status has changed to {newConStatus}. Old Status: {curConStatus}");
Console.WriteLine($"IsConnected: {IsConnected}, serialPort == null : {serialPort == null}");
NotifyConnectionStateChanged(newConStatus);
}
}
private void DeinitSerialPort()
{
conTimer.Elapsed -= ConTimer_Elapsed;
conTimer.Stop();
serialPort?.Dispose();
serialPort = null;
}
/// <summary>
/// Reconfigure the wrapped SerialPort instance.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown if the wrapped SerialPort instance is connected</exception>
private void ReconfigureSerialPort()
{
// This happens while the constructor initializes the properties with the default values
if (serialPort == null)
return;
if (IsConnected)
throw new InvalidOperationException("Serial port is open, reconfiguration not possible");
// The timer and the assigned callback shouldn't cause problems but clean them up anyway
conTimer.Stop();
serialPort.DataReceived -= SerialPort_DataReceived;
// Create new SerialPort instance using the current configuration
serialPort = new SerialPort()
{
PortName = ComPort,
BaudRate = BaudRate,
Parity = Parity,
DataBits = 8,
StopBits = StopBits,
ReadTimeout = 5000,
WriteTimeout = 5000,
RtsEnable = false,
DtrEnable = false
};
serialPort.DataReceived += SerialPort_DataReceived;
conTimer.Start();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
namespace Cocktailmaker.UI.Helper
{
public class UIErrorManager
{
public class UIPropertyError
{
public string Name { get; set; }
public bool HasError { get; set; } = false;
public string ErrorInfoText { get; set; } = "";
}
public event PropertyErrorHandler OnPropertyErrorHandler;
private readonly List<UIPropertyError> propertyErrors = new List<UIPropertyError>();
public void Register(string propertyName)
{
if (IsRegistered(propertyName))
throw new SystemException($"Property {propertyName} is already registered.");
propertyErrors.Add(new UIPropertyError() { Name = propertyName });
}
public bool IsRegistered(string propertyName) => propertyErrors.Where(x => x.Name == propertyName).Any();
public void SetError(string propertyName, string errInfoText)
{
if (!IsRegistered(propertyName))
throw new SystemException($"Property {propertyName} is not registered.");
var pe = propertyErrors.Where(x => x.Name == propertyName).ToList()[0];
pe.ErrorInfoText = errInfoText;
pe.HasError = true;
OnPropertyErrorHandler?.Invoke(this, new PropertyErrorEventArgs() { Name = pe.Name });
}
public bool HasError(string propertyName)
{
if (!IsRegistered(propertyName))
throw new SystemException($"Property {propertyName} is not registered.");
var pe = propertyErrors.Where(x => x.Name == propertyName).ToList()[0];
return pe.HasError;
}
public string GetErrorInfoText(string propertyName)
{
if (!IsRegistered(propertyName))
throw new SystemException($"Property {propertyName} is not registered.");
var pe = propertyErrors.Where(x => x.Name == propertyName).ToList()[0];
// We reset the error flag since the error info text has been retrieved!
pe.HasError = false;
return pe.ErrorInfoText;
}
}
public delegate void PropertyErrorHandler(object sender, PropertyErrorEventArgs args);
public class PropertyErrorEventArgs : EventArgs
{
public string Name { get; set; }
}
}
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace Cocktailmaker.UI.Helper
{
public abstract class ViewModel : INotifyPropertyChanged, IDataErrorInfo
{
internal void RaisePropertyChange([CallerMemberName] string property = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
public event PropertyChangedEventHandler PropertyChanged;
#region UI Error Interface
public UIErrorManager ErrorManager = new UIErrorManager();
public string Error => "";
public string this[string propertyName]
{
get
{
var retStr = "";
// Check if property is registered.
if (ErrorManager.IsRegistered(propertyName) && ErrorManager.HasError(propertyName))
return ErrorManager.GetErrorInfoText(propertyName);
return retStr;
}
}
#endregion
}
}

using System.Collections.ObjectModel;
using Cocktailmaker.UI.Helper;
namespace Cocktailmaker.UI.ViewModels
{
internal class MainWindowViewModel : ViewModel
{
public MainWindowViewModel()
{
ErrorManager.Register(nameof(Result));
ErrorManager.Register(nameof(ProgressMin));
ErrorManager.Register(nameof(ProgressValue));
ErrorManager.Register(nameof(ProgressMax));
// Every time the property manager sets an error, we raise the "PropertyChange" event so that the UI can display the error!
ErrorManager.OnPropertyErrorHandler += (s, args) => RaisePropertyChange(args.Name);
}
private string result;
public string Result
{
get => result;
set
{
result = value;
RaisePropertyChange();
}
}
private int progressMin = 0;
public int ProgressMin
{
get => progressMin;
set
{
progressMin = value;
RaisePropertyChange();
}
}
private int progressValue = 0;
public int ProgressValue
{
get => progressValue;
set
{
progressValue = value;
RaisePropertyChange();
}
}
private int progressMax = 100;
public int ProgressMax
{
get => progressMax;
set
{
progressMax = value;
RaisePropertyChange();
}
}
public ObservableCollection<Cocktail> Liste { get; set; } = new ObservableCollection<Cocktail>();
}
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment