MVVM Implementing started

This commit is contained in:
2023-03-25 12:34:24 +01:00
parent 47535950b2
commit b1ef5cffb5
36 changed files with 844 additions and 5 deletions

View File

@@ -2,7 +2,7 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SewerStammGen"
StartupUri="MainWindow.xaml">
>
<Application.Resources>
</Application.Resources>

View File

@@ -1,4 +1,8 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using SewerStammGen.HostBuilders;
using SewerStammGen.ViewModel;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
@@ -13,5 +17,52 @@ namespace SewerStammGen
/// </summary>
public partial class App : Application
{
private readonly IHost _host;
public App()
{
_host = CreateHostBuilder().Build();
}
static IHostBuilder CreateHostBuilder(string[]? args = null)
{
return Host.CreateDefaultBuilder(args)
.AddConfiguration()
.AddServices()
.AddViewModels()
.AddStores()
.AddDBContext();
}
protected override void OnStartup(StartupEventArgs e)
{
Application.Current.DispatcherUnhandledException += Current_DispatcherUnhandledException;
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
_host.Start();
MainWindow? window = new MainWindow() { DataContext = _host.Services.GetRequiredService<MainWindowViewModel>() };
window.Show();
base.OnStartup(e);
}
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
try
{
Exception ex = (Exception)e.ExceptionObject;
string text = "An application error occured. Plrease contact the Administrator with the following information:\n\n";
MessageBox.Show(text + " " + ex.Message + "\n\n" + ex.StackTrace);
}
catch (Exception ex2)
{
MessageBox.Show("Fatal Non-UI error", ex2.Message, MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void Current_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SewerStammGen
{
public enum EMainWindowViewType
{
Home
}
}

View File

@@ -0,0 +1,24 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SewerStammGen.HostBuilders
{
static class AddConfigurationHostBuilderExtensions
{
public static IHostBuilder AddConfiguration(this IHostBuilder hostBuilder)
{
hostBuilder.ConfigureAppConfiguration(c =>
{
c.AddJsonFile("appsettings.json");
c.AddEnvironmentVariables();
}
);
return hostBuilder;
}
}
}

View File

@@ -0,0 +1,21 @@
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SewerStammGen.HostBuilders
{
static class AddDBContextHostBuilderExtensions
{
public static IHostBuilder AddDBContext(this IHostBuilder hostBuilder)
{
hostBuilder.ConfigureServices((context, services) =>
{
});
return hostBuilder;
}
}
}

View File

@@ -0,0 +1,25 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using SewerStammGen.Interface.Navigator;
using SewerStammGen.ViewModel.State.Navigation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace SewerStammGen.HostBuilders
{
internal static class AddServicesHostBuilderExtensions
{
public static IHostBuilder AddServices(this IHostBuilder host)
{
host.ConfigureServices(services =>
{
services.AddSingleton<IMainWindowNavigator, MainWindowNavigator>();
});
return host;
}
}
}

View File

@@ -0,0 +1,21 @@
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SewerStammGen.HostBuilders
{
static class AddStoresHostBuilderExtensions
{
public static IHostBuilder AddStores(this IHostBuilder hostBuilder)
{
hostBuilder.ConfigureServices(services =>
{
});
return hostBuilder;
}
}
}

View File

@@ -0,0 +1,24 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using SewerStammGen.ViewModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SewerStammGen.HostBuilders
{
static class AddViewModelsHostBuilderExtensions
{
public static IHostBuilder AddViewModels(this IHostBuilder hostBuilder)
{
hostBuilder.ConfigureServices(services =>
{
services.AddTransient<MainWindowViewModel>();
});
return hostBuilder;
}
}
}

View File

@@ -0,0 +1,14 @@
using SewerStammGen.ViewModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SewerStammGen.Interface
{
public interface IViewModelAbstractFactory
{
BaseViewModel CreateViewModel(EMainWindowViewType viewType);
}
}

View File

@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SewerStammGen.Interface.Navigator
{
internal interface IMainWindowNavigator : INavigator
{
}
}

View File

@@ -0,0 +1,19 @@
using SewerStammGen.ViewModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SewerStammGen.Interface.Navigator
{
internal interface INavigator
{
BaseViewModel CurrentViewModel
{
get;
set;
}
event Action StateChanged;
}
}

View File

@@ -3,10 +3,16 @@
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:my="clr-namespace:SewerStammGen.Views"
xmlns:local="clr-namespace:SewerStammGen"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<my:UCSewerConnector x:Name="SewerConnector" />
<ContentControl Grid.Column="1" Content="{Binding CurrentViewModel}" />
</Grid>
</Window>

View File

@@ -23,6 +23,8 @@ namespace SewerStammGen
public MainWindow()
{
InitializeComponent();
SewerConnector.DataContext = new ViewModel.SewerConnectorViewModel();
}
}
}

View File

@@ -7,4 +7,16 @@
<UseWPF>true</UseWPF>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="7.0.0" />
<PackageReference Include="Syncfusion.SfGrid.WPF" Version="20.4.0.54" />
</ItemGroup>
<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@@ -1,4 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
<ItemGroup>
<ApplicationDefinition Update="App.xaml">
<SubType>Designer</SubType>
</ApplicationDefinition>
</ItemGroup>
<ItemGroup>
<Compile Update="Views\EditSchacht.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\UCNormXML.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\UCSewerConnector.xaml.cs">
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<Page Update="MainWindow.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Views\EditSchacht.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Views\UCNormXML.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Views\UCSewerConnector.xaml">
<SubType>Designer</SubType>
</Page>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SewerStammGen.ViewModel
{
public delegate TViewModel CreateViewModel<TViewModel>() where TViewModel : BaseViewModel;
public class BaseViewModel : ObservableObject
{
public virtual void Dispose() { }
}
}

View File

@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SewerStammGen.ViewModel
{
public interface IViewModel : INotifyPropertyChanged
{
}
public interface IViewModel<TModel> : IViewModel
{
[Browsable(false)]
[Bindable(false)]
TModel Model { get; set; }
}
}

View File

@@ -0,0 +1,21 @@
using SewerStammGen.Interface.Navigator;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace SewerStammGen.ViewModel
{
internal class MainWindowViewModel : BaseViewModel
{
public IMainWindowNavigator Navigator { get; set; }
public BaseViewModel CurrentView => Navigator.CurrentViewModel;
public MainWindowViewModel(IMainWindowNavigator navigator)
{
Navigator = navigator;
}
}
}

View File

@@ -0,0 +1,14 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace SewerStammGen.ViewModel
{
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

View File

@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace SewerStammGen.ViewModel
{
[Serializable]
class RelayCommand : ICommand
{
#region Fields
private readonly Action<object> execute;
private readonly Predicate<object> canExecute;
#endregion
#region Constructors
public RelayCommand(Action<object> execute) : this(execute, null) { }
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null) throw new ArgumentNullException("execute");
this.execute = execute;
this.canExecute = canExecute;
}
#endregion
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
if (canExecute == null) return true;
return canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
execute(parameter);
}
#endregion
}
}

