Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Community
  • |
  • Diskussionsforum
MVM: Event aus UserControl mit Command binden
BJA-CH
myCSharp.de - Member



Dabei seit:
Beiträge: 59
Herkunft: Schwyz, CH

Themenstarter:

MVM: Event aus UserControl mit Command binden

beantworten | zitieren | melden

Hallo zäme
Ich habe ein UserControl, welches mit einem Event ein Zustand an das aufrufende Element (XAML) weitergibt. Eine "normal" Eventübergabe in den Code-Behind funktioniert.
Nach dem Konzept des MVVM soll aber ein Event mit Commands übergeben werden. Dies funktioniert in den Steuerelementen, z.B. bei einem Mouse-Trigger etwa wie folgt:

            <i:Interaction.Triggers>
                <i:EventTrigger EventName="MouseEnter">
                    <i:InvokeCommandAction Command="{Binding Path=MouseEventCommand}" CommandParameter="{Binding}"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>

Wenn ich dies mit meinem Event aus meinem UserControl auch tue, funktioniert nichts. Das hat vermutlich zu tun, dass ein Event und ein ICommand wohl nicht das gleiche ist.

Wie kann ich in einem UserControl ein Event bauen, damit ich diesen mit einem Command-Befehl in mein ViewModel bringen kann?

Weiss jemand hier weiter, diese Problem müsste doch schon längstens gelöst sein.
Überhaupt, ich finde auch das propangierte Konzept mit dem "Interaction.Trigger" etwas fragwürdig. Ist das wirklich das Konzept, das man heute für solch zentrale Aufgaben verwendet?
private Nachricht | Beiträge des Benutzers
Deaktiviertes Profil
myCSharp.de - Member



Dabei seit:
Beiträge: 996

beantworten | zitieren | melden

Wenn du uns mehr über das erzählst was du wirklich machen möchtest (anstatt uns nur davon zu erzählen wie du es nicht lösen kannst) könnten wir dir wohl besser weiterhelfen.
private Nachricht | Beiträge des Benutzers
ErfinderDesRades
myCSharp.de - Experte

Avatar #avatar-3151.jpg


Dabei seit:
Beiträge: 5409

beantworten | zitieren | melden

Zitat von BJA-CH
Überhaupt, ich finde auch das propangierte Konzept mit dem "Interaction.Trigger" etwas fragwürdig. Ist das wirklich das Konzept, das man heute für solch zentrale Aufgaben verwendet?
Jo, viele tun das, und finden das toll.
Manche aber auch nicht.
Ich zB. finde das so dermassen umständlich, dass ich mir lieber ein Control ins Viewmodel hole (ganz böse!! :evil: ), damit ich dessen Events behandeln kann.
Denn darum gehts ja bei der Interaction.Event-Trigger-Binding-auf-Commands: dass Events eines Controls behandelt werden.
Also den Vorteil, dass ich die Events so behandeln kann, wie ich sie brauche, erkaufe ich mir durch 2 nachteile:
  • geht nicht, wenn mehrere Controls ans selbe Viewmodel binden
  • soll wohl Probleme beim UnitTesting aufwerfen (ich habs noch nicht ersnsthaft probiert)
vlt. gibts noch mehr Nachteile - grad nicht im Kopf.
Ich wollte nur kundtun, dassichdas mit dem "fragwürdig" gut verstehe (oder gut zu verstehen glaube)
Dieser Beitrag wurde 4 mal editiert, zum letzten Mal von ErfinderDesRades am .
Der frühe Apfel fängt den Wurm.
private Nachricht | Beiträge des Benutzers
ThomasE.
myCSharp.de - Member



Dabei seit:
Beiträge: 453

beantworten | zitieren | melden

Ja, welcher WPF'ler stand noch nicht vor diesem Problem :)


Meine Lösung, derzeit nur für Window.Closing: (ist teilweise noch verbesserungswürdig...)


public static class WindowEventsBinding
    {
        #region Closing

        /// <summary>
        /// Closing Event.
        /// </summary>
        public static readonly DependencyProperty ClosingProperty = DependencyProperty.RegisterAttached( "Closing", typeof( ICommand ), typeof( WindowEventsBinding ), new UIPropertyMetadata( new PropertyChangedCallback( WindowEventsBinding.ClosingChanged ) ) );

        public static ICommand GetClosing(DependencyObject obj)
        {
            return obj.GetValue( WindowEventsBinding.ClosingProperty ) as ICommand;
        }

        public static void SetClosing(DependencyObject obj, ICommand value)
        {
            obj.SetValue( WindowEventsBinding.ClosingProperty, value );
        }

        private static void ClosingChanged( DependencyObject target, DependencyPropertyChangedEventArgs e )
        {
            Window window = target as Window;
            if (window == null) return;

            if (e.NewValue != null) window.Closing += WindowEventsBinding.Window_Closing;
            else window.Closing -= WindowEventsBinding.Window_Closing;
        }

        private static void Window_Closing( object sender, global::System.ComponentModel.CancelEventArgs e )
        {
            Window window = sender as Window;
            if (window == null) return;

            ICommand closingBinding = window.GetValue( WindowEventsBinding.ClosingProperty ) as ICommand;
            if (closingBinding == null) return;

            if (closingBinding.CanExecute( null )) closingBinding.Execute( null );
            else e.Cancel = true;
        }

        #endregion
    }

und im XAML:

<Window x:Class="[Namespace].MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:wb="clr-namespace:[Namespace];assembly=[assembly]"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        wb:WindowEventsBinding.Closing="{Binding Path=ClosingCmd}">
</Window>

Ist immer die Frage welchen Umweg man lieber macht...
Ich habe den Titel mal angepasst, so dass Suchende auch etwas damit anfangen können. EDIT: Ich sollte beim Wort "Shift" im Titel das "f" nicht vergessen...
private Nachricht | Beiträge des Benutzers
ErfinderDesRades
myCSharp.de - Experte

Avatar #avatar-3151.jpg


Dabei seit:
Beiträge: 5409

beantworten | zitieren | melden

also eine extra-Klasse, > 30 Zeilen Code, mit DependancyProperty - nur für ein popeliges Window-Closing-Event?
wie gesagt: fragwürdig.
Der frühe Apfel fängt den Wurm.
private Nachricht | Beiträge des Benutzers
ThomasE.
myCSharp.de - Member



Dabei seit:
Beiträge: 453

beantworten | zitieren | melden

Naja, für eine Einmalverwendung sicher unnötig aber wer programmiert schon so... ;)
Ich habe den Titel mal angepasst, so dass Suchende auch etwas damit anfangen können. EDIT: Ich sollte beim Wort "Shift" im Titel das "f" nicht vergessen...
private Nachricht | Beiträge des Benutzers
Deaktiviertes Profil
myCSharp.de - Member



Dabei seit:
Beiträge: 996

beantworten | zitieren | melden

@ThomasE.

Du hast da einen kleinen Bug drin


public static class WindowEventsBinding
    {
        // ...
        private static void ClosingChanged( DependencyObject target, DependencyPropertyChangedEventArgs e )
        {
            Window window = target as Window;
            if (window == null) return;

            if (e.OldValue == null && e.NewValue != null) window.Closing += WindowEventsBinding.Window_Closing;
            
            if (e.NewValue == null && e.OldValue != null) window.Closing -= WindowEventsBinding.Window_Closing;
        }
    }
Kommt wahrscheinlich nie zum Vorschein, weil du die Command-Eigenschaft nur einmal setzt.

BTW: Bei Setter/Getter könntest du noch statt DependencyObject den Typ als Window festlegen, dann kann diese Eigenschaft wirklich nur an einem Window gesetzt werden.
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Deaktiviertes Profil am .
private Nachricht | Beiträge des Benutzers
ThomasE.
myCSharp.de - Member



Dabei seit:
Beiträge: 453

beantworten | zitieren | melden

@Sir Rufo,

besten Dank für die Info, werd mir das gleich notieren!

Grüße
Ich habe den Titel mal angepasst, so dass Suchende auch etwas damit anfangen können. EDIT: Ich sollte beim Wort "Shift" im Titel das "f" nicht vergessen...
private Nachricht | Beiträge des Benutzers
mfe
myCSharp.de - Member



Dabei seit:
Beiträge: 183

beantworten | zitieren | melden

Ich finde die Variante von ThomasE. legitim sowie die Verwendung der Interactions vom Blend SDK.
<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        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:i="http://schemas.microsoft.com/expression/2010/interactivity"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d" 
        Title="MainWindow" Height="350" Width="525">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Closing">
            <i:InvokeCommandAction Command="{Binding Path=CloseCommand}" CommandParameter="{Binding}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>

Es macht durchaus Sinn für Events, bei denen man öfter ein Command ausführen will, eine Helper Klasse mit Attached Properties anzulegen. Die Helper Klasse kann man ja in mehreren Projekten wiederverwenden. Die Zeitersparnis und der Komfort zahlt sich jedenfalls aus.
Zitat
Wenn ich dies mit meinem Event aus meinem UserControl auch tue, funktioniert nichts.
Kannst du das bitte präzisieren? Wird der Command nicht ausgelöst?
Zitat
Wie kann ich in einem UserControl ein Event bauen...
RoutedEvent - https://msdn.microsoft.com/de-de/library/ms752288(v=vs.110).aspx
Zitat
, damit ich diesen mit einem Command-Befehl in mein ViewModel bringen kann?
Keine Ahnung was du damit meinst, ich kann es nur erahnen. Jedenfalls wenn du ein RoutedEvent erstellst, kannst du dieses dann im i:EventTrigger EventName="DeinCustomRoutedEvent" angeben.

PS: Passt zum Thema: https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/routed-events-overview#routing-strategies
Dieser Beitrag wurde 2 mal editiert, zum letzten Mal von mfe am .
private Nachricht | Beiträge des Benutzers
BJA-CH
myCSharp.de - Member



Dabei seit:
Beiträge: 59
Herkunft: Schwyz, CH

Themenstarter:

beantworten | zitieren | melden

Besten Dank für eure Hinweise. Tatsächlich habe ich meinen Fehler gefunden.
Ich hatte im UserControl nur ein Delagte/Event gebaut und kein RoutedEvent.
Mit dem RoutedEvent wird der Interaction EventTrigger angesprochen.

Besten Dank für eure Beiträge!
private Nachricht | Beiträge des Benutzers