Laden...
T
trashkid2000
myCSharp.de - Member
4
Themen
156
Beiträge
Letzte Aktivität
vor 5 Jahren
Dabei seit
27.12.2010
Alter
43
Erstellt vor 5 Jahren

Hi,

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);
     }
}

Eingebunden wird dies dann wie folgt:


<Controls:CustomRibbon ItemsSource="{Binding RibbonTabItems}">
   <Controls:CustomRibbon.ApplicationMenu>
      <Controls:CustomRibbonApplicationMenu ItemsSource="{Binding ApplicationMenuItems}" FooterPaneContent="{Binding CloseApplicationFooterPaneContent}" AuxiliaryPaneContent="{Binding AuxiliaryPaneContent}"/>
   </Controls:CustomRibbon.ApplicationMenu>
</Controls:CustomRibbon>

Ein Sample-Projekt kann unter
https://drive.google.com/file/d/1d2OckuDTLwnZ86p50Keyh_67zz2eWwFC/view?usp=sharing
heruntergeladen werden.

Viel Spaß damit,
Marko

Erstellt vor 5 Jahren

Hi,

es reicht nicht, einfach nur von RadioButton zu erben und in der vererbten Klasse Methoden zu überschreiben. Denn so änderst Du nur das Verhalten.

Was Du machen möchtest, ist, dass Du das Aussehen verändern willst (ja, und das Verhalten).

Da kommst Du nicht drum rum, das ControlTemplate zu überschreiben.
Eine gute Übersicht findest Du hier: RadioButton Styles und Templates

lG, Marko

Erstellt vor 5 Jahren

Hi,

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.

Erstellt vor 5 Jahren

Hi Abt 🙂

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)

Unter .NETStandard 2.0 diese:
System.Buffers (≥ 4.4.0)
System.Security.Cryptography.Cng (≥ 4.4.0)
System.Memory (≥ 4.5.1)

Ab .NET Framework 4.6.1 gibt es keine Abhängigkeiten mehr...

Ich habe es zum Laufen gebracht, indem nun die Assembly, die für NETStandard 2.0 gedacht ist, sowie alle da abhängigen Assemblies, geladen werden.

Aber so ganz verstehen tue ich es trotzdem nicht 🤔
lG, Marko

//EDIT: Ich werde mal ein konkretes Beispiel nachstellen

Erstellt vor 5 Jahren

Hi Abt,

.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.

Die Frage ist hier also eher, ob sich jede Runtime identisch verhält.

Konkretes Beispiel?

>

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

Erstellt vor 5 Jahren

Hi,

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

Erstellt vor 5 Jahren

Ich sehe da eindeutig zu viele

KillTasks();
Erstellt vor 5 Jahren

Hallo,

also sowas ist böse:

comboBox_SampleFreq.SelectedIndex=i;

Was der User ausgewählt hat, solltest Du so belassen. Das gibt sonst nur Probleme.
Und wozu das?

Viel schlimmer ist aber noch das:


if (Gen.StandardError.Peek()>0)
{
   comboBox_SampleFreq.Items.RemoveAt(i--);
}

Was kann die "arme ComboBox" dafür, dass Fehler auftreten? Die Items sollten hier ziemlich konstant sein!

Erstellt vor 5 Jahren

Hi, das Argument bei der Methode


rand.Next(...);

verlangt einfach mal einen maxValue. Und der darf nicht kleiner als 0 sein.
Du kannst aber den Wert auch einfach umformen und dann * -1 rechnen 😉

lG, Marko

Erstellt vor 5 Jahren

Hi Abt,

vielen Dank erstmal für Deinen Beitrag 👍

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...

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 !!

Marko