Laden...

Wie kann ich im WPF an Properties meiner Klasse binden?

Letzter Beitrag vor 3 Jahren 11 Posts 959 Views
Wie kann ich im WPF an Properties meiner Klasse binden?

Hallo

Ich bin Anfänger und beschäftige mich gerade mit Binding bei WPF

Ich habe mir eine Klasse erstelle wo die Variablen drin sind.

Meine Klasse


namespace WPF_C_DB_Lernen
{
    class Binding_Klasse
    {

        public string ButtonName { get; set; }

        public void ButtonNameTest()
        {


            ButtonName = "Hallo";


        }
    }
}

mein MainWindow


namespace WPF_C_DB_Lernen
{
    /// <summary>
    /// Interaktionslogik für MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            Binding_Klasse BindingKlasse = new Binding_Klasse();
            BindingKlasse.ButtonNameTest();

          //  Button1.Content = BindingKlasse.ButtonName;


        }
    }
}

Mein WPF

<Window x:Class="WPF_C_DB_Lernen.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:local="clr-namespace:WPF_C_DB_Lernen"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Button x:Name="Button1" Content="{Binding Path=ButtonName}" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="121" Height="67"/>

    </Grid>
</Window>

Wenn ich mein Button ein x:Name gebe und per Code mit .Content = BindingKlasse.ButtonName reinschreibe dann funktioniert das. aber es sollte ja auch ohne gehen.

Was habe ich da falsch gemacht?

Gruß

Mattes

Dein ViewModel muß das INotifyPropertyChanged-Interface implementieren. Hier gibt es eine ausführlichere Erklärung dazu: [Artikel] MVVM und DataBinding

Weeks of programming can save you hours of planning

Hallo Danke für deine Antwort.

Mein Code sieht nun so aus.

Meine Klasse:


namespace WPF_C_DB_Lernen
{
    class Binding_Klasse : INotifyPropertyChanged
    {
     
        public event PropertyChangedEventHandler PropertyChanged;

        public virtual void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public string _ButtonName;

        public string ButtonName
        {
            get { return _ButtonName; }
            set
            {
                if (value != _ButtonName)
                {
                    _ButtonName = value;
                    OnPropertyChanged("ButtonName");
                }
            }
        }
        public void ButtonNameTest()
        {


            ButtonName = "Hallo";


        }
    }
}

mein MainWindow:


namespace WPF_C_DB_Lernen
{
    /// <summary>
    /// Interaktionslogik für MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            Binding_Klasse BindingKlasse = new Binding_Klasse();

            this.DataContext = BindingKlasse;
            
            BindingKlasse.ButtonNameTest();

        }
    }
}

Mein WPF:


<Window x:Class="WPF_C_DB_Lernen.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:local="clr-namespace:WPF_C_DB_Lernen"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    <Window.DataContext>
        <local:Binding_Klasse />
    </Window.DataContext>
    
    <Grid>
        <Button x:Name="Button1" Content="{Binding ButtonName}" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="121" Height="67"/>

    </Grid>
</Window>

Nun noch eine Frage, kann man auch mehrere Klassen mit DataContext machen oder nur mit 1er Klasse?

Wenn es mit mehrere geht, wie funktioniert das dann?

Gruß
Mattes

Servus,

warum definierst Du den DataContext im XAML und im Code behind?

Hallo

So habe ich es in dem Tutorial rausgelesen.

MVVM und DataBinding

Ich bin für jede Verbesserung dankbar will es ja auch richtig Lernen

Gruß

Mattes

Da steht aber: "Oder alternativ mit Hilfe von XAML".
Alternativ 😃

kann man auch mehrere Klassen mit DataContext machen oder nur mit 1er Klasse?

DataContext kann nur ein Objekt zugewiesen werden. Normalerweise hat das Fenster ein MainViewModel, und die einzelnen Unterbereiche im Fenster dann spezielle Unter-ViewModels, welche als Eigenschaften im MainViewModel liegen. Das kann beliebig verschachtelt werden.

Ansonsten würde ich empfehlen, statt OnPropertyChanged("ButtonName") besser OnPropertyChanged(nameOf(ButtonName)) zu schreiben, weil dann die Bindung nicht verloren geht, wenn du die Eigenschaft mal umbenennst.

Und deine Binding_Klasse würde ich lieber MainViewModel oder ButtonViewModel nennen, dann ist klar, welche Aufgabe die Klasse hat.

Weeks of programming can save you hours of planning

Danke für deine Antwort

Was wäre besser in XAML oder in Code Behind?

Also ist nur 1 ViewModel möglich wenn ich es Richtig verstanden habe.

Ich hätte gerne verschiedene für

ButtonViewModel
LabelViewModel
TextboxViewModel

Dazu würde noch eine Extra Klasse kommen für SQLite Abfragen.

In der Regel wird alles im XAML definiert. Im MVVM Pattern wird normalerweise nichts, aber auch gar nichts im Code behind programmiert.
Und ich fürchte, du hast ein Verständnisproblem mit den View Models. Willst Du für jedes Steuerelement ein eigenes ViewModel machen? Also auch ein CheckBoxViewModel, RadioButtonViewModel, TextBlockViewModel, DataGridViewModel, ...?
Was müssen Deine ViewModels außergewöhnliches können - außer Text anzeigen - dass man dies so wie von Dir gewünscht, definieren müßte?
Ändert sich die Ansicht?

Hallo

mein Beispiel war ein schlechtes Beispiel. Es geht nicht um die einzelne Steuerelemente.

Ich möchte eine Trennung Zwischen Sprachanzeigen, Datenbankanzeigen und Funktionsanzeigen.

z.b.

Die ein WPF-Fenster würde ich gern ein SprachViewModel, DatenbankViewModel, und ein FunktionViewModen und sogar ein FunktionKlasse.

Damit will ich mit selber einen Besseren Überlick beschaffen und nicht alles in einer ViewModel reinpacken.

Vielleicht denke ich ein bissen zu Kompliziert.

Ich sag ja immer warum einfach wenn es ja auch Kompliziert geht 😃

Gruß

Mattes

Hallo Mattes80,

DataContext kann nur ein Objekt zugewiesen werden. Normalerweise hat das Fenster ein MainViewModel, und die einzelnen Unterbereiche im Fenster dann spezielle Unter-ViewModels, welche als Eigenschaften im MainViewModel liegen. Das kann beliebig verschachtelt werden.

Nach dem Schema würde ich auch vorgehen und du könntest es dann zB so schachteln:

MainViewModel
-> SprachViewModel
-> DatenbankViewModel
-> FunktionsViewModel

Die Struktur kannst du auch in Form von verschachtelten Views wiederspiegeln und zB eine ähnliche Struktur in den Views anlegen:

MainView
-> SprachView
-> DatenbankView
-> FunktionsView

Das Hauptfenster bekommt als DataContext dann das MainViewModel und alles weitere ergibt sich rein aus den Bindings:


<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:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    <Window.DataContext>
        <local:MainViewModel></local:MainViewModel>
    </Window.DataContext>

	<Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>

        <local:SprachView Grid.Column="0" DataContext="{Binding Path=SprachViewModel}"></local:SprachView>
        <local:DatenbankView Grid.Column="1" DataContext="{Binding Path=DatenbankViewModel}"></local:DatenbankView>
        <local:FunktionsView Grid.Column="2" DataContext="{Binding Path=FunktionsViewModel}"></local:FunktionsView>

	</Grid>
</Window>

<UserControl x:Class="WpfApp1.SprachView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:wpfApp1="clr-namespace:WpfApp1"
             mc:Ignorable="d"
             d:DesignHeight="450" d:DesignWidth="800" 
             d:DataContext="{d:DesignInstance wpfApp1:SprachViewModel}">
	<Grid>
		<ListView ItemsSource="{Binding Sprachen}" />
	</Grid>
</UserControl>

und die entsprechenden ViewModels stark vereinfacht:


	public class MainViewModel
	{
		public SprachViewModel SprachViewModel { get; set; } = new SprachViewModel();
		public DatenbankViewModel DatenbankViewModel { get; set; } = new DatenbankViewModel();
		public FunktionsViewModel FunktionsViewModel { get; set; } = new FunktionsViewModel();
	}

	public class SprachViewModel
	{
		public List<string> Sprachen { get; set; } = new List<string> {"Deutsch", "Englisch"};
	}

	public class DatenbankViewModel
	{
		public List<string> Datenbanken { get; set; } = new List<string> {"DbA", "DbB"};
	}

	public class FunktionsViewModel
	{
		public List<string> Funktionen { get; set; } = new List<string> {"DoSomething", "DoSomethingElse"};
	}

Lg, XXX

Hallo danke für deine Antwort

Das sieht ja schon mal sehr interessant aus. Das werde ich gleich mal heute Abend Testen.

Gruß

Mattes