Laden...

WPF Usercontrol mit DPs und eigenem Viewmodel

Erstellt von Talbot21 vor 2 Jahren Letzter Beitrag vor 2 Jahren 583 Views
T
Talbot21 Themenstarter:in
8 Beiträge seit 2021
vor 2 Jahren
WPF Usercontrol mit DPs und eigenem Viewmodel

Hallo zusammen,

ich bin relativ neu bei WPF und stehe gerade vor einem Verständnisproblem.

Was ich möchte, ist ein WPF Usercontrol zu erstellen, welches 3 Parameter als Input bekommt (Dependency Properties), z.B. von einem Window oder anderen Usercontrol, in dem es verwendet wird.

Mit den Inputparametern muss nun eine DB-Abfrage gemacht werden, die Daten zurück liefert, welche im Usercontrol angezeigt werden sollen.
Daher würde ich gerne dem Usercontrol ein eigenes Viewmodel verpassen, welches die Abfrage im passenden Model macht und die zurück kommenden Daten dann so aufbereitet, dass das UC sie anständig anzeigen kann.

Was ich explizit nicht will ist, dass jedes View, welches das UC verwendet, in seinem eigenen Viewmodel immer wieder dieselbe DB-Abfrage machen muss.

Mein Problem: Wie leite ich die Werte der DependecyProperties des Usercontrols an das Viewmodel des Usercontrols weiter, damit dieses die Abfrage machen kann?

Danke.

5.658 Beiträge seit 2006
vor 2 Jahren

DependencyProperties und DB-Abfragen haben im ViewModel nichts zu suchen. Hier gibt es zwei Artikel dazu, die dir weiterhelfen könnten:
[Artikel] MVVM und DataBinding
[Artikel] Drei-Schichten-Architektur

Weeks of programming can save you hours of planning

A
764 Beiträge seit 2007
vor 2 Jahren

Hallo Talbot21

Das ViewModel bekommt Properties, die an die DependencyProperies des UserControls gebunden werden. Ganz normales Vorgehen.

Was hast du denn schon probiert?

Viele Grüße
Alf

T
Talbot21 Themenstarter:in
8 Beiträge seit 2021
vor 2 Jahren

Hallo Alf Ator,

mir ist klar, wie ich Werte aus dem Viewmodel des aufrufenden Views an die Dependency Property des Usercontrols binde, aber nicht wie diese Werte dann vom UserControl ins Viewmodel des Usercontrols kommen. Ich könnte die im Codebehind des Usercontrols zwar direkt im Viewmodel setzen, aber das scheint mir der falsche Weg zu sein, oder?

Hier mal ein Minimalbeispiel:

Aufrufende View:


<Window x:Class="MyNamespace.MyWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        ... />
       <Grid>
              <local:MyDisplayControl Filter1={Binding Datum} Filter2={Binding Username} Filter3 = {Binding EntryType} />
       </Grid>
</Window>

UserControl:


<UserControl x:Class="MyNamespace.MyDisplayControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             ...
             d:DataContext="{d:DesignInstance Type=local:DisplayControlViewModel, IsDesignTimeCreatable=True}"
             d:DesignHeight="200" d:DesignWidth="600">
    <Grid>
            <ContentPresenter>
                ...
            </ContentPresenter>
    </Grid>
</UserControl>

UserControl.xaml.cs:


namespace MyNamespace
{
    /// <summary>
    /// l
    /// </summary>
    public partial class MyDisplayControl: UserControl
    {
        public static readonly DependencyProperty Filterwert1= DependencyProperty.Register("Filter1", typeof(string), typeof(MyDisplayControl), new PropertyMetadata(string.Empty));

        public static readonly DependencyProperty Filterwert2 = DependencyProperty.Register("Filter2", typeof(string), typeof(MyDisplayControl), new PropertyMetadata(string.Empty));

        public static readonly DependencyProperty Filterwert3 = DependencyProperty.Register("Filter3", typeof(string), typeof(MyDisplayControl), new PropertyMetadata(string.Empty));

        public MyDisplayControl()
        {
            InitializeComponent();
        }

        public string Filter1
        {
            get => (string)GetValue(Filterwert1);
            set => SetValue(Filterwert1, value);
        }

        public string Filter2
        {
            get => (string)GetValue(Filterwert2);
            set => SetValue(Filterwert2, value);
        }

