ich dachte mir mal, ich poste mal ein Tutorial.
Und das für die Verwendung des WPF - Ribbon unter der Verwendung des MVVM-Pattern.
Vorwort: Es gibt von M$ ein paar Samples zu diesem Thema, eine Suche nach 'MicrosoftRibbonForWPFSourceAndSamples' findet diese.
Soweit so gut, aber dies ist nicht das, nach dem ich suchte...
In den Samples werden (umfangreiche) Objekte im ViewModel zusammengebaut. Aber MVVM-konform ist das nicht. Es ist so einfach nicht möglich, das ganze Konstrukt einfach umzubiegen...
Beruflich habe ich eine Umsetzung benötigt, in der wie gewöhnt, einfach ein ViewModel und das entsprechende DataTemplate definiert werden kann.
Also, nach ein paar fails hier eine Lösung des Ganzen.
Überschrieben wurde dabei die Definition, wie Das jeweilige ItemsControl seine Items erzeugt.
Bei dem Ribbon ist es das ControlTemplate, was bei der Erzeugung der Tabs überschrieben wurde.
Bei dem ApplicationMenu einfach der Container, der zur Anzeige eines Items erzeugt wird.
public class CustomRibbon : Ribbon
{
/// <summary>
/// Bereitet den RibbonTab so vor, dass der Content auf das Item gesetzt wird und ein definiertes DataTemplate zur Anzeige genutzt wird
/// </summary>
/// <param name="element">Der Tab, der zur Anzeige vorbereitet werden soll</param>
/// <param name="item">Objekt, das als Content gesetzt wird</param>
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
var tab = (RibbonTab)element;
var controlTemplate = new ControlTemplate(typeof(RibbonTab));
var contentControlFactory = new FrameworkElementFactory(typeof(ContentControl));
contentControlFactory.SetBinding(ContentControl.ContentProperty, new Binding() { Source = item });
contentControlFactory.SetBinding(VisibilityProperty, new Binding()
{
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged, Source = tab,
Path = new PropertyPath(nameof(tab.IsSelected)), Converter = new BooleanToValueConverter() { FalseValue = Visibility.Collapsed, TrueValue = Visibility.Visible }
});
controlTemplate.VisualTree = contentControlFactory;
contentControlFactory.SetValue(IsTabStopProperty, false);
tab.Template = controlTemplate;
tab.Header = item;
}
}
public class CustomRibbonApplicationMenu : RibbonApplicationMenu
{
/// <summary>
/// Erstellt einen neuen Container für einen Eintrag im Menü
/// </summary>
/// <returns>Den erstellten Container</returns>
protected override DependencyObject GetContainerForItemOverride()
{
return new ContentControl() { IsTabStop = false };
}
/// <summary>
/// Bereitet den Menüeintrag für die Anzeige vor
/// </summary>
/// <param name="element">Das <see cref="ContentControl"/>, das als Container genutzt wird</param>
/// <param name="item">Der Content</param>
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
var contentControl = ((ContentControl)element);
contentControl.Content = item;
}
/// <summary>
/// Behebt das Verhalten, dass das Menü geschlossen wird, wenn ein Menuitem, das disabled ist, angeklickt wird.
/// </summary>
/// <param name="e">Die <see cref="MouseButtonEventArgs"/></param>
protected override void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs e)
{
if (e.OriginalSource is ContentPresenter applicationMenuItemContentPresenter)
{
if (VisualTreeHelper.GetChild(applicationMenuItemContentPresenter, 0) is RibbonApplicationMenuItem applicationMenuItem)
{
if (!applicationMenuItem.IsEnabled)
{
e.Handled = true;
}
}
}
base.OnPreviewMouseLeftButtonUp(e);
}
/// <summary>
/// Behebt das Verhalten, dass in dem Menü manchmal direkt das ContentControl des ContainerForItemOverride angesprungen wurden und das AppMenuItem dann nicht fokussiert wurde. Gerade bei Up und Down war das aufgefallen.
/// </summary>
/// <param name="e">Die <see cref="KeyboardFocusChangedEventArgs"/></param>
protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
if (e.OriginalSource is ContentControl contentControl)
{
var applicationMenuItem = (UIElement)contentControl.FindVisualChildren<DependencyObject>().FirstOrDefault(x => x.GetType().GetProperty(nameof(QuickAccessToolBarId)) != null);
if (applicationMenuItem != null)
{
applicationMenuItem.Focus();
}
}
base.OnGotKeyboardFocus(e);
}
}
ich hatte zwischenzeitlich auch mal VS 2019 ausprobiert...
Nach ein wenig Arbeit habe ich mir nun wieder VS 2017 installiert. Dank Side by Side ja zum Glück möglich :-)
Ich hatte die genannten Probleme auch.
Das war bei einer WPF-Bibliothek, bei der ich eine neues Control eingefügt habe.
Und dieses bei dem referenzierten Projekt einfach nicht erkannt wurde...
Nach gefühlten 10x Clean, Rebuild, schließen und öffnen von VS und erneutem Laden der Solution ging es dann irgendwann.
Ich konnte damals das Problem nicht wirklich eingrenzen.
Nachdem mir dann irgendwann die gesamte Solution mit Fehlern, die nicht ersichtlich waren, um die Ohren flog, habe ich den Entschluss gefasst, zurück zu rudern.
Du kannst die Bibliothek höchstens irgendwo referenzieren und dann in .NET Core, .NET Framework... ausführen.
Daher in der Form der Erklärung eigentlich nur zwei potentielle Ursachen:
- Unterschiedene der referenzierten Pakete
Ja, die NETStandard-Bibliothek wird in ein .NET Framework-Projekt refrenziert.
Und genau, der Unterschied liegt in dem von dem Package referenzierten Pakete:
Unter .NET Framework 4.6 sind es diese:
System.Security.Cryptography.Encoding (≥ 4.3.0)
System.Security.Cryptography.X509Certificates (≥ 4.3.0)
.NET Standard ist nur ein Vertrag - keine Runtime.
.NET Standard ist aus C# sicht als Interface zu sehen, während .NET Framework, Mono, Xamarin, .NET Core.. als konkrete Implementierung zu sehen sind.
Soweit dachte ich auch. Wie gesagt, die API auf dem Server ist gege .NET CORE 2.x entwickelt.
Zitat von Abt
Die Frage ist hier also eher, ob sich jede Runtime identisch verhält.
Das habe ich mir gestern gebaut, in dem ich eine dumme Consolen-App geschrieben habe, die unter .NET Framework 4.6 läuft, und einmal ein Binary vom Server mit Framework-Bordmitteln entschlüsselt. Da kommt das Besagte bei raus.
Zusätzlich inkludiert das Testprojekt noch eine netStandard-Bibliothek in 2.0, wo genau das Gleiche getan wird.
Und da passt es dann
Wenn ich heute mal dazu komme, werde ich es hier mal posten.
Danke erstmal, Marko
ich kämpfe nun schon eine ganze Weile, und es bringt mich bald um den Verstand....
Wir haben eine API, die unter .NET Core 2.1 läuft, und unter anderem Daten verschlüsselt....
Das macht die Bibliothek 'System.Security.Cryptography.Pkcs'. Diese erstellt eine verschlüsselte Nachricht für ein oder mehrere Empfänger. Per nuget eingebunden.
Soweit alles gut, läuft
Auf Clientside empfangen wir die Nachrichten und müssen sie auch wieder entschlüsseln.
Und genau hier gehen die Probleme los...
Der Client ist gegen .NET Framework 4.7 programmiert. Die oben genannte Bibliothek ist in der gleichen Version per nuget eingebunden, aber es verhält sich anders...
So können die verschlüsselten Werte auch wieder entschlüsselt werden, aber der Wert, der da raus fällt, passt nur so halb.
Es sind immer irgendwelche Sonderzeichen am Anfang drin...
Das scheint irgendwie ein Codierungs-Problem zu sein. Aber wir benutzen überall UTF8.
Gegen netStandard 2.0 können die Werte ganz normal entschlüsselt werden.
Ich sehe auch, dass das Package je nach Framework unterschiedliche Assemblies einbindet, vielleicht liegt darin das Problem??
Ist da was bekannt?
Für Antworten bin ich sehr dankbar,
lG Marko
Das Problem von JavaScript ist aber, dass es teilweise einfach eine Hassliebe ist; und, dass JavaScript eben im Plaintext auf dem Client verarbeitet wird.
Genau so ist es... die Liebe dazu und der Hass dagegen, das sind wirklich Welten...
Zitat von Abt
Damit wäre ein weiterer Schritt gegeben, dass heutige Web-Technologien mehr und mehr auf dem Desktop ankommen.
Ein sehr guter Weg in die richtige Richtung.
Aber ich sehe es nicht kommen, das man mit einer SPA alles machen kann, was man halt mit einer Desktop-App machen kann.
Denn irgendwie bist Du ja immer im Browser gefangen... das hoffe ich doch zumindest !!
Um den Fehler eingrenzen zu können, müsste man sich wohl mal das ganze Netzwerkprotokoll ansehen... also wirklich, was sendet der Client, und was bekommt er dann vom Server.
Also:
ich entwickle einen barrierefreien Texteditor mit Microsoft Dotnet WPF.
der kommt auf meine Webseite zum kostenlosen Download.
Auch wirklich nicht böse gemeint, aber ich persönlich bleibe ganz sicher, wenn ich das hier so lese, bei meinem Notepad++ oder Visual Studio Code.
Alle können unheimlich viel, inklusive zig PlugIns.
Und ehrlich gesagt wäre es für mich ein Totschlagkriterium, wenn sich eine Software (und am besten noch ohne Nachfrage) als Standardprogramm registrieren würde !!
Was bedeutet denn barrierefrei eigentlich im Kontext eines Editors?
Es gibt keine Begründung, wieso ein Programm, das unter eingeschränkten Berechtigungen laufen können muss, Schreibrechte auf sein Installationsverzeichnis haben sollte.
Ich habe ja auch nicht gesagt, dass dort ein schreibender Zugriff möglich sein soll Ja, bitte, never!!
Aber die Anwendung, die aus dem Pfad gestartet wird, sollte sehr wohl seine Application Settings laden dürfen.
Wer und wie diese geändert werden können, steht auf einem anderen Blatt.
User Settings landen natürlich im Benutzerprofil.
Zitat von LaTino
Es ist gefährlicher Unsinn, ApplicationSettings im Installationspfad zu hinterlegen.
LaTino
von welcher Art von Konfiguration reden wir hier?
Die typische app.config?
In denen nur Application-Settings stehen?
Zitat von Abt
Wer unter Windows XP oder Vista Konfigurationsdateien in das Anwendungsverzeichnis und damit in einen für den User geschützten Ordner abgelegt hat, hat es damals schon falsch gemacht.
Ach ja?
Ich bin sehr wohl der Meinung, dass eine app.config mit ApplicationSettings dort gespeichert sein sollte, wo das Programm liegt.. also meist unter C:\Programme, oder C:\Programme(x86),
(und auch nicht jeder DAU-User schreiben und etwas ändern kann)
Wo hat VS seine devenv.exe.config abgelegt??
Ein anderer Part sind User-Settings
Die gehören ganz sicher an einen Plaz, wo der User auch Schreibberechtigung hat
Hier wieder das Rad neu zu erfinden um es wieder einmal runder zu machen zeugt meistens von mangelhafter Erfahrung was generelle und grundsätzliche Lösungswege angeht
natürlich
Und nein, nach mehr als 10 Jahren damit vertraut keine mangelnde Erfahrung...
Vielleicht nur manchmal einfach (zu sehr) auf Sicherheit bedacht
Das Grundproblem ist, dass hier offensichtlich der Beschreibung nach keine Schichtentrennung erfolgt.
Es werden also Klassen zum Speichern verwendet, die UI- oder Businessmodelle darstellen.
Bzw. Businesslogik enthalten. Ja, da gebe ich Dir Recht. Das ist nicht gut.
Die DataModel-Schicht (Infrastructure, oder wie auch immer) soll also nur dumm sein. Und Daten halten. Und im besten Fall die Daten nicht einfach von "außen" modifizierbar machen.
Zitat von Abt
Ein Feld ist nur ein "dummes" Klassenmember; also eine Variable.
Eigenschaften sind jedoch Abstraktionen; Du kannst beim Setzen (oder Lesen) noch zusätzliche Dinge tun (zB Lazy Loading, Events feuern..) - was bei einem Feld nicht geht.
Das beißt sich meiner Meinung nach mit Schichtentrennung und dumme Datenklasse
@trashkid2000
Sowas macht man nicht.
Dafür gibt es, wie Th69 schon sagt, einfach Eigenschaften.
Die dann aber auch public get set sein müssen...
Deswegen lasse ich es nicht so einfach stehen.
Und ja, manchmal ist für Sicherheit auch (viel) Mehraufwand nötig.
Du könntest einen Custom-Serializer/ Deserializer erstellen, der dann aus privaten Feldern die Daten in die XML schreibt bzw. aus dieser in die privaten Felder schreibt.
also mein erster Gedanke bei Deinen Fragen unten war, dass Du mit einem ValueConverter arbeiten könntest. Das wäre dann eine Konvertierung von Width des StackPanels, in dem die Vorschläge angezeigt werden zu max 2* Width für die TextBox.
Und ansonsten würde ich bei der TextBox dann einfach das TextWrapping einschalten.
Dann sollte es passen. Nur weiß ich nicht, wegen Popup und Visual Tree, ob Du da an die Controls ohne weiteres rankommst...
Irgendwie bist du immer von oben herab unterwegs, eigentlich sehr schade für diese Forum!
Gerade fundierte Meinungen sind für dieses Forum (und im Allgemeinen) sehr wichtig !
Und ob von oben herab oder auf gleicher Augenhöhe liegt immer im Auge des Betrachters