ich möchte mich in die Liste der Gratulanten einreihen und auch meinen herzlichen Dank aussprechen.
Es ist schon beeindruckend, was das Entwicklungsteam der neuen Software hier geleistet hat. Meinen größten Respekt dafür!
Ihr habt myCSharp.de auf eine zukunftssichere Basis gestellt, und ich finde es super, dass diese jetzt auch .NET ist.
Ich bin gespannt, wie sich die neue Plattform entwickelt.
Unten im Beitrag habe ich ein paar Möglichkeiten für diese Problematik aufgelistet.
Der dort erwähnte ViewManager entspricht dabei dem WindowsService, den MrSparkle nannte.
ich würde definitiv ein eigenes Fenster bevorzugen - dann kann man die Informationen auch hübsch darstellen. Der MessageBox sind sehr enge Grenzen gestetzt, was Styling/Layout/Formatierung angeht.
Firebird wäre auch noch eine Alternative - ebenfalls komplett frei und mit .NET Unterstützung. Kann als Server oder embedded laufen, und man kann leicht dazwischen wechseln.
bei Commands auf Buttons ist für das enablen/disablen normalerweise die CanExecute-Methode und das CanExecuteChanged-Event zuständig. Wenn Du die richtig implementierst, solte Dein Button vom Command die Information automatisch erhalten.
Edit: Und ich sollte richtig lesen - kein ausgewählter Employee, kein Command, kein CanExecute... dann wie JimStark geschrieben hat, über IsEnabled.
nur der Vollständigkeit halber, EF unterstützt seit Version 6.3.0 .NET Standard 2.1 und ist damit auch in .NET Core lauffähig.
Das steht auch so in den von Abt verlinkten Release Notes:
Zitat von Release Notes
Support for .NET Core 3.0
- The EntityFramework package now targets .NET Standard 2.1 in addition to .NET Framework 4.x.
- This means that EF 6.3 is cross-platform and supported on other operating systems besides Windows, like Linux and macOS.
- The migrations commands have been rewritten to execute out of process and work with SDK-style projects.
Weiterentwickelt wird es aber trotzdem nicht mehr, und für Neuentwicklungen ist EF Core daher wärmstens zu empfehlen.
wann wird denn VM3 erstellt?
Das riecht danach, dass das erst nach dem Aufruf von Publish passiert. Dann bekommst Du das Event nicht mehr mit, weil Du zu spät subscribst.
also vorweg: die Diskussion in allen Ehren, und jeder sollte sich so seine Gedanken machen.
Aber bei technischen Begriffen wie hier bei den Branches oder dem von Th69 angesprochenen Master/Slave finde ich die Diskussion teilweise kleinlich bis peinlich.
Wahrscheinlich werden bestimmte Begriffe irgendwann vollkommen aus unserem Wortschatz verschwinden. Ob damit dann jemand geholfen ist, halte ich eher für fraglich.
Bin mal gespannt, wie dann in Zukunft z.B. Mastering/Remastering im Audiobereich genannt werden,
und wie lange man die Begriffe "herrlich" und "dämlich" noch verwenden darf (ok, das ist eine andere Baustelle, aber ähnlich geartet) ...
eine wichtige Anmerkung zu der Extension-Methode im Startbeitrag, da hier offenbar ein massives Verständnisproblem bezüglich Delegates/Events vorliegt: Diese Methode bringt gar nichts!
Sobald ein EventHandler einer Variable zugewiesen oder als Parameter übergeben wird, arbeitest Du nur noch auf einer Kopie des EventHandlers.
Aus dieser Kopie werden zwar mit der Extensionmethode alle Handler entfernt, das interssiert aber den ursprünglich an die Methode übergebenen EventHandler nicht die Bohne - da bleibt alles weiter registriert.
ich hatte es so interpretiert, dass der Benutzer sehen soll, wenn irgendwo sonst durch irgeneine Aktion im Code der Watcher ein- oder ausgeschaltet wird.
Also quasi eine Überwachung der Property, egel von wo oder wodurch sie geändert wird. Vielleicht habe ich das aber auch missverstanden. Das weiss nur der TE...
Im Grunde brauchst du ein ViewModel, das das Event vom FileSystemWatcher abonniert, und darauf reagiert, indem es eine Eigenschaft ändert, an die du deine Textbox gebunden hast.
Es geht ja hier um die Property FileSystemWatcher.EnableRaisingEvents, und wenn sich diese ändert, gibt es eben kein Event, auf das man reagieren könnte.
Deshalb denke ich auch, dass ein Wrapper die richtige Lösung ist. Zumindest sollte man den FileSystemWatcher in irgend einer Weise so kapseln, dass das Property nur über eine eigene Klasse gesetzt werden kann, dann kann man bei Änderung des Wertes auch eine eigene Notification auslösen.
das kann nicht funktionieren, weil der FileSystemWatcher nicht INotifyPropertyChanged implementiert.
Es werden also keine Nachrichten über die Änderung dieser Property generiert, folglich kann die GUI auch keine solchen Benachrichtigungen erhalten.
ich programmiere einen Dienst generell immer so, dass die ganze Logik in einer eigenen Assembly (oder mehreren) liegt, und ich dann auch ein minimales Konsolenprogramm schreiben kann, welches diese Logik ausführt. Das ist bei Diensten auch immer möglich.
Das ist oft sehr hilfreich beim Debuggen und bei der Fehlersuche, und ich kann dieses Vorgehen aus Erfahrung wärmstens empfehlen.
das Control das in dem DataTemplate steckt ist ein Viewmodel
Ein Control ist niemals ein ViewModel, sondern eine View.
Das Objekt, das Du per Binding mit dieser View verbindest, ist ein ViewModel - also das, was Du hier als Model bezeichnest.
Schau Dir nochmal ein paar Artikel zu MVVM an, z.B. [Artikel] MVVM und DataBinding , hier liegt offenbar noch ein grundsätzliches Verständnisproblem vor.
zusätzlich solltest Du am Ende eines Befehls einen Zeilenumbruch (Environment.NewLine) am Ende mitschicken, denn auf der Konsole wird der Befehl ja auch für gewöhnlich erst durch das Drücken der Enter-Taste ausgelöst.
im gezeigten Code hast Du für das Binding an die DataGridTextColumn keinen Converter gesetzt, da versuchst Du also, ein bool an ein Element zu binden, dass eine Visibility erwartet.
die Endlosrekursion kann (relativ) leicht entstehen, wenn Du z.B. auf einer - irgendwo im Baum durch Ermitteln der zugehörigen Paletten zu einem gefundenen Sub-Vorgang - gefundenen Palette nochmal auf einen bereits gefundenen Vorgang stößt (z.B. den ursprünglichen, über den Du in die Suche startest).
Du musst also irgendwie sicherstellen, dass bereits gefundene Vorgänge oder Paletten nicht nochmal berücksichtigt werden.
Entweder Du setzt mehrere SQL-Queries ab, die diese Logik abbilden, oder Du versuchst, die Logik in einer Stored Procedure zu implementieren.
Das in einem einzigen SQL-Statement abzubilden, wird jedenfalls ziemlich schwierig (wenn nicht unmöglich) werden.
VS ist nur eine Entwicklungsumgebung; das hat null mit Sqlite zutun.
Zitat
LiteDB ist eine NoSQL Datenbank; also ein völlig anderes Paradigma (mit allen Vor- und Nachteilen)als Sqlite.
Allerdings kann man bei einer Evaluierung eines geeignten Datenbanksystems heutzutage durchaus NoSQL auch mal in Betracht ziehen. Was sich besser eignet, sollte die Evaluierung ergeben.
Wenn es um Alternativen geht, seien noch folgende genannt: Firebird (SQL, embedded oder als Server möglich) CouchDB (NoSQL)
Unter den oben genannten Bedingungen ist der Typ<T> doch bekannt?
Inwiefern?
Was hindert mich daran, so etwas zu schreiben:
public class MyClass
{
public bool Equals<T>(T value)
{
//...
}
}
// und dann so aufzurufen:
MyClass c = new MyClass();
if(c.Equals<int>(42)) // statt int könnte ich hier auch JEDEN ANDEREN TYPEN einsetzen.
{
}
Main Rat: Schau Dir mal IEquatable<T> an, und verwende es.
Abgesehen von dem was LaTino bereits geschrieben hat, auch die generische Implementierung:
XmlSerializer serializer = new XmlSerializer(typeof(T));
serializer.Serialize(xmlWriter, this);
Das dürfte gewaltig rumpeln, wenn this nicht gerade vom Typ T ist.
Wenn man schon eine Equals-Methode zur Verfügung stellt, sollte wenigstens dafür georgt sein das sie auch sicher ist.
Meiner Ansicht nach sollte dann zumindest auch IEquatable<T> implementieren.
Generell sollten Snippets universell und sicher einsetzbar sein - das ist hier nicht der Fall.
Die UserControl wird ja wie ein Child behandelt, mit dem MainWindow als Parent, richtig?
Ja, korrekt.
Zitat
In diesem Fall, wenn ich den DAtaContext also an der Stelle rausnehme, verwendet das UC den DataContext des MainWindows? Andernfalls würde das Binding ja nicht funktionieren, oder?
Generell ist es so, dass Child-Controls den DataContext des Parent automatisch bekommen, sofern man ihn nicht anders besetzt.
Beim ItemsControl ist es aber so, dass jedes darin enthaltene Child-Control als DataContext das jeweilige zugeordnete Item aus der ItemsSource bekommt.
Dadurch werden in Deinem Beispiel automatisch die PathViewModels, die in der (als ItemsSource gebundenen) ObdservableColection stecken, als DataContext für die über das ItemTemplate erstellten Items fungieren. Daher funktioniert das Binding - der DataContext im Item ist immer das zugehörige Objekt aus der ItemsSource.
Zitat
Kennt damit mein ItemsViewModel nicht automatisch mein PathViewModel? Ist das dann MVVM-konform?
Zweimal ja. Typischerweise hat man beim MVVM eine Art Baumstruktur der ViewModels, und es ist völlig in Ordnung, dass übergeorndete ViewModels die untergeordneten ViewModels kennen - das ist sogar meistens notwendig.
Wichtig ist dabei, dass das alles auf ViewModel-Ebene passiert. Gegen MVVM würde es dann verstoßen, wenn (wie in Deinem ursprünglichen Code) in der ViewModel-Ebene das UserControl selbst bekannt sein müsste, da das UserControl eben zur View-Ebene gehört.
Zitat
Das Command wird ja vom MainViewModel (ItemsViewModel) ausgeführt, aber muss doch an der Stelle eine neue Instanz des PathViewModels erstellen, oder?
Genau, es muss eine Instanz des PathViewModels erstellt werden. Dafür ist aber eben das MainViewModel zuständig, denn es enthält ja die Collection der PathViewModels und muss diese auch pflegen.
Was ich mit "einzeln steuern" meinte, ist die Random-Funktionalität. Wenn Du für jedes PathViewModel einzeln den Index zufällig neu besetzen willst, dann brauchst Du dafür die Funktioaltät im PathViewModel selbst.
ich bin bisher der Ansicht, wenn ich im XAML einer view eine local:ItemsViewModel-Variable deklariere, dass sie dann jede View zur genau gleichen Instanz referenziert. Das ist offenbar falsch?
Ja, das ist falsch. Es wird durch diese Deklaration für jedes UserControl eine eigene Instanz des ViewModels erstellt. Und diese Instanzen wissen eben nicht, dass Du in einer anderen Instanz, die als DataContext im MainWindow lebt, sich irgend eine Index-Property ändert.
Zitat
Kapier ich jetzt nicht: welches UserControl habe ich denn ans MainWindow gebunden?
Stimmt, mein Fehler... Ich meinte ViewModel, nicht UserControl.
Zum Code:
1. Nimm aus Deinem UserControl die DataContext-Deklaration raus:
3. Trenne die ViewModels auf, ungefähr so (nicht getestet, nur Deinen Code hier im Editor etwas umgebaut, müsste aber grob funktionieren oder zumindest die Vorgehensweise verdeutlichen):
public class PathViewModel
{
public ObservableCollection<int> AvailableNumbers { get; set; } = new ObservableCollection<int>();
private int _theIndex;
public int TheIndex
{
get { return _theIndex; }
set
{
_theIndex = value;
OnPropertyChanged(ref _theIndex, value);
}
}
public PathViewModel()
{
AvailableNumbers.Add(1);
AvailableNumbers.Add(2);
AvailableNumbers.Add(3);
AvailableNumbers.Add(4);
AvailableNumbers.Add(5);
}
}
public class ItemsViewModel
{
public ObservableCollection<PathViewModel> PathViewModels { get; set; } = new ObservableCollection<PathViewModel>();
public ItemsViewModel()
{
UCCreationCommand = new CommandDelegateBase(UCCreationExecute, UCCreationCanExecute);
UCDeletionCommand = new CommandDelegateBase(UCDeletionExecute, UCDeletionCanExecute);
ChangeIndexCommand = new CommandDelegateBase(IndexExecute, IndexCanExecute);
}
public ICommand UCCreationCommand { get; set; }
public ICommand UCDeletionCommand { get; set; }
public ICommand ChangeIndexCommand { get; set; }
private bool UCCreationCanExecute(object paramerter)
{
if (PathViewModels.Count < 8)
{
return true;
}
else
{
return false;
}
}
private void UCCreationExecute(object parameter)
{
PathViewModel p = new PathViewModel();
PathViewModels.Add(p);
}
private bool UCDeletionCanExecute(object paramerter)
{
if (PathViewModels.Count != 0)
{
return true;
}
else
{
return false;
}
}
private void UCDeletionExecute(object parameter)
{
PathViewModels.RemoveAt(PathViewModels.Count -1);
}
private bool IndexCanExecute(object paramerter)
{
return true;
}
private void IndexExecute(object parameter)
{
Random rnd = new Random();
foreach(var pathVM in PathViewModels)
{
int x = rnd.Next(0, AvailableNumbers.Count);
pathVM.TheIndex = x;
}
}
}
So, jetzt erstellst Du mit dem Click auf den Button nur ein ViewModel und packst es in die ObservableCollection. Diese ist an das ItemsControl gebunden - und durch das ItemTemplate wird jetzt für jedes PathViewModel, das sich in der Collection befindet, automatisch ein PathControl angezeigt.
Die ChangeIndex-Geschichte hab ich jetzt mal so umgebaut, dass in jedem vorhandenen PathViewModel ein neuer zufälliger Wert gesetzt wird. Wenn Du das einzeln steuern willst, gehört es aber ins PathViewModel und nicht ins MainViewModel (bzw. ItemsViewModel).