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
Eigenschaften des Model aus dynamisch generiertem ViewModel zugreifen
torka
myCSharp.de - Member



Dabei seit:
Beiträge: 8

Themenstarter:

Eigenschaften des Model aus dynamisch generiertem ViewModel zugreifen

beantworten | zitieren | melden

Hallo,

ich versuche gerade eine Applikation zu bauen, in der aus einer ObservableCollection von Models dynamisch per ItemsContol template die zugehörigen ViewModels geladen werden. Das binding der Daten aus den Objekten der ObservableCollection zum XAML funktioniert soweit, die richtigen Einträge werden dargestellt. Was mir jedoch nicht gelingt ist das Ausführen von Methoden auf dem Objekt des zugehörigen ViewModels, bspw. wenn man einen Button in einem VM klickt.
Ich hoffe ich konnte mein Problem ausreichend beschreiben sowie, dass jemand mir helfen kann, per google/Forensuche hatte ich keinen Erfolg - vielleicht sind es auch die falschen Begriffe

Gruß
private Nachricht | Beiträge des Benutzers
KroaX
myCSharp.de - Member

Avatar #avatar-4080.jpg


Dabei seit:
Beiträge: 315
Herkunft: Köln

beantworten | zitieren | melden

Hast du auf deinen Item ViewModels einen Command den du ausführen könntest? Mir ist nicht ganz klar was du bisher versucht hast.
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 15702
Herkunft: BW

beantworten | zitieren | melden

Erklärt doch mal insgesamt was Dein Ziel ist.
Evtl. ist Dein Vorgehen schon grundlegend suboptimal, wonach es riecht.
private Nachricht | Beiträge des Benutzers
MarsStein
myCSharp.de - Experte

Avatar #avatar-3191.gif


Dabei seit:
Beiträge: 3429
Herkunft: Trier -> München

beantworten | zitieren | melden

Hallo,
Zitat
in der aus einer ObservableCollection von Models dynamisch per ItemsContol template die zugehörigen ViewModels geladen werden.
Klingt seltsam. Gänging wäre eher eine ObservableCollection von ViewModels, und die View wird dann automatisch per ItemsControl.ItemTemplate erstellt.

Wenn Deine ObservableCollection tatsächlich Models enthält, und Du dann sagst:
Zitat
Das binding der Daten aus den Objekten der ObservableCollection zum XAML funktioniert soweit,
Dann klingt das danach, dass Du die Models direkt ans zum XAML bindest. Wo bleiben dann die ViewModels?

Kannst Du mal zeigen, was Du da eigentlich machst?

Gruß, MarsSTein
Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca
private Nachricht | Beiträge des Benutzers
torka
myCSharp.de - Member



Dabei seit:
Beiträge: 8

Themenstarter:

beantworten | zitieren | melden

Mein Ziel ist, eine Liste von ViewModels in meiner MainView in einem Dock zu erzeugen, die physischen Netzwerkgeräten folgt. Jedes Gerät soll dynamisch sein ViewModel bekommen, das ViewModel soll in der Lage sein, Funktionen auf dem betreffenden Viewmodel für ein Gerät auszuführen. Um die Eigenschaften eines Gerätes zu organisieren habe ich ein Model erstellt, mit Eigenschaften wie IP, Verbindung, etc.
In meinem MainViewModel baue ich dann die ObservableCollection<Gerät> Geräte und binde dann in das XAML die Collection wie folgt:
  
