Hosten eines Windows Media Player-Controls in einer MVVM Application

  • WPF MVVM
  • .NET (FX) 4.5–4.8

Es gibt 35 Antworten in diesem Thema. Der letzte Beitrag () ist von kafffee.

    @-Franky-
    Beispielprojekt anbei, sry bin erst jetzt dazu gekommen...

    PadreSperanza schrieb:

    erstellen sie es einfach direkt im XAML und fügen danach das WinForm-Control ein...


    Jou ich wusste dass man das auch im XAML machen kann, auch wenn ich die gezeigte Art und Weise so noch nicht gesehen hab. Mein Control hat jedoch kein UI...

    PadreSperanza schrieb:

    Aber die Klasse, die dein Modul enthalten muss (Singleton), schon

    Yep das ist klar und logisch, dass man den Inhalt des Moduls dann trotzdem instanziieren muss...


    PadreSperanza schrieb:

    Hoffe, das ist etwas verständlich erklärt


    Isses :)

    ___________________

    Thema Interfaces. Hab ich bis jetzt nur ein Mal benutzt in meiner MVVM-App - für das Anzeigen von Dialogen. Hab ich aber so einfach hingenommen und noch nicht direkt verstanden das wie und was...

    Ein eigenes Event zu erstellen das bekomm ich denke ich noch hin. Hab dieses Tutorial hier von @ErfinderDesRades gelesen:

    Alles über Events

    So nun habe ich aber eine Eigenschaft .burnState von meinem WMP. Wie bekomme ich es hin, dass diese Eigenschaft nun "überwacht" wird, so dass ich bei Änderung dann ein Event feuern kann? Ich habe ja keinen Getter und Setter, bzw. auf den kann ich nicht zugreifen, denn die Eigenschaft ist von meinem WMP. Ist ein Interface nun dazu da, dass ich da trotzdem "eingreifen" kann?
    Dateien

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „kafffee“ ()

    kafffee schrieb:

    Mein Control hat jedoch kein UI...
    Das sollte - hier bin ich mir nicht ganz sicher - aber keine Rolle spielen. Entscheidend sollte sein, denke ich, dass du dennoch dein WMP dort instanzieren und mittels x:Name auch benennen können solltest. Dass es dann nicht angezeigt werden kann, ist das eine. Sollte das als Control nicht gehen, dann darfst du es eben nicht in ein Control packen, sondern unter <Window.Resources>. Da gehören solche Resourcen dann auch hin :)

    Bzgl. deines ​.burnState: da bin ich auch gerade aus dem Stehgreif überfragt... Eine mir sinnvolle Möglichkeit wäre: Leite den WMP in einer eigenen Klasse ab und überschreibe ggf. das, was du brauchst - Was wiederum schwierig ist, wenn du nicht weißt, wie das gesetzt oder behandelt wird... Gibt es denn für das WMP irgendeine Art Doku, in der drin steht, ob das WMP-Ding irgendwelche Events bereitstellt, auf die du hören kannst? Das wäre die beste Möglichkeit, da etwas mit anzufangen.


    Bzgl Interfaces: Ein Interface ist eher so etwas wie "Katalog" an Dingen, du du einbauen musst, damit du das auch tatsächlich verwenden kannst. Ein Interface selbst ist im Normalfall keine direkte Implementierung von irgendwas, sondern ist mehr abstrakt gehalten und sagt lediglich aus, was deine Klasse können muss, oder beinhalten muss, damit du mit dieser Schnittstelle voll kompatibel bist. Und du musst sie dann entsprechend einbauen.

    Ist jetzt ein sehr abstraktes Beispiel (und vielleicht passt es nicht 100%) aber nehmen wir mal einen fahrbaren Untersatz, welcher mittels Motor angetrieben, einer Lenkstange geführt und auf Rädern aufgesetzt ist. Nun könnte man denken, dass dieser Untersatz ja ein Auto ist - Ist es aber nicht automatisch. Es könnte ja auch ein GoKart, eine Seifenkiste oder etwas arg selbstgebasteltes sein. Damit es nun aber ein vollwertiges Auto gibt, gibt es Anforderungen und Bedingungen, die dein Untersatz erfüllen muss, damit es als Kraftfahrzeug zugelassen ist und auch alle wissen, wie es das umsetzen wird.

    Hierfür schreibt zB das Gesetz vor, dass ein KFZ einen Motor braucht, mit Benzin/Diesel betrieben wird, einen Katalysator besitzt, einen Gurt, mind. 2 Achsen, eine maximal zulässige Gesamtmasse von 3,5t, Hupe, Lichter, Bremsen, Bremslichter, Nebelschlussleuchte.... und und und

    Ein Interface sehe dementsprechend auch genau so aus:

    C#-Quellcode

    1. ​public Interface IAuto
    2. {
    3. public Motor motor;
    4. public Kraftstoff kraftstoff;
    5. public Katalysator kat;
    6. private Sitzplatzanzahl sitze;
    7. ...
    8. //Methoden entsprechend so:
    9. public void BetankeAuto();
    10. public void FahreGeschwindigkeit(Geschwindigkeit geschwindigkeit);
    11. ...
    12. }


    Wenn du nun also deine Klasse schreibst und sie von Auto ableiten möchtest oder das Interface von Auto implementierne möchtest (ja, ableiten und Interface sind im Grunde gleich), bist du verpflichtet, alle in der Schnittstelle definierten Eigenschaften und Methoden zwangsläufig mit einzubauen:

    C#-Quellcode

    1. public class FahrbarerUntersatz : IAuto
    2. {
    3. public Motor motor = new VierTaktMotor;
    4. public Kraftstoff kraftstoff = Kraftstoff.Diesel;
    5. private Sitzplatzanzahl sitze = 5;
    6. ...
    7. //Methoden entsprechend so:
    8. public void BetankeAuto(){
    9. while(Zapfsäule.IstNochDrin)
    10. {
    11. Tank += Zapfsäule.Befüllung * DeltaZeit;
    12. }
    13. }
    14. ...
    15. }


    Im Interface gibst du also nur an, was alle, die sich nach deinem Interface richten möchten, implementieren müssen.
    In deiner Klasse selbst musst du aber für die Implementierung sorgen. Solltest du etwas vergessen haben, wird deine IDE dich schon darauf aufmerksam machen, dass das Interface noch ein paar Punkte bereitstellt, die zwingend auch implementiert werden müssen.

    Nur dadurch, dass du alles - wirklich alles - aus dem Interface implementierst in deiner Klasse, ist auch sichergestellt, dass du dieses Interface auch vollkommen nutzen kannst - und noch viel wichtiger: wenn Klassen mit demselben Interface miteinander kommunizieren, wissen sie, dass alle Klassen ebenfalls alles unterstützen, was dort enthalten ist.

    Ein Interface ist im Grunde nichts anderes als eine Vererbung. Da man aber zB in .NET nur einmal erben kann, bietet es sich aber an, Schnittstellen zu implementieren, wenn diese sinnvoll und wichtig sind und so schafft man - auch wenn man nicht von der Klasse direkt erbt - eine normalisierte Möglichkeit der Kommunikation
    Bezüglich des Events für die Änderung des Brennstatus hab ich tatsächlich was gefunden:

    github.com/MicrosoftDocs/win32…c/WMP/about-cd-burning.md

    Ich probier das mal einzubauen...

    In dem Artikel ist auch was über Events, die ausgelöst werden, wenn das Brennen fehlschlägt. Das sollte ich dann auch mal probieren einzubauen, könnte evtl. auch interessant werden, wenn ich den freien Speicher auf einem Rohling dann doch nicht per IMAPI2 ermitteln kann...
    Hallo

    Ich wurde auf Seite 1 markiert und muss zugeben das ich mir jetzt nicht alles durchgelesen habe.
    Was ich aber kurz loswerden will/muss ist das am Anfang des Threads eine leichte Verirrung bez. MVVM auftrat.

    Ob nun MVVM, WPF mir CodeBehind oder Winforms ist an dieser Stelle völlig schnuppe. Hast du Code zum "Brennen" der funktioniert ist es egal ob CodeBehind oder MVVM.
    MVVM ist lediglich ein Pattern, völlig egal was hier drumrum passiert.
    Und du kannst auch diverse Dinge mit Controls (ja auch Fremdcontrols wie irgendwelche COM Controls) machen. Du solltest eine manipulation der Controls nur eben nicht in deine ViewModels holen, aber deshalb gibt es ja auch die CodeBehind. CodeBehind ist nicht verboten, solange es rein die UI betrifft.

    Zum restlichen Thema (Brennen) kann ich leider nix sagen.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    @Nofear23m

    Jou danke für deine Teilnahme. Ich denke dann habe ich alles richtig gemacht. Ich wüsste jetzt eh nicht wie ich vom Viewmodel aus den Windows Media Player ansprechen soll. Die Standardcontrols wie Picturebox und so weiter wäre klar aber COM-Controls? Wäre trotzdem mal interessant wie das geht, ich habs nicht hinbekommen...

    Also hab ich mir ein Winforms-UCL gebaut, in das ich dann den Windows Media Player integriert hab und das mir dann Methoden zum Brennen und beispielsweise Properties mit dem Brennstatus bereitstellt. Dieses Control hat kein UI und soll es auch nicht haben, es geht rein um die Brennfunktion. Wie ich mein Control jetzt ohne UI mache, da bin ich ehrlich gesagt noch nicht dahinter gekommen. Mit .Visible = False wäre da gepfuscht denke ich...

    kafffee schrieb:

    Wie ich mein Control jetzt ohne UI mache, da bin ich ehrlich gesagt noch nicht dahinter gekommen.

    Wenn du ein Control instanziierst aber keinem "Container" hinzufügt wird es auch nicht gerendert.
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    @Nofear23m

    Ah ok alles klar

    Edit: @Nofear23m
    @PadreSperanza

    Bin gerade dabei mir einen Lösungsweg auszudenken, wie ich denn jetzt die Brennstatus-Property meiner Winforms-UCL-DLL an eine Property von meinem Viewmodel binde.

    Überlegung #1 (per Xaml):
    Ich füge meiner View die DLL zu und hoste sie in einem WindowsFormsHost mit Visibility = Collapsed und binde dann die Brennstatus-Property der DLL mit der Brennstatus-Property meines Viewmodels. Mal angenommen dass das überhaupt klappt, so bin ich spätestens dann in einer Sackgasse, wenn ich meinetwegen die Methode zum Brennen starten ausführen will. Weil wenn ich die DLL jetzt auch noch dem ViewModel zufügen würde und dann von da aus die Methode starte dass dann nicht klappen würde, weil ich dann zwei Instanzen der DLL haben würde, eine in der View und eine im Viewmodel... Sehe ich das richtig?

    Also führt mich das zur Überlegung #2:

    Ich füge die DLL dem Viewmodel zu, instanziiere diese und lausche auf ein Event, das ich in meinem Winforms-UCL erstellt habe, das mir dann den Brennstatus in den EventArgs übergibt. Ist das machbar so a la:
    AddHandler MeinEigenesUCL.BrennstatusUpdated, AddressOf UpdateBrennstatus

    Oder:

    Überlegung #3
    Ist es möglich die Brennstatus-Property meines Winforms-UCLs schlicht direkt an die Brennstatus-Property meines Viewmodels per Code zu binden? Und zwar so, dass wenn sich die UCL-Property ändert, automatisch die Viewmodel-Property updatet...

    Dieser Beitrag wurde bereits 8 mal editiert, zuletzt von „kafffee“ ()

    @kafffee
    Problem bei Deinem Code gefunden. Evtl. weil Du den Umgang mit COM-Interfaces nicht kennst. Du kannst bei COM-Interfaces nicht einfach so Funktionen mittendrin weglassen, weil diese evtl. nicht benötigt werden, oder die Reihenfolge der Funktionen ändern. Wenn Du Dir die Beschreibung in der MS-Doku zum Interface IDiscFormat2TrackAtOnce anschaust, steht da "The IDiscFormat2TrackAtOnce interface inherits from IDiscFormat2." Bei IDiscFormat2 steht: "The IDiscFormat2 interface inherits from the IDispatch interface." und bei IDispatch steht "The IDispatch interface inherits from the IUnknown interface." Entsprechend muss das Interface IDiscFormat2TrackAtOnce auch die Funktionen der anderen Interfaces in der richtigen Reihenfolge enthalten (außer die von IUnknown) und zwar so, wie diese in den Headerfiles beschrieben sind.

    Dein Interface IDiscFormat2TrackAtOnce in Deinem Code.
    Spoiler anzeigen

    VB.NET-Quellcode

    1. <ComImport>
    2. <InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
    3. <Guid(IID_IDiscFormat2TrackAtOnce)>
    4. Public Interface IDiscFormat2TrackAtOnce
    5. <PreserveSig> Function get_FreeSectorsOnMedia(<Out> ByRef value As Integer) As Integer
    6. <PreserveSig> Function put_Recorder(<[In], MarshalAs(UnmanagedType.Interface)> recorder As Object) As Integer
    7. <PreserveSig> Function put_ClientName(<[In], MarshalAs(UnmanagedType.BStr)> value As String) As Integer
    8. <PreserveSig> Function PrepareMedia() As Integer
    9. <PreserveSig> Function ReleaseMedia() As Integer
    10. End Interface


    Mein Interface IDiscFormat2TrackAtOnce in meinem Code.
    Spoiler anzeigen

    VB.NET-Quellcode

    1. <ComImport>
    2. <InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
    3. <Guid(IID_IDiscFormat2TrackAtOnce)>
    4. Public Interface IDiscFormat2TrackAtOnce
    5. '----==== IDispatch (nur Dummyfunktionen!) ====----
    6. <PreserveSig> Function GetTypeInfoCount() As Integer
    7. <PreserveSig> Function GetTypeInfo() As Integer
    8. <PreserveSig> Function GetIDsOfNames() As Integer
    9. <PreserveSig> Function Invoke() As Integer
    10. '----==== IDiscFormat2 ====----
    11. <PreserveSig> Function IsRecorderSupported(<[In], MarshalAs(UnmanagedType.Interface)> recorder As Object,
    12. <Out, MarshalAs(UnmanagedType.VariantBool)> ByRef value As Boolean) As Integer
    13. <PreserveSig> Function IsCurrentMediaSupported(<[In], MarshalAs(UnmanagedType.Interface)> recorder As Object,
    14. <Out, MarshalAs(UnmanagedType.VariantBool)> ByRef value As Boolean) As Integer
    15. <PreserveSig> Function get_MediaPhysicallyBlank(<Out, MarshalAs(UnmanagedType.VariantBool)> ByRef value As Boolean) As Integer
    16. <PreserveSig> Function get_MediaHeuristicallyBlank(<Out, MarshalAs(UnmanagedType.VariantBool)> ByRef value As Boolean) As Integer
    17. <PreserveSig> Function get_SupportedMediaTypes(<Out, MarshalAs(UnmanagedType.SafeArray, SafeArraySubType:=VarEnum.VT_VARIANT)> ByRef value As Object()) As Integer
    18. '----==== IDiscFormat2TrackAtOnce ====----
    19. <PreserveSig> Function PrepareMedia() As Integer
    20. <PreserveSig> Function AddAudioTrack(<[In], MarshalAs(UnmanagedType.Interface)> data As Object) As Integer
    21. <PreserveSig> Function CancelAddTrack() As Integer
    22. <PreserveSig> Function ReleaseMedia() As Integer
    23. <PreserveSig> Function SetWriteSpeed(<[In]> RequestedSectorsPerSecond As Integer,
    24. <[In], MarshalAs(UnmanagedType.VariantBool)> RotationTypeIsPureCAV As Boolean) As Integer
    25. <PreserveSig> Function put_Recorder(<[In], MarshalAs(UnmanagedType.Interface)> recorder As Object) As Integer
    26. <PreserveSig> Function get_Recorder(<Out, MarshalAs(UnmanagedType.Interface)> ByRef value As Object) As Integer
    27. <PreserveSig> Function put_BufferUnderrunFreeDisabled(<[In], MarshalAs(UnmanagedType.VariantBool)> value As Boolean) As Integer
    28. <PreserveSig> Function get_BufferUnderrunFreeDisabled(<Out, MarshalAs(UnmanagedType.VariantBool)> ByRef value As Boolean) As Integer
    29. <PreserveSig> Function get_NumberOfExistingTracks(<Out> ByRef value As Integer) As Integer
    30. <PreserveSig> Function get_TotalSectorsOnMedia(<Out> ByRef value As Integer) As Integer
    31. <PreserveSig> Function get_FreeSectorsOnMedia(<Out> ByRef value As Integer) As Integer
    32. <PreserveSig> Function get_UsedSectorsOnMedia(<Out> ByRef value As Integer) As Integer
    33. <PreserveSig> Function put_DoNotFinalizeMedia(<[In], MarshalAs(UnmanagedType.VariantBool)> value As Boolean) As Integer
    34. <PreserveSig> Function get_DoNotFinalizeMedia(<Out, MarshalAs(UnmanagedType.VariantBool)> ByRef value As Boolean) As Integer
    35. <PreserveSig> Function get_ExpectedTableOfContents(<Out, MarshalAs(UnmanagedType.SafeArray, SafeArraySubType:=VarEnum.VT_VARIANT)> ByRef value As Object()) As Integer
    36. <PreserveSig> Function get_CurrentPhysicalMediaType(<Out> ByRef value As IMAPI_MEDIA_PHYSICAL_TYPE) As Integer
    37. <PreserveSig> Function put_ClientName(<[In], MarshalAs(UnmanagedType.BStr)> value As String) As Integer
    38. <PreserveSig> Function get_ClientName(<Out, MarshalAs(UnmanagedType.BStr)> ByRef value As String) As Integer
    39. <PreserveSig> Function get_RequestedWriteSpeed(<Out> ByRef value As Integer) As Integer
    40. <PreserveSig> Function get_RequestedRotationTypeIsPureCAV(<Out, MarshalAs(UnmanagedType.VariantBool)> ByRef value As Boolean) As Integer
    41. <PreserveSig> Function get_CurrentWriteSpeed(<Out> ByRef value As Integer) As Integer
    42. <PreserveSig> Function get_CurrentRotationTypeIsPureCAV(<Out, MarshalAs(UnmanagedType.VariantBool)> ByRef value As Boolean) As Integer
    43. <PreserveSig> Function get_SupportedWriteSpeeds(<Out, MarshalAs(UnmanagedType.SafeArray, SafeArraySubType:=VarEnum.VT_VARIANT)> ByRef supportedSpeeds As Object()) As Integer
    44. <PreserveSig> Function get_SupportedWriteSpeedDescriptors(<Out, MarshalAs(UnmanagedType.SafeArray, SafeArraySubType:=VarEnum.VT_VARIANT)> ByRef supportedSpeedDescriptors As Object()) As Integer
    45. End Interface

    Mfg -Franky-

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „-Franky-“ ()

    So, n'Abend alllerseits :)

    ich muss an dieser Stelle nochmal nachhaken. Situation ist folgende:

    Ich hab ein Winforms-Control geschrieben, das von einer Picturebox erbt.

    Jetzt möchte ich dieses in meiner MVVM-App verwenden, komme aber über einen bestimmten Punkt nicht raus:

    Klar, dem ViewModel nen Verweis zufügen, und eine Instanz davon erzeugen. Doch wie bekomme ich genau diese Instanz in meine View? Denn das Control soll mir ja was auf meinem UI anzeigen...

    Ich habe unter anderem Folgendes versucht:

    (1) Als erstes hab ichs mal andersrum versucht und das Control in die View eingebaut und versucht die Properties an mein ViewModel zu binden:

    XML-Quellcode

    1. <WindowsFormsHost>
    2. <view:Wellenform x:Name="MeinUIControl" Width="300" Height="100" Quelldatei="{Binding WFQuellDatei}"/>
    3. </WindowsFormsHost>


    Kommt dann aber der Fehler:
    "Binding" kann nicht für die Eigenschaft "Quelldatei" vom Typ "Wellenform" festgelegt werden. "Binding" kann nur für eine "Dependency Property" eines "Dependency Object" festgelegt werden.

    Heisst das, dass in dem Falle Binding partout nicht möglich ist oder kann man das Control irgendwie zu einem "Dependency Object" machen?

    (2) Da es die View betrifft, hab ich das Ganze auch mal über Code Behind versucht:

    VB.NET-Quellcode

    1. Class MainWindow
    2. Private host As New System.Windows.Forms.Integration.WindowsFormsHost()
    3. Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs)
    4. host.Child = ViewModel.MainViewModel.MeineWellenForm
    5. End Sub
    6. End Class


    und in meinem ViewModel:

    VB.NET-Quellcode

    1. Private _MeineWEllenForm As MP3WaveForm.Wellenform
    2. Public Property MeineWellenForm As MP3WaveForm.Wellenform
    3. Get
    4. Return _MeineWEllenForm
    5. End Get
    6. Set(value As MP3WaveForm.Wellenform)
    7. _MeineWEllenForm = value
    8. RaisePropertyChanged()
    9. End Set
    10. End Property


    Kommt dann aber der Fehler:

    Der Verweis auf einen nicht freigegebenen Member erfordert einen Objektverweis in Zeile 6 des MainWindow.xaml.vb

    Ich sitz da jetzt seit Stunden dran und komm auf keinen grünen Zweig... kann mir jemand unter die Arme greifen?
    Ich fürchte, du verstehst hier etwas falsch: Nicht Wellenform - also dein Control, sondern dessen Eigenschaft Quelldatei muss als DependencyProperty hinterlegt sein, damit du auf diese binden kannst/darfst.

    Das heißt: Quelldatei muss INotifyPropertyChanged implementieren, damit du erfolgreich darauf binden kannst. Oder (bei Controls immer die bessere Variante) du fügst deinem Control als Property direkt eine DependencyProperty hinzu

    C#-Quellcode

    1. public static readonly DependencyProperty QuelldateiProperty = DependencyProperty.Register("Quelldatei", typeof(HIER_DER_TYP_VON_QUELLDATEI), typeof(Wellenform), new PropertyMetadata(...));


    Durch diese Art gibst du die Property global bekannt und alle wissen damit umzugehen.

    Du musst danach nur noch in diese Property schreiben, respektive von ihr lesen:

    C#-Quellcode

    1. public HIER_DER_TYP_VON_QUELLDATEI Quelldatei
    2. {
    3. get
    4. {
    5. (HIER_DER_TYP_VON_QUELLDATEI)return GetValue(QuelldateiProperty);
    6. }
    7. set
    8. {
    9. SetValue(QuelldateiProperty, value);
    10. }
    11. }


    Im Augenblick denke ich nämlich, dass du Quelldatei zwar als öffentlichen Member von Wellenform implementiert hast, dieses aber nicht reagiert, wenn es Änderungen erlebt - was beim Binding wichtig ist
    Jou hab ich hinbekommen... Jetzt hatte ich weiters das Problem, dass ich meine DLL-Klasse nicht von FrameworkElement erben lassen konnte, da diese ja schon von PictureBox erbt. Also hab ich meiner DLL eine weitere Klasse Eigenschaften spendiert, in die ich dann meine DependencyProperties gepackt hab.

    Jetzt krieg ich im XAML die Fehler:

    XML-Quellcode

    1. <WindowsFormsHost>
    2. <view:Wellenform x:Name="MeinUIControl" Width="300" Height="100" Eigenschaften.Quelldatei=""/>
    3. </WindowsFormsHost>


    Fehler XLS0415 Die 'Quelldatei'-Eigenschaft, die angehängt werden kann, wurde in Typ 'Eigenschaften' nicht gefunden.

    und

    Fehler XDG0008 "Eigenschaften" wird in einem Windows Presentation Foundation (WPF)-Projekt nicht unterstützt.

    Ich hoffe mal das ist bloss ein Syntaxfehler.... Weisst du mehr?
    Edit: Die Klasse mit InotifyPropertyChanged versehen vielleicht?

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „kafffee“ ()

    Wenn du das auf ein anderes Element packen möchtest, solltest du mittels AttachedProperties arbeiten. Dann kannst du das hinzufügen und einem anderen Control bereitstellen. Dazu muss Eigenschaften - du hast es ja schon mal richtig ausgelagert - alles implementieren, was eine AttachedProperty braucht.
    Ungefähr so?: (hab ich in den Microsoft Docs gefunden)

    VB.NET-Quellcode

    1. Option Strict On
    2. Public Class Eigenschaften
    3. Inherits FrameworkElement
    4. Public Shared ReadOnly QuelldateiProperty As DependencyProperty = DependencyProperty.RegisterAttached("Quelldatei", GetType(String), GetType(Eigenschaften))
    5. Public Shared Function GetQuelldatei(target As UIElement) As String
    6. 'Return CType(target.GetValue(CType(QuelldateiProperty, DependencyProperty)), String)
    7. Return CType(target.GetValue(QuelldateiProperty), String)
    8. End Function
    9. Public Shared Sub SetQuelldatei(target As UIElement, value As String)
    10. target.SetValue(QuelldateiProperty, value)
    11. End Sub
    12. Public Property Quelldatei As String
    13. Get
    14. Return CType(GetValue(QuelldateiProperty), String)
    15. End Get
    16. Set(value As String)
    17. SetValue(QuelldateiProperty, value)
    18. End Set
    19. End Property
    20. End Class


    Kommen aber immer noch die gleichen Fehler... Fehlt noch was?