View File

@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SewerStammGen.ViewModel
{
class SewerConnectorViewModel : ViewModel
{
}
}

View File

@@ -0,0 +1,25 @@
using SewerStammGen.Interface.Navigator;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SewerStammGen.ViewModel.State.Navigation
{
internal class MainWindowNavigator : ObservableObject, IMainWindowNavigator
{
private BaseViewModel _currentViewModel;
public BaseViewModel CurrentViewModel
{
get => _currentViewModel;
set
{
_currentViewModel?.Dispose();
_currentViewModel = value;
StateChanged?.Invoke();
}
}
public event Action StateChanged;
}
}

View File

@@ -0,0 +1,179 @@
using Syncfusion.UI.Xaml.Collections.Generic;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace SewerStammGen.ViewModel
{
[Serializable]
public abstract class ViewModel : IViewModel
{
protected ViewModel()
{
var initializationTask = new Task(() => Initialize());
initializationTask.ContinueWith(result => InitializationCompletedCallback(result));
initializationTask.Start();
}
/// <summary>
/// Initializes this instance.
/// </summary>
protected virtual void Initialize()
{
}
/// <summary>
/// Callback method for the async initialization.
/// </summary>
/// <param name="result">The result.</param>
private void InitializationCompletedCallback(IAsyncResult result)
{
var initializationCompleted = InitializationCompleted;
if (initializationCompleted != null)
{
InitializationCompleted(this, new AsyncCompletedEventArgs(null, !result.IsCompleted, result.AsyncState));
}
InitializationCompleted = null;
}
/// <summary>
/// Occurs when the initialization is completed.
/// </summary>
public event AsyncCompletedEventHandler InitializationCompleted;
/// <summary>
/// Called when a property has changed.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
/// <remarks></remarks>
protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = "")
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#region INotifyPropertyChanged Members
/// <summary>
/// Occurs when a property value changes.
/// </summary>
/// <remarks></remarks>
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
public abstract class ViewModel<TModel> : ViewModel, IViewModel<TModel> where TModel : class
{
private TModel model;
/// <summary>
/// The Model encapsulated by this ViewModel.
/// </summary>
/// <remarks>If you change this, all needed PropertyChanged events will be raised automatically.</remarks>
[Browsable(false)]
[Bindable(false)]
public TModel Model
{
get
{
return model;
}
set
{
if (Model != value)
{
// get all properties
var properties = this.GetType().GetProperties(BindingFlags.Public);
// all values before the model has changed
var oldValues = properties.Select(p => p.GetValue(this, null));
var enumerator = oldValues.GetEnumerator();
model = value;
// call OnPropertyChanged for all changed properties
foreach (var property in properties)
{
enumerator.MoveNext();
var oldValue = enumerator.Current;
var newValue = property.GetValue(this, null);
if ((oldValue == null && newValue != null)
|| (oldValue != null && newValue == null)
|| (!oldValue.Equals(newValue)))
{
OnPropertyChanged(property.Name);
}
}
}
}
}
/// <summary>
/// Initializes a new instance of the <see cref="ViewModel"/> class.
/// </summary>
/// <remarks></remarks>
protected ViewModel(TModel model)
: base()
{
this.Model = model;
}
/// <summary>
/// Returns a hash code for this instance.
/// </summary>
/// <returns>
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
/// </returns>
public override int GetHashCode()
{
return Model.GetHashCode();
}
/// <summary>
/// Determines whether the specified <see cref="System.Object"/> is equal to this instance.
/// </summary>
/// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
public override bool Equals(object obj)
{
if (obj == null)
return false;
var other = obj as IViewModel<TModel>;
if (other == null)
return false;
return Equals(other);
}
/// <summary>
/// Determines whether the specified <see cref="IViewModel<TModel>"/> is equal to this instance.
/// </summary>
/// <param name="other">The <see cref="IViewModel<TModel>"/> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="IViewModel<TModel>"/> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
public bool Equals(IViewModel<TModel> other)
{
if (other == null)
return false;
if (Model == null)
return Model == other.Model;
return Model.Equals(other.Model);
}
}
}

View File

@@ -0,0 +1,14 @@
<Page x:Class="SewerStammGen.Views.EditSchacht"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:SewerStammGen.Views"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Title="EditSchacht">
<Grid>
</Grid>
</Page>

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace SewerStammGen.Views
{
/// <summary>
/// Interaktionslogik für EditSchacht.xaml
/// </summary>
public partial class EditSchacht : Page
{
public EditSchacht()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,36 @@
<UserControl x:Class="SewerStammGen.Views.UCNormXML"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:SewerStammGen.Views"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<RadioButton GroupName="Norm">DIN - EN 13508 - 2: 2003 / Ohne nationale Festlegung</RadioButton>
<RadioButton GroupName="Norm">DIN - EN 13508 - 2: 2003 / Nationale Festlegung DWA M 149-2</RadioButton>
<RadioButton GroupName="Norm" IsEnabled="False">DIN - EN 13508 - 2: 2003 / andere nationale Festlegung (Bemerkung erforderlich)</RadioButton>
<RadioButton GroupName="Norm" IsEnabled="False">ISYBAU 2001</RadioButton>
<RadioButton GroupName="Norm" IsEnabled="False">ISYBAU 1996</RadioButton>
<RadioButton GroupName="Norm" IsEnabled="False">anderes Kodiersystem (Bemerkung erfolrderlich)</RadioButton>
<RadioButton GroupName="Norm">DIN - EN 13508 - 2: 2003 / Nationale Festlegung Arbeitshilfen Abwasser</RadioButton>
<RadioButton GroupName="Norm">DIN - EN 13508 - 2: 2011 / Ohne nationale Festlegung</RadioButton>
<RadioButton GroupName="Norm">DIN - EN 13508 - 2: 2011 / Nationale Festlegung DWA M 149 - 2</RadioButton>
<RadioButton GroupName="Norm">DIN - EN 13508 - 2: 2011 / Nationale Festlegung Arbeitshilfen Abwasser</RadioButton>
</StackPanel>
<StackPanel Grid.Row="1">
<RadioButton GroupName="Regelwerk" IsEnabled="False">Arbeitshilfen Abwasser (ISYBAU 1996/2001)</RadioButton>
<RadioButton GroupName="Regelwerk">Arbeitshilfen Abwasser (ISYBAU 2006)</RadioButton>
<RadioButton GroupName="Regelwerk" IsEnabled="False">Sonstige Festlegungen</RadioButton>
<RadioButton GroupName="Regelwerk" IsEnabled="False">keine Angaben</RadioButton>
<RadioButton GroupName="Regelwerk">Arbeitshilfen Abwasser (ISYBAU 2013)</RadioButton>
<RadioButton GroupName="Regelwerk">Arbeitshilfen Abwasser (ISYBAU 2017)</RadioButton>
</StackPanel>
</Grid>
</UserControl>

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace SewerStammGen.Views
{
/// <summary>
/// Interaktionslogik für UCNormXML.xaml
/// </summary>
public partial class UCNormXML : UserControl
{
public UCNormXML()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,36 @@
<UserControl x:Class="SewerStammGen.Views.UCSewerConnector"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:syncfusion="http://schemas.syncfusion.com/wpf"
xmlns:local="clr-namespace:SewerStammGen.Views"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<DataTemplate x:Key="headerTemplate">
<TextBox></TextBox>
</DataTemplate>
</UserControl.Resources>
<Grid>
<StackPanel>
<syncfusion:SfMultiColumnDropDownControl
Width="250"
Height="24"
Margin="10,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
AllowAutoComplete="True"
AllowImmediatePopup="True"
AllowIncrementalFiltering="True"
AutoGenerateColumns="False"
DisplayMember="Title"
HeaderTemplate="{StaticResource headerTemplate}"
PopupWidth="400"
ValueMember="Cast"
>
</syncfusion:SfMultiColumnDropDownControl>
</StackPanel>
</Grid>
</UserControl>

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace SewerStammGen.Views
{
/// <summary>
/// Interaktionslogik für UCSewerConnector.xaml
/// </summary>
public partial class UCSewerConnector : UserControl
{
public UCSewerConnector()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,7 @@
{
"ConnectionStrings": {
"databaseToUse": "default",
"default": "Host = localhost; Database = dasaso; Username = kansan; Password = kansan",
"sqlite": "Data Source=database.db"
}
}