<Grid>
        <avalonDock:DockingManager Name="dockingManager" Loaded="OnDockManagerLoaded">
            <avalonDock:LayoutRoot>
                <avalonDock:LayoutPanel Orientation="Horizontal" CanRepositionItems="False">
                    <avalonDock:LayoutAnchorablePane DockMinWidth="500" 
                                                        DockWidth="500" 
                                                        CanRepositionItems="False" >
                        <avalonDock:LayoutAnchorable x:Name="dockGeräte" 
                                                        Title="Geräte" 
                                                        AutoHideMinHeight="500"
                                                        AutoHideMinWidth="495"
                                                        CanClose="False" 
                                                        CanDockAsTabbedDocument="False" 
                                                        CanFloat="False" 
                                                        CanHide="False">
                            <ScrollViewer VerticalScrollBarVisibility="Visible">
                                <ItemsControl ItemsSource="{Binding Geräte}"  Width="460">
                                    <ItemsControl.ItemTemplate>
                                        <DataTemplate>
                                            <local:Gerät Margin="5"/>
                                        </DataTemplate>
                                    </ItemsControl.ItemTemplate>
                                </ItemsControl>
                            </ScrollViewer>
                        </avalonDock:LayoutAnchorable>
                    </avalonDock:LayoutAnchorablePane>
                    <avalonDock:LayoutDocumentPane ShowHeader="False">
                        <avalonDock:LayoutDocument CanClose="False"
                                                    CanFloat="False"
                                                    IsMaximized="True">
                            <!-- Hier kommen dann weitere (auswählbare) control-ViewModels für die Geräte rein, welche aus dem Ribbon für ein ausgewähltes Gerät bestimmte Funktionen ausführen (bspw. Kommunikation aneigen) -->
                        </avalonDock:LayoutDocument>
                    </avalonDock:LayoutDocumentPane>
                </avalonDock:LayoutPanel>
            </avalonDock:LayoutRoot>
        </avalonDock:DockingManager>
    </Grid>

Gebundene Funktionen auf die ViewModels der Geräte funktionieren, ich kann jedoch nicht die Eigenschaften des Models abrufen, dessen Button auf dem ViewModel geklickt wurde.


Ich hoffe, das war etwas verständlicher, vielen Dank schon mal für Eure Antworten.
private Nachricht | Beiträge des Benutzers
KroaX
myCSharp.de - Member

Avatar #avatar-4080.jpg


Dabei seit:
Beiträge: 315
Herkunft: Köln

beantworten | zitieren | melden

Zitat
ich kann jedoch nicht die Eigenschaften des Models abrufen, dessen Button auf dem ViewModel geklickt wurde.

Das musst du näher ausführen. Was ist dein Model ( Gerät? ). Wie willst du die Eigenschaften abrufen?

EIn Button wird nicht auf einem ViewModel geklickt. Du hast vielleicht einen Command in deinem ViewModel welcher per Binding auf einen Button in deinem DataTemplate gebunden ist.

Dein DataTemplate besteht hier jedoch nur aus einem Gerät Control. Das Control müsste man mal sehen.

Ich hoffe der Typ Gerät aus ObservableCollection<Gerät> entspricht nicht demselben Typ Gerät deines Controls ( local:Gerät ) ansonsten hast du etwas missverstanden bei MVVM Konzept.
private Nachricht | Beiträge des Benutzers
torka
myCSharp.de - Member



Dabei seit:
Beiträge: 8

Themenstarter:

beantworten | zitieren | melden