        public string Filter3
        {
            get => (string)GetValue(Filterwert3);
            set => SetValue(Filterwert3, value);
        }
    }
}

Usercontrol Viewmodel:


namespace MyNamespace
{
    public class MyDisplayControlViewModel : BindableBase
    {
         private string _filter1;
         private string _filter2;
         private string _filter3;

         public MyDisplayControlViewModel()
         {
         }

         public string Filter1
         {
              get  { return _filter1; }
              set  { SetProperty(ref _filter1, value); RefreshDisplay(); }
         } 
         
         public string Filter2
         {
              get  { return _filter2; }
              set  { SetProperty(ref _filter2, value); RefreshDisplay(); }
         }

         public string Filter3
         {
              get { return _filter3; }
              set { SetProperty(ref _filter3, value); RefreshDisplay(); }
         } 

         ...
    }
}

Was mir jetzt fehlt ist, wie ich die Werte aus dem Window, die an die DependencyProperties des UC gebunden werden, in das Viewmodel des Usercontrols bekomme.

A
764 Beiträge seit 2007
vor 2 Jahren

Verstehe ich das richtig, das die Auswahl für die Filter außerhalb des UserControls stattfindet?

Dann könntest du diese Lösung hier verwenden:
How to bind to a WPF dependency property when the datacontext of the page is used for other bindings?

Dafür muss das ViewModel die Instanz vom UserControl kennen.

T
Talbot21 Themenstarter:in
8 Beiträge seit 2021
vor 2 Jahren

Hallo Alf Ator,

ja, im Prinzip schon - die genannten Filterwerte ergeben sich aus verschiedensten Viewmodelwerten der aufrufenden Views, daher binde ich diese ja über Dependency Properties an das Usercontrol. Ich will das Usercontrol einfach nur auf alle möglichen Fenster klatschen können, ihm ein paar Werte übergeben und dann soll die Anzeige aktualisiert werden, ohne, dass ich noch eigene Logik in meinem Fensterviewmodel hinterlegen muss.

Hab ich da überhaupt den richtigen Ansatz oder wäre es hier richtiger, das Usercontrol über ein Datatemplate mit seinem VM zu verbinden und dann nur dieses VM im Fenster VM zu instanziieren? Aber wie würde ich dann das eigentliche Control in mein Fenster bekommen?

C
55 Beiträge seit 2020
vor 2 Jahren

Hallo,

Ich habe selbst bisher noch nie Dependency Property genutzt, auch weil ich eher selten im Codebehind unterwegs bin.
Normalerweise legt man den Datacontext im Rootelement(Window oder UserControl) fest, jedoch kann für fast jedes Element, auch eigene UserControls und Views, ein DataContext festlegen wie hier:


<Window x:Class="MyNamespace.MyWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        ... />
       <Grid>
              <local:MyDisplayControl DataContext="{Binding  ...}" />
       </Grid>
</Window>

Das Binding könnte z.B. vom View Model der aufrufenden View kommen, sprich das View Model erzeugt eine Instanz von MyDisplayControlViewModel.

Ich hoffe das dich bringt ein wenig weiter.

Grüße

T
Talbot21 Themenstarter:in
8 Beiträge seit 2021
vor 2 Jahren

Ich dachte, ich hätte es, aber irgendwie tut es nicht, wie es soll.

Im MainWindow kann ich meinem Usercontrol zwar die Eingabewerte über die DependencyProperties zuweisen, diese hat das Usercontrol auch, wenn ich es mir im Debugger anschaue, aber die Setter im Usercontrol Viewmodel werden nicht aufgerufen. Außerdem sehe ich im Debugger, dass das Usercontrol als DataContext das Viewmodel des MainWindow hat und nicht sein eigenes, ich hab aber keine Ahnung, warum das so ist 😦

C
55 Beiträge seit 2020
vor 2 Jahren

Außerdem sehe ich im Debugger, dass das Usercontrol als DataContext das Viewmodel des MainWindow hat und nicht sein eigenes, ich hab aber keine Ahnung, warum das so ist 😦

Hallo,

Du musst dem Usercontrol auch das entsprechende DataContext zuweisen, sonst nutzt das Usercontrol, den DataContext des übergeordneten UserControl bzw. Window.

Grüße