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).
das kann so nicht funktionieren.
Du erstellst mehrere ItemsViewModel-Instanzen.
Eines davon ist ans MainWindow gebunden, die anderen jeweils an ein PathControl.
Wenn Du jetzt Deinen Button zum Wechseln des Index klickst, wirkt sich das nur auf den Index des UserControls aus, das ans MainWindow gebunden ist -> deshalb kannst Du in den UsderControls keine Veränderung sehen.
Was soll bei Deinem Code Deiner Meinung nach passieren, wenn Du den Button für das Wechseln des Index klickst, und bereits mehrere UserControls vorhanden sind? Wo genau soll dann gewechselt werden?
Die Vorgehensweise ist schon sehr obskur. Du solltest auf jeden Fall das MainViewModel für das MainWindow von den ItemViewModels trennen - und Dir dann überlegen, welche Funktionalität Du in welchem dieser ViewModels brauchst.
Beim Erstellen eines neuen UserControls solltest Du dann lediglich das ItemsViewModel erzeugen (nicht das Control selbst - das widerspricht MVVM, weil Dein ViewModel Teile der View kennt!) und es in die ObservableCollection packen.
Im ItemsControl des MainWindow legst Du dann ein ItemTemplate an, das Dein UserControl enthält. Such DIr dazu am besten ein paar Beispiele, die mit ItemTemplate arbeiten, es ist nicht sehr kompliziert.
Jedoch möchte ich das Browser-Element nicht in der Anwendung.
Möchtest das Element grundsätzlich nicht verwenden oder einfach nicht anzeigen?
Das Webbrowser-Element funktioniert nämlich auch, wenn es nicht angezeigt wird.
Bist Du Dir da sicher? Hast Du versucht, den Wert nach der Änderung im MainViewModel aus der gebundenen Property in Bena mal abzurufen?
Zitat von CodeHamster
meine Debugging-Ausgaben kommen nicht.
Das ist jedenfalls nicht verwunderlich, denn über das Binding wird sicherlich nicht Dein Setter aufgerufen, sondern direkt die SetValue-Methode der DependencyProperty. Du kannst also nicht erwarten, dass die Ausgabe automatisch bei jeder Änderung kommt. Die kommt nur, wenn der Wert tatsächlich über Deinen Setter gesetzt wird.
Zitat von CodeHamster
oder eine bessere Idee wie ich das Binding zwischen MVM und meiner Custom-Klasse bewerkstelligen kann?
Ja. Das Gefrickel im CodeBehind sein lassen und das Binding in XAML umsetzen.
Zitat von MrSparkle
Davon abgesehen ist dort der falsche Ort für die Logik der View. Die gehört ins ViewModel.
Warum denn? Wenn Bena ein Control ist, an das er einen Wert aus dem ViewModel binden will, dann muss das doch da umgesetzt werden...
hinzu kommt noch, dass Du in Deiner Suche()-Methode eine neue Instanz von PrefixTree erstellst, auf der Du dann weiterarbeitest.
Diese Instanz kennt aber die in der Main-Methode hinzugefügten Wörter nicht...
denn bei EmployeeListView handelt es sich (wohl) nicht um das UI-Element (denn dieses hat ja gar kein CollectionChanged-Ereignis), sondern um die Collection
In der Tat, da habe ich mich wohl von der Benennung hinreissen lassen - Freitag Nachmittag, puh!
Das bedeutet, was ich bezüglich Views geschrieben habe, trifft hier nicht zu, wir sind die ganze Zeit auf ViewModel-Ebene.
Trotzdem halte ich den Ansatz, über das CollectionChanged-Ereignis zu gehen, aus den ebenfalls genannten Gründen für den falschen Weg.
auf jeden Fall Möglichkeit a.)
Und zwar in umgekehrter Reihenfolge: Erst löschst Du das Objekt aus der Datenbank (über ein entsprechedes Datenlayer), und erst wenn das funktioniert hat aus der ObservableCollection im ViewModel. Dann kannst Du auch noch Fehlerbehandlung machen, wenn das Löschen aus der DB schiefgeht, und das Objekt ist in der UI noch vorhanden.
Möglichkeit b.) ist ein völlig falscher Ansatz.
Wenn ein solcher Code
EmployeeListView.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler
(CollectionChangedMethod);
im ViewModel auftaucht, bedeutet das, dass das ViewModel die View kennt und das sollte es nie.
Wenn der Code im CodeBehind steht, verlagerst Du Deine Löschlogik direkt unter Umgehung des ViewModels in die View - und das ist dann auf jeden Fall ein Designfehler.
Auf dieser Grundlage sind dann auch die Aussagen
Zitat von Abt
In MVVM arbeitet man aber nicht mit Code Behind Eventhandling
Zitat von Abt
Das Control gehört da nicht in Dein ViewModel.
hoffentlich verständlich, denn bei dem Ansatz über CollectionChanged trifft auf jeden Fall einer dieser Umstände zu.
Machst Du mittlerweile irgendwas anders, oder spielst Du noch im CodeBehind oder sonstwo mit den Mausereignissen rum?
Ansonsten sollte es eigentlich mit meinem Vorschlag funktionieren.
Ich hatte das mehr oder weniger 1:1 mit Deinem Code getestet und nur den Style entsprechend verschoben und angepasst und den fa-Namespace und die zugehörigen Elemente rausgeschmissen, an der Struktur aber sonst nichts geändert. Dabei ergab sich das von Dir beschriebene gewünschte Verhalten.
einfach nur eine Klassenbibliothek.
Um using System.Windows.Forms nutzen zu können, musst Du dann noch einen Verweis (Referenz) auf die System.Windows.Forms.dll erstellen.
Und auch die Einschränkung "class" ist bei Angabe eines Interfaces überflüssig, denn Interfaces funktionieren generell nur bei Klassen (nicht bei Strukturen).
Nur zur Richtigstellung: Das stimmt nicht. Auch Strukturen können Interfaces implementieren, und dann auch in auf diese Interfaces eingeschränkten Generics benutzt werden.
so rum geht's nicht, das musst Du genau andersrum machen.
Sprich, die Border (dein Target) bekommt den Style und reagiert per DataTrigger auf das MouseOver des Elternelements:
innerhalb der Controls-Auflistung eines übergeordneten Elements sind Deine Objekte nur als Control bekannt, und da gibt es die Methode UpdateStatus eben nicht.
Wie Th69 schon angedeutet hat, musst Du das dann auf Deinen Typen zurück casten.
bist Du ganz sicher, dass das SelectedItem, dass Du initial setzen willst, die selbe Instanz ist, die auch in der ItemsSource der ComboBox steckt?
Wenn Du das Item z.B. zweimal aus den selben Daten generierst, einmal für die Nutzung als SelectedItem und einmal für die Verwendung in der ItemsSource, dann hättest Du nicht die selbe Instanz und dann ergäbe sich genau Deine Fehlerbeschreibung.
und den Button dann auch nicht mit Click-Event im Code behind behandeln, sondern Command Binding benutzen und ein Command aus dem Viewmodel dran binden.
Dort ist dann wenn das SelectedItem richtig gebunden dasselbe schon bekannt und kann direkt verwendet werden.
ich hab das gerade mal ausprobiert mit einem float[70,70,70], also 343000 Werten, die ich mit Zufallswerten befüllt habe.
--> dauert auf meiner Maschine hier 7 Millisekunden...
Ich vermute also, dass bei Dir eher das Besorgen der Werte so lange dauert. Wo kommen die denn her?
das keiner von Euch mir sagen kann warum mein Programm hängt sobald das externe Tool gestartet wurde
Eine Vermutung hätte ich schon anzubieten: das OutputDataReceived-Event wird nur ausgelöst, wenn das externe Programm eine vollständige Zeile inklusive Zeilenumbruch schreibt.
Vermutlich ist das nicht der Fall, dann müsstest Du selbst direkt auf dem StandardOutput-Stream lesen.
nein, das ist nicht möglich. Du kannst auch nicht von Objekten erben, die von Model3D abgeleitet sind. Die gesamte Vererbungshierarchie ist durch internal oder sealed geschützt.
Du kannst nur die vorhandenen Ableitungen von Model3D benutzen, aber nicht davon erben.
die Methoden sind in der abstrakten Klasse als internal abstract deklariert.
Du kannst also nicht direkt davon erben. Nur Klasen innerhalb der PresentationCore.dll können diese abstrakten Methoden implementieren.
Ich weiss, was das ist. Ich habe ja auch nicht gesagt, dass Cloudflare mir was unterjubelt, sondern die besuchten Sites mir über Cloudflare was unterjubeln, während Ihre Grundfunktionalität witzigerweise meist auf der eigenen Domain verbleibt.
Aber egal scheint ja ohnehin irgendwie ein Geisterproblem gewesen zu sein.
Und ja, genau der RocketLoader war's!! das soll einer verstehen, wie der sich bei mir eingeschlichen hat...