Mein Model Gerät ist eine Klasse, die von der Klasse ModelBase (ich verwende catel) erbt. Da hab ich Eigenschaften des Gerätes drin, bspw.


    public class Gerät : ModelBase
    {
        #region Properties
        /// <summary>
        /// IP-Address of the device
        /// </summary>
        public IPAddress IP
        {
            get { return GetValue<IPAddress>(IPProperty); }
            set { SetValue(IPProperty, value); }
        }
        public static readonly PropertyData IPProperty = RegisterProperty(nameof(IP), typeof(IPAddress), null);

Der Typ aus der ObservableCollection<Gerät> ist das Model, also ObservableCollection<Models.Gerät>, das Control das in dem DataTemplate steckt ist ein Viewmodel mit ebenfalls dem Namen "Gerät". Dort hab ich Commands die im ViewModel Gerät drin stehen und bei Tastendruck ausgeführt werden. Ich verstehe nur nicht, wie ich jetzt Daten aus dem Model in das Viewmodel bekomme. Wo ist mein Denkfehler?

Was ich eigentlich wollte:
Eine ObservableCollection die ich schön speichern kann, organisiert in den Models (das sollte doch eigentlich richtig so sein) und ein ViewModel, welches sich an den Objekten in der Collection bedient und die Eigenschaften darstellt.
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von torka am .
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 15702
Herkunft: BW

beantworten | zitieren | melden

Wenn Du schon unbedingt in Deutsch Quellcode schreibst, was ohnehin nicht schön ist, lass wenigstens die Umlaute weg.
Umlaute werden prinzipiell unterstützt (in C#) - machen trotzdem an einigen Stellen hin und wieder Probleme.
private Nachricht | Beiträge des Benutzers
torka
myCSharp.de - Member



Dabei seit:
Beiträge: 8

Themenstarter:

beantworten | zitieren | melden

Jep, wird geändert
private Nachricht | Beiträge des Benutzers
MarsStein
myCSharp.de - Experte

Avatar #avatar-3191.gif


Dabei seit:
Beiträge: 3429
Herkunft: Trier -> München

beantworten | zitieren | melden

Hallo,
Zitat
Wo ist mein Denkfehler?
Hier:
Zitat
das Control das in dem DataTemplate steckt ist ein Viewmodel
Ein Control ist niemals ein ViewModel, sondern eine View.
Das Objekt, das Du per Binding mit dieser View verbindest, ist ein ViewModel - also das, was Du hier als Model bezeichnest.

Schau Dir nochmal ein paar Artikel zu MVVM an, z.B. [Artikel] MVVM und DataBinding , hier liegt offenbar noch ein grundsätzliches Verständnisproblem vor.

Gruß, MarsStein
Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca
private Nachricht | Beiträge des Benutzers
torka
myCSharp.de - Member



Dabei seit:
Beiträge: 8

Themenstarter:

beantworten | zitieren | melden

Ja, es ist ein View. Das ViewModel heißt DeviceViewModel, die View' Device' (vorher Gerät). Aber ist denn mein Ansatz mit der ObservableCollection richtig?
private Nachricht | Beiträge des Benutzers
torka
myCSharp.de - Member



Dabei seit:
Beiträge: 8

Themenstarter:

beantworten | zitieren | melden

Ich hab noch mal drüber nachgedacht und glaube, dass es vom Prinzip her richtig aufgebaut ist. Hier mal etwas ausführlicher die Struktur:

MainViewModel.cs


    public class MainViewModel : ViewModelBase
    {
        private readonly ICommandManager _commandManager;
        private readonly IMessageService _messageService;
        private readonly ISensorsService _sensorsService;
        private readonly IUIVisualizerService _uiVisualizerService;
        public MainViewModel(ICommandManager commandManager, IMessageService messageService, ISensorsService sensorsService, IUIVisualizerService uiVisualizerService)
        {
            // Services
            Argument.IsNotNull(() => commandManager);
            _commandManager = commandManager;
            Argument.IsNotNull(() => sensorsService);
            _sensorsService = sensorsService; 
            Argument.IsNotNull(() => uiVisualizerService);
            _uiVisualizerService = uiVisualizerService;
            Argument.IsNotNull(() => messageService);
            _messageService = messageService;
            // Commands            
           }       
        public ObservableCollection<Models.Device> Devices
        {
            get { return GetValue<ObservableCollection<Models.Device>>(DevicesProperty); }
            set { SetValue(DevicesProperty, value); }
        }
        public static readonly PropertyData DevicesProperty = RegisterProperty(nameof(Sensors), typeof(ObservableCollection<Models.Device>), null);

MainView.xaml

   <Grid>
        <avalonDock:DockingManager Name="dockingManager" Loaded="OnDockManagerLoaded">
            <avalonDock:LayoutRoot>
                <avalonDock:LayoutPanel Orientation="Horizontal" CanRepositionItems="False">
                    <avalonDock:LayoutAnchorablePane DockMinWidth="500" 
                                                        DockWidth="500" 
                                                        CanRepositionItems="False" >
                        <avalonDock:LayoutAnchorable x:Name="dockDevices" 
                                                        Title="Geräte" 
                                                        AutoHideMinHeight="500"
                                                        AutoHideMinWidth="495"
                                                        CanClose="False" 
                                                        CanDockAsTabbedDocument="False" 
                                                        CanFloat="False" 
                                                        CanHide="False">
                            <ScrollViewer VerticalScrollBarVisibility="Visible">
                                <ItemsControl ItemsSource="{Binding Devices}"  Width="460">
                                    <ItemsControl.ItemTemplate>
                                        <DataTemplate>
                                            <local:Device Margin="5"/>
                                        </DataTemplate>
                                    </ItemsControl.ItemTemplate>
                                </ItemsControl>
                            </ScrollViewer>
                        </avalonDock:LayoutAnchorable>
                    </avalonDock:LayoutAnchorablePane>
                    <avalonDock:LayoutDocumentPane ShowHeader="False">
                        <avalonDock:LayoutDocument CanClose="False"
                                                    CanFloat="False"
                                                    IsMaximized="True">
                            <!-- Hier kommen dann weitere control-ViewModels für die Geräte rein-->
                        </avalonDock:LayoutDocument>
                    </avalonDock:LayoutDocumentPane>
                </avalonDock:LayoutPanel>
            </avalonDock:LayoutRoot>
        </avalonDock:DockingManager>
    </Grid>


Device.xaml

<Grid>
	<Border BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Left" Height="233" VerticalAlignment="Top" Width="450" CornerRadius="10" Background="Transparent">
		<Grid x:Name="deviceGrid" Background="Transparent">			
			<Border x:Name="topBorder" Background="{StaticResource CiDarkGrey}" CornerRadius="10,10,0,0">
				<Grid>
					<Grid.ColumnDefinitions>
						<ColumnDefinition Width="*"/>
						<ColumnDefinition Width="30"/>
					</Grid.ColumnDefinitions>
					<TextBlock Grid.Column="0" Foreground="White"
						   HorizontalAlignment="Center"
						   VerticalAlignment="Top"                          
						   Height="25">
						<!-- Header row text -->
						<Run Text="Device: Serial Number"/>
						<Run Text="{Binding SerialNumber}"/>
						<Run Text=" - ("/>
						<Run Text="{Binding Path=IP, Converter={StaticResource IpAddressToString}}"/>
						<Run Text=")"/>
					</TextBlock>
					<!-- Close Button-->
					<Button Grid.Column="1" Margin="-15,-15"  Width="20" Height="20"  Background="{StaticResource CiLightGrey}" Command="{Binding RemoveDevice}">
						<TextBlock FontWeight="UltraBold" Text="&#xE106;" FontFamily="Segoe MDL2 Assets" FontSize="10" />
						<Button.Resources>
							<Style TargetType="Border">
								<Setter Property="CornerRadius" Value="10"/>
							</Style>
						</Button.Resources>
					</Button>
				</Grid>
			</Border>
		</Grid>
	</Border>
</Grid>

DeviceViewModel.cs


public class DeviceViewModel : ViewModelBase
{
	#region Public Properties
	/// <summary>
	/// device serial number
	/// </summary>
	public String SerialNumber { get; set; }
	/// <summary>
	/// device IP
	/// </summary>
	public String IP { get; set; }
	#endregion
	#region Commands
	/// <summary>
	/// Remove device
	/// </summary>
	public ICommand RemoveDevice { get; set; }
	#endregion
	#region Constructor
	public DeviceViewModel()
	{            
		RemoveDevice = new TaskCommand(RemoveDevice);
	}

	private async Task RemoveDevice)
	{
		System.Windows.Forms.MessageBox.Show("RemoveDevice aufgerufen");
	}
 }



Der RemoveDevice command wird aufgerufen, das binding funktioniert in dieser Richtung. Was mir (und meinem Verständnis noch) fehlt ist das Binding auf die Eigenschaften aus der ObesservableCollection, die im MainViewModel steckt. Es ist auf jeden Fall schon mal so, dass mir in meiner ItemsControl zwei Device-Views angezeigt werden, wenn die OC auch zwei Einträge hat. Was schief läuft ist wohl das binding. Bin ich da auf nem Holzweg?
private Nachricht | Beiträge des Benutzers
MrSparkle
myCSharp.de - Team

Avatar #avatar-2159.gif


Dabei seit:
Beiträge: 5963
Herkunft: Leipzig

beantworten | zitieren | melden

Die Antwort hat dir doch MarsStein schon im ersten Post gegeben. Und wenn das nicht verständlich ist, schau mal in den Artikel, der dir verlinkt wurde. Da gibt es Beispiel-Code und ein Beispiel-Projekt.
Weeks of programming can save you hours of planning
private Nachricht | Beiträge des Benutzers
KroaX
myCSharp.de - Member

Avatar #avatar-4080.jpg


Dabei seit:
Beiträge: 315
Herkunft: Köln

beantworten | zitieren | melden

Warum möchtest du in der ObservableCollection denn unbedingt Models haben?

Wäre es nicht logischer deine DeviceViewModels in dieser Collection zu halten und jedes DeviceViewModel ist der Container für ein Model? Damit sollte dein Problem der Hierarchie doch einfach lösbar sein?
private Nachricht | Beiträge des Benutzers
torka
myCSharp.de - Member



Dabei seit:
Beiträge: 8

Themenstarter:

beantworten | zitieren | melden

Meine Idee war, dass ich dann bei Programmstart und -ende die Devices besser als XML mit einem XMLSerializer laden bzw. speichern kann. Ist das ein sinnvolles Konzept?
Ich bin relativ unerfahren in der Richtung, hatte bisher nur schnell mal die ein oder andere C#-Applikation zusammengetippt, das hier ist mein erstes MVVM-Konzept.
private Nachricht | Beiträge des Benutzers
MrSparkle
myCSharp.de - Team

Avatar #avatar-2159.gif


Dabei seit:
Beiträge: 5963
Herkunft: Leipzig

beantworten | zitieren | melden

Zitat von torka
Ist das ein sinnvolles Konzept?

Du siehst doch selbst, daß es nicht funktioniert :)
Daher, nein. Die ViewModels haben eine Funktion, und die Models haben eine andere Funktion, und der Sinn der Schichtentrennung ist es, diese beiden Verantwortlichkeiten voneinander zu entkoppeln. Verwende daher in der View die ViewModels, um die Daten anzuzeigen und zu bearbeiten, und die Models beim Datenzugriff, um sie zu laden und zu speichern.

Hier gibt es ausführlichere Infos dazu:
[Artikel] Drei-Schichten-Architektur
[Artikel] MVVM und DataBinding
Weeks of programming can save you hours of planning
private Nachricht | Beiträge des Benutzers
torka
myCSharp.de - Member



Dabei seit:
Beiträge: 8

Themenstarter:

beantworten | zitieren | melden

Genau so mache ich es doch - kam wohl anders rüber.
private Nachricht | Beiträge des Benutzers
FZelle
myCSharp.de - Experte



Dabei seit:
Beiträge: 10072

beantworten | zitieren | melden

Nein, Du benutzt EIN ViewModel um alle Models zu verwalten und anzuzeigen.

Du sollst eine ObservableCollection<DeviceViewModel> benutzen.

Natürlich kann dein MainViewModel eine List<Model> beinhalten, die Du am ende dann serialisieren kannst.
Und ganz bestimmt kein Static benutzen
private Nachricht | Beiträge des Benutzers