Laden...

Forenbeiträge von CSharpNewbie2022 Ingesamt 27 Beiträge

19.05.2024 - 19:49 Uhr

Hallo,

ich möchte Pushbenachrichtigungen an alle App User schicken können (Ähnlich wie Firebase) nur ohne dafür zu zahlen.

In Android erstelle ich dafür einen Foreground Service. Dieser Pollt ständig einen server und wenn eine neue Benachrichtigung vorhanden ist, dann wird diese per push benachrichtigung an den User gesendet.

Ich greife quasi mit einem Restzugriff auf eine Api und diese Liest dann die in einer Datenbank abgelegten Notifications aus (GET Zugriff). Gibt es eine bessere Möglichkeit?

In IOS klappt das irgendwie nicht oder ich finde da keine Richtige Dokumentation, wie ich dasselbe im Hintergrund machen könnte.

Habt ihr da eine Idee?

09.02.2024 - 09:29 Uhr

Also erstmal danke ich nochmal allen. Das hat mir echt weitergeholfen.

Ich habe jetzt das mit den MultiViewmodels verstanden, aber nutze doch die Messenger, weil die echt praktisch sind.

Jetzt entwickle ich alle Viewmodels unabhängig voneinander und kommuniziere bei Bedarf mit Messengern.

Jetzt habe ich eigentlcih aus dem allen nur noch 2 Fragen, die mir etwas Unklarheit geschaffen haben.

  1. Bei diesem Schichten Modell trennen wir GUI, Business Logic and Data Access. Das habe ich so adaptiert. Als Data Access wäre der Zugriff zur Datenbank und jegliche Art von Lesen und Beschreiben von Dateien. Aber wie sieht es ..
    1. mit Schnittstellen (Ethernet, Ethercat, SPI, RS232, ...)
    2. mit Webdiensten also Kommunikation zu Api s aus, gehören diese Elemente auch in den Data Access Layer?

Diese Dinger unterscheiden sich zu DB und Dateien Zugriff darin, dass mit einem externen Partner kommuniziert wird, also gehört das nun zur Business Logic oder zu dem Data Access Layer?

2. Ich nutze den DI Container von Microsoft für alle ViewModels und diverse Singleton Klassen aus der Business Logic, damit diese in der GUI Schicht zur Verfügung stehen. Aber kleine Klassen, die nur innerhalb einer Klasse verwendet werden, erstelle ich immernoch mit new. Ist das eher gegen das Konzept? Sollte jedes Object mit dem DI erstellt werden?

30.01.2024 - 22:33 Uhr

Hallo Alf,

erstmal danke dir für deine Zeit.

Ich habe mir jetzt verschiedenste Dinge angesehen und mir alle Beiträge durchgelesen (nicht alles im Detail verstanden), aber ich glaube, dass die Messenger genau das sind, was ich suche. Nichtsdestotrotz sollte es doch auch ohne gehen oder´? Ich glaube, dass deine Lösung immer noch nicht mein Anliegen löst. Daher habe ich ein weiteres Beispiel. Mit dieser Problemstellung kann ich einfach kein DI nutzen. Mit new würde es klappen, daher habe ich das Ziel Projekt ohne DI und ein unfertiges Wunschprojekt mit DI angehangen.

Also ganz kleines WPF Projekt mit 4 Grid Zeilen im MainWindow Fenster - gebunden an MainWindowViewModel:

Zeile 0: TextBlock mit Hintergrund farbe (Rot oder Grün)

Zeile 1: ListView (Gebunden an LiestViewItems property in ListViewViewModel mit UserControl ListView)

Zeile 2: Eingabe TextBox: Inhalt gebunden an TextBoxContent property im MainWindowViewModel

Zeile 3: Add und DeleteButton beide gebunden an jeweils 1 buttonCommand im MainWindowViewModel. Beide Commands manipulieren LiestViewItems property aus dem UserControl (Bis hierhin klappt es mit dem MultiViewModel). Sobald LiestViewItems property mehr als 5 Elemente hat, wird das TextBlock aus Zeile 0 (gebunden auf property im MainWindowViewModel) rot.

Mit new klappt auch das, weil ich ja beim erstellen von ListViewViewModel auch die Referenz mit This MainWindowViewModel übergeben kann. Aber mit dem DI kriege ich das nicht hin. Hat da einer eine Idee?

24.01.2024 - 23:39 Uhr

Zitat von Alf Ator

Du könntest ein Haupt-ViewModel benutzen, dass die anderen ViewModels hält und mit Daten füttert.
Ich habe dir mal ein minimales Beispiel angehängt.

Ich hatte jetzt doch lust etwas wenig zu schlafen und mich daran zu setzen^^ Dein Beispiel ist doch dahingegen anders als mein Fall, da ich mehrere Views habe, es ist ähnlich zu dem Fall bei dem Verlinkten anderen Beitrag.

Ich sehe mir das mit den Messengern mal an, aber ich verstehe die Aufteilung von MVVM wohl nicht 100%. Daher mal ein anderes Beispiel. Ich habe ein Projekt, da möchte ich in der View editierbare Textboxen haben. Der Inhalt der Textboxen stammt defaultmäßig aus xml dateien und kann in der View beschrieben und in die xml Datei geladen werden. Die Textboxen enthalten lediglich Zahlen. Die View zeigt zu den Zahlen einen Text. Text zu Zahl die Information stammt aus einer DB.

Jetzt frage ich mich ok, Text zu Zahl aus der DB ist Teil des Models/Businessmodels, Laden des Inhalts der Zahlen aus einer Datei und Speichern in eine Datei auch auch? Was passiert dann im ViewModel, werden dort nur die Klassen DB und File laden als Objekte erstellt und die Methoden genutzt + Button Commands und Alle Binding Variablen? Versuche ich soviel in Modell zu legen, wie geht? Die Methoden des Modells spucken als return values dann strings oder listen aus und diese werden im Viewmodell an die Binding variablen übergeben?

Sehe ich das so korrekt?

24.01.2024 - 23:18 Uhr

Danke an alle, dass muss ich mir am Wochenende mal im Detail ansehen.

24.01.2024 - 11:44 Uhr

Wenn die Oberservable Collection in dem ViewModel bleibt, was mache ich dann in dem eben beschriebenen Fall der 3 ViewModels?

ViewModel 1 Enthält die observable Collection, des Listviews, aber in ViewModel 2 und 3 (Für Bereiche 2 & 3) sind Controls, die diese ListView ändern. Soll und womöglich gibt es in Bereich 1 auch Elemente, die Inhalte von Bereich 2 und 3 ändern. Dann müsste ich Referenzen hin und her geben und habe diese circular dependency. Mit dem exportieren der observable Collection und aller weiteren Bindings der 3 ViewModels, kann ich von den 3 ViewModels aus jedes gebundene GUI Element verändern.

Wie würde man das denn anders implementieren?

24.01.2024 - 05:26 Uhr

Erstmal Danke für die ganzen Nachrichten

Also wenn ich das richtig verstehe...

Ich habe 3 Bereiche in der GUI. Bereich 1 enthält eine ListView. Bereich 2 enthält UI Elemente, die die ListView mit Inhalt füllen. Bereich 3 Enthält ebenfalls UI Elemente, die die Listview ergänzend mit anderem Inhalt füllen können oder komplett ersetzen können.

Es wird ein Service erstellt also zB. ListViewService, der gewisse methoden hat, die eine ListView mit dem jeweiligen Inhalt befüllen. In dem Service soll nicht die observable Collection enthalten sein. Die soll jedesmal im ViewModel aus der Liste erstellt werden? Aber so klappt das irgendwie nicht, denn, wenn die View aus Bereich 1 nur mit einer observable Collection aus dem Viewmodel 1 gebunden ist und ich in Bereich 2 ListViewService aufrufe und dort lokale Variablen ändere, merkt die View das nicht.

Daher hatte ich jetzt gedacht, dass der ListViewService die observable Collection enthält und in Bereich 1, 2 und 3 muss dieser Service nur noch injecten werden. An die View 1 aus Bereich 1 (UserControl) ist die observable Collection aus dem ListViewService gebunden. Dann ist die Manipulation der ListView abgekapselt in einer Klasse und diese wird einseitig von den Viewmodels aufgerufen. Wenn ich in Viewmodel 1, 2 oder 3 die Methoden aus ListViewService  aufrufe, dann wird die View immer angepasst.

Wäre das so sinnig?

Dann wäre meine Frage. Der ListviewService würde auch die Businesslogik enthalten. Aber die BusinessLogik sollte eigentlich nicht die observable Collection enthalten oder?

Hier bin ich etwas verwirrt.

Wäre es dann sinnvoll ein ViewModel, ein ListViewViewModelService und eine ListViewBusinseslogik zu erstellen?

Und die Klassen werden derartig injected:

ViewModel → ListViewViewModelService  → ListViewBusinseslogik

15.01.2024 - 21:44 Uhr

Hallo,

ich möchte einen Code mit einem MainWindow, MainWindowViewmodel und einem UserService schreiben. In der View gibt es eine TextBox, ein Button und ein Itemcontrol.

<Window x:Class="InversionOfControl.MainWindow"
       ...>
   <StackPanel Margin="20">
       <TextBox Text="{Binding InputName}" Margin="5"></TextBox>
       <Button Command="{Binding AddNameButton}" Margin="5">Next ViewModel</Button>
       <ItemsControl MinHeight="100" ItemsSource="{Binding Usernames}" BorderBrush="Black" BorderThickness="1" Margin="5">
           <ItemsControl.ItemTemplate>
               <DataTemplate>
                   <TextBlock Text="{Binding}"></TextBlock>
               </DataTemplate>
           </ItemsControl.ItemTemplate>
       </ItemsControl>
   </StackPanel>
</Window>

In der View kann ein Name eingetippt werden und mit einem Klick auf den Button, wird eine Methode von UserService aufgerufen, die den Namen in die ObservableCollection aus dem ViewModel speichert. Ja das geht auch ohne UserService, aber für meine eigentliche Anwendung brauche ich eine derartige Konstellation, also ein Command aus dem Viewmodel, öffnet eine Methode eines Servcie und dieser Service ändert properties im ViewModel. Oder auch ein Viewmodel wird von einem anderen ausgerufen, und das gleiche Beispiele (Hierbei ist das . Viewmodel für einen kleinen Bereich der View verantwortlich und wenn Controls in diesem Bereich genutzt werden, ändern sich auch andere Bereiche).

So wie ich es versuchen will, geht das mit "new", denn so kann ich Referenzen wild hin und her geben. Aber ich möchte etwas besser programmieren und die IoC mit DI anwenden. Aber ich glaube ich mache da prinizipiell was falsch. Daher habe ich dieselbe Methode jetzt zweimal implementiert, um von der "altbewerten" Programmierart auf die etwas modernere umzusteigen, aber ich kriege es irgendwie nicht hin (Ich erhalte immer eine Meldung: A circular dependency was detected)

Meine Klassen ohne DI:

public partial class MainWindow : Window
{
   public MainWindow()
   {
       InitializeComponent();
       this.DataContext = new MainWindowViewModel();
   }
}
    public class MainWindowViewModel : ObservableObject
   {
       public ObservableCollection<string> Usernames { get; private set; } = new ObservableCollection<string>();
       
       public RelayCommand AddNameButton { get; set; }
       
       private string _inputName;
       public string InputName
       {
           get { return _inputName; }
           set
           {
               _inputName = value;
               OnPropertyChanged();
           }
       }
       
       private UserService _userService = new UserService();
       
       public MainWindowViewModel()
       {
           AddNameButton = new RelayCommand(() =>
           {
               _userService.GetUsers(this);
           });
       }
  }
public ObservableCollection<string> Usernames { get; private set; } = new ObservableCollection<string>();
      public void GetUsers(MainWindowViewModel mainWindowViewModel)
      {
          mainWindowViewModel.Usernames.Clear();
          Usernames.Add(mainWindowViewModel.InputName);
          foreach (var username in Usernames)
          {
              mainWindowViewModel.Usernames.Add(username);
          }
      }
  }

Meine Klassen mit DI:

        public App()
       {
           Ioc.Default.ConfigureServices(
           
               new ServiceCollection()
                   .AddSingleton<MainWindow>()
                   .AddSingleton<MainWindowViewModel>()
                   .AddTransient<IUserService, UserService>()
                   
                   .BuildServiceProvider() );

           MainWindow mainWindow = Ioc.Default.GetRequiredService<MainWindow>();
           mainWindow.Show();
       }
        public MainWindow(IUserService iUserService, MainWindowViewModel mainWindowViewModel)
       {
           InitializeComponent();
           this.DataContext = mainWindowViewModel;
       }
        public ObservableCollection<string> Usernames { get; private set; } = new ObservableCollection<string>();

       public RelayCommand AddNameButton { get; set; }
       
       private string _inputName;
       public string InputName
       {
           get { return _inputName; }
           set
           {
               _inputName = value;
               OnPropertyChanged();
           }
       }

       public MainWindowViewModel(IUserService userService)
       {
           AddNameButton = new RelayCommand(() =>
           {
               userService.GetUsers();
           });
       }
   }
        public ObservableCollection<string> Usernames { get; private set; } = new ObservableCollection<string>();
       MainWindowViewModel _mainWindowViewModel;
       public UserService(MainWindowViewModel mainWindowViewModel)
       {
           _mainWindowViewModel = mainWindowViewModel;
       }
       public void GetUsers()
       {
           _mainWindowViewModel.Usernames.Clear();
           Usernames.Add(_mainWindowViewModel.InputName);
           foreach (var username in Usernames)
           {
               _mainWindowViewModel.Usernames.Add(username);
           }
       }
   }
15.01.2024 - 21:23 Uhr

Ok ich komme mit den Popups weiter. Danke

11.01.2024 - 09:13 Uhr

Hi,ich habe das Probiert. Ja der Expander überdeckt das Rechteck, aber die erste Spalte des Grids wird bei der Expansion des Expanders einfach breiter.
Eine ComboBox verhält sich beispielsweise nicht so. Sie hebt sich über das Grid hinweg. Da wäre meine Frage, ob ich das auch für einen Expander einstellen kann oder allgemein für UI Elemente.

09.01.2024 - 17:15 Uhr

Hallo,

ich möchte ein Grid aufbauen mit mehreren Zellen. In der obersten Zelle sollen Elemente derartig angeordnet sein, dass die GridZelle eine Breite und eine Spalte hat.  Beim Expandieren des Expanders aus Zeile und Spale 0 soll die Breite der Zelle sich nicht ändern. Also der Expander soll sich darüber hinweg öffnen.

Der Code unten sorgt stets dafür, dass sich beim expandieren die Zelle mit ändert.

Hat da einer eine Idee, wie man das lösen kann?

    <Grid Grid.ZIndex="0">
       <Grid.ColumnDefinitions>
           <ColumnDefinition Width="Auto"></ColumnDefinition>
           <ColumnDefinition Width="Auto"></ColumnDefinition>
           <ColumnDefinition Width="Auto"></ColumnDefinition>
       </Grid.ColumnDefinitions>
       <Rectangle Fill="LightGray" Width="200" Height="100" Grid.ZIndex="1" />
       <Ellipse Fill="LightBlue" Width="100" Height="100"  Grid.ZIndex="1"/>
       <Expander HorizontalAlignment="Left" VerticalAlignment="Top" x:Name="myExpander" Grid.ZIndex="0" ExpandDirection="Right">
           <Expander.Header>
               <TextBlock Text="Expander" Background="LightGreen"/>
           </Expander.Header>
           <TextBlock Text="Expander-Inhalt" Background="LightYellow" Width="600" Height="300"/>
       </Expander>
   </Grid>
07.12.2023 - 14:27 Uhr

Ich hab doch noch ein Problem, vllt kennt das ja einer...

ich wollte für jedes Element in der observable Collection eine Ellipse erstellen und diese verbinden. Allerdings sobald ich das Path Element hinzufügen, passiert komisches Zeug. Es liegt irgendwie damit zusammen, dass ich

                           <ItemsControl.ItemContainerStyle>
                               <Style TargetType="ContentPresenter">
                                   <Setter Property="Canvas.Left" Value="{Binding StartCornerEllipse.X}"/>
                                   <Setter Property="Canvas.Top" Value="{Binding StartCornerEllipse.Y}"/>
                               </Style>
                           </ItemsControl.ItemContainerStyle>

auf alle Elemente anwende, aber das möchte ich nicht, sondern nur auf die Ellipse und die Textbox. Geht das?

Restlicher XAML Code:

<ItemsControl ItemsSource="{Binding GraphicalObjects}">
                           <ItemsControl.ItemTemplate>
                               <DataTemplate>
                                   <Grid>
                                       <Ellipse Width="{Binding SizeEllipse.X}" Height="{Binding SizeEllipse.Y}" Fill="White" Stroke="#1cad8e" StrokeThickness="1"/>
                                       <TextBox Style="{StaticResource detailedDecsriptionDisplayTextBoxes2}" Width="{Binding SizeEllipse.X}" Height="{Binding SizeEllipse.Y}" Text="{Binding Content}" />
                                       <!-- Path Element -->
                                       <Path Stroke="Black">
                                           <Path.Data>
                                               <PathGeometry>
                                                   <PathFigure StartPoint="{Binding StartPointArrow, Converter={StaticResource Point2DToXAMLConverter}}">
                                                       <BezierSegment 
                                   Point1="{Binding IntermediatePoint1Arrow, Converter={StaticResource Point2DToXAMLConverter}}" 
                                   Point2="{Binding IntermediatePoint2Arrow, Converter={StaticResource Point2DToXAMLConverter}}" 
                                   Point3="{Binding EndPointArrow, Converter={StaticResource Point2DToXAMLConverter}}"/>
                                                   </PathFigure>
                                               </PathGeometry>
                                           </Path.Data>
                                       </Path>
                                   </Grid>
                               </DataTemplate>
                           </ItemsControl.ItemTemplate>
                           <ItemsControl.ItemsPanel>
                               <ItemsPanelTemplate>
                                   <Canvas Height="{Binding CanvasSizeY}" Width="{Binding CanvasSizeX}" HorizontalAlignment="Left"/>
                               </ItemsPanelTemplate>
                           </ItemsControl.ItemsPanel>
                           <ItemsControl.ItemContainerStyle>
                               <Style TargetType="ContentPresenter">
                                   <Setter Property="Canvas.Left" Value="{Binding StartCornerEllipse.X}"/>
                                   <Setter Property="Canvas.Top" Value="{Binding StartCornerEllipse.Y}"/>
                               </Style>
                           </ItemsControl.ItemContainerStyle>
                           <ItemsControl.Template>
                               <ControlTemplate>
                                   <ItemsPresenter />
                               </ControlTemplate>
                           </ItemsControl.Template>
                       </ItemsControl>
05.12.2023 - 16:51 Uhr

Ich musste lediglich sowohl dem Canvas als auch dem Scrollviewer feste bzw. relative Längen zuweisen, dann klappt das Danke

04.12.2023 - 21:11 Uhr

Aber das habe ich doch? Ich baue mit einem itemcontrol ein canvas so aus, dass wichtige infos wie die Koordinaten etc . Aus einer Liste stammen.

Mein Problem hierbei ist, wenn ich das Canvas mit Referenzen bearbeite also ohne itemcontrol, dann kriege ich bei einem 300 x 300 canvas eine scrollbar sobald elemente ausserhalb fieses fensters sind.

Mit der Itemcontrol variante nicht und da frage ich mich, was ich dagegen machen kann 🤔

04.12.2023 - 16:36 Uhr

Hallo,

ich habe eine Liste von Informationen und ich möchte diese grafische darstellen. Jeder String in der Liste wird zu einem Block. Die Blöcke sollen mit Pfeilen verbunden werden. Bei gewissen Begriffen, sollen verzweigungen entstehen. Z.B. Jump To 6 und irgendwo steht JumpingPoint 6, dann soll das eine Verzweigung sein.

Ich möchte das mit dem MVVM Design Pattern lösen. Ich wollte ein Canvas in Verbindung mit einem Itemcontrol verwenden. Ich habe 2 Punkte an denen ich hänge.

  1. Ich kann schonmal eine Reihe an rechtecken mit dem gewünschten Content erstellen, aber die Blöcke gehen einfach über den Bildschirmrand hinaus ohne, dass eine Scrollbar entsteht.
  2. Kann ich ItemControls in Itemcontrols erstellen, wie for schleifen in for schleifen?
Code:
              <Grid>
                   <ItemsControl ItemsSource="{Binding RectangleItems}" Margin="20" Height="400">
                   
                           <ItemsControl.ItemTemplate>
                               <DataTemplate>
                                   <Grid>
                                       <Rectangle Width="{Binding Width}" Height="{Binding Height}" Fill="White" Stroke="#1cad8e" StrokeThickness="1" />
                                       <TextBlock Style="{StaticResource detailedDecsriptionDisplayTextBlocks}" Text="{Binding Content}" Margin="5"/>
                                   </Grid>
                               </DataTemplate>
                           </ItemsControl.ItemTemplate>
                           
                           <ItemsControl.ItemsPanel>
                               <ItemsPanelTemplate>
                               <Grid>
                                   <Canvas> 
                                   </Canvas>
                               </Grid>
                               </ItemsPanelTemplate>
                           </ItemsControl.ItemsPanel>
                           
                           <ItemsControl.Template>
                               <ControlTemplate>
                                   <ItemsPresenter />
                               </ControlTemplate>
                           </ItemsControl.Template>
                           
                           <ItemsControl.ItemContainerStyle>
                               <Style TargetType="ContentPresenter">
                                   <Setter Property="Canvas.Left" Value="{Binding X}"/>
                                   <Setter Property="Canvas.Top" Value="{Binding Y}"/>
                               </Style>
                           </ItemsControl.ItemContainerStyle>
                           
                   </ItemsControl>
               </Grid>
19.11.2023 - 16:52 Uhr

Blonder Hans, danke für die Mühe das hat mir beim DI Container geholfen. Ich hatte ein paar Sachebn einfach falsch verstanden...

19.11.2023 - 16:49 Uhr

So ich bin zurückgerudert, habe jetzt alles in den Viewmodels, keine doppelte zuweisung zwischen view und viewmodel, DI Container verwendet und kann seiten navigieren mit variablenübergabe.

Es war wirklich nicht so schwer, aber all das neu gelernte hat mich gut verwirrt ...

Danke an alle!

18.11.2023 - 19:42 Uhr

Page1.XAML:

<UserControl x:Class="_22_Page_Navigation.View.Page1"
             .... 
             Background="DarkRed">

    <UserControl.Resources>

        <Style TargetType="TextBox">
            <Setter Property="Background" Value="Beige"></Setter>
            <Setter Property="Height" Value="30"></Setter>
            <Setter Property="Width" Value="200"></Setter>
            <Setter Property="Margin" Value="10"></Setter>
            <Setter Property="BorderBrush" Value="Black"></Setter>
            <Setter Property="BorderThickness" Value="1"></Setter>
            <Setter Property="VerticalContentAlignment" Value="Center"></Setter>
            <Setter Property="HorizontalContentAlignment" Value="Center"></Setter>
        </Style>
     </UserControl.Resources>

        <!--<UserControl.DataContext>
        <viewModel:Page1ViewModel></viewModel:Page1ViewModel>
    </UserControl.DataContext>-->
    <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
        <TextBox Text="{Binding Id}"></TextBox>
        <TextBox Text="{Binding FirstName}" ></TextBox>
        <TextBox Text="{Binding LastName}"  ></TextBox>
    </StackPanel>
</UserControl>

Page1.XAML.cs:

namespace _22_Page_Navigation.View
{
   public partial class Page1 : UserControl
   {
       public static readonly DependencyProperty IdProperty =
       DependencyProperty.Register(
       nameof(Page1.Id),
       typeof(int),
       typeof(Page1),
       new FrameworkPropertyMetadata(default(int), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
       
       public static readonly DependencyProperty FirstNameProperty =
           DependencyProperty.Register(
               nameof(Page1.FirstName),
               typeof(string),
               typeof(Page1),
               new FrameworkPropertyMetadata(default(string), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
               
       public static readonly DependencyProperty LastNameProperty =
           DependencyProperty.Register(
               nameof(Page1.LastName),
               typeof(string),
               typeof(Page1),
               new FrameworkPropertyMetadata(default(string), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

       public int Id
       {
           get => (int)GetValue(IdProperty);
           set => SetValue(IdProperty, value);
       }
       
       public string FirstName
       {
           get => (string)GetValue(FirstNameProperty);
           set => SetValue(FirstNameProperty, value);
       }
       
       public string LastName
       {
           get => (string)GetValue(LastNameProperty);
           set => SetValue(LastNameProperty, value);
       }

       public Page1()
       {
           InitializeComponent();
       }
   }
}

Page1ViewModel.cs: Leer, nicht verwendet..

Alle Weiteren Fenster identisch..

Kann man das optimieren?

18.11.2023 - 19:39 Uhr

Ich habe jetzt einen Ansatz, damit klappt es, aber ich habe 3 Probleme damit:

  1. Die Variablen der Pages sind in der XAML.cs Datei, statt dem ViewModel, hat irgendwie nicht geklappt, wegen dem Dependency Code ( Zeige ich an der Stelle in Rot)
  2. Der Dependency Code ist rech aufwendig, aber da kann man bestimmt nichts machen.
  3. Ich habe die UserControls übereinander und blende die nicht notwendigen nur aus, ist das so ok?

Wenn einem eine Optimierung einfällt bitte melden:

MainWindow.XAML:

<Window x:Class="_22_Page_Navigation.View.MainWindow"
       ...
       
   <Window.DataContext>
       <viewModel:MainWindowViewModel></viewModel:MainWindowViewModel>
   </Window.DataContext
   
   <Window.Resources>

        <Style TargetType="Button">
            <Setter Property="Height" Value="50"></Setter>
            <Setter Property="Width" Value="100"></Setter>
            <Setter Property="Margin" Value="10"></Setter>
        </Style>
   
       <DataTemplate x:Key="Page1">
           <view:Page1 />
       </DataTemplate>
       
       <DataTemplate x:Key="Page2">
           <view:Page2 />
       </DataTemplate>
       
   </Window.Resources>
   
   <Grid>
       <Grid.ColumnDefinitions></Grid.ColumnDefinitions>
       <Grid.RowDefinitions>
           <RowDefinition Height="20"></RowDefinition>
           <RowDefinition Height="70"></RowDefinition>
           <RowDefinition></RowDefinition>
       </Grid.RowDefinitions>
       
       <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center">
           <Button Content="Page 1" Command="{Binding NavigationCommand}" CommandParameter="{x:Static model:PageIdentification.Page1}"/>
           <Button Content="Page 2" Command="{Binding NavigationCommand}" CommandParameter="{x:Static model:PageIdentification.Page2}"/>
           <Button Content="Page 3" Command="{Binding NavigationCommand}" CommandParameter="{x:Static model:PageIdentification.Page3}"/>
       </StackPanel>
       
       <ContentControl Grid.Row="2" Visibility="{Binding Page1Visibility}">
           <local:Page1 Id="{Binding Id}" FirstName="{Binding FirstName}" LastName="{Binding LastName}" />
       </ContentControl>
       
       <ContentControl Grid.Row="2"  Visibility="{Binding Page2Visibility}">
           <local:Page2 Id="{Binding Id}" FirstName="{Binding FirstName}" LastName="{Binding LastName}" />
       </ContentControl>
       
       <ContentControl Grid.Row="2"  Visibility="{Binding Page3Visibility}">
           <local:Page3 Id="{Binding Id}" FirstName="{Binding FirstName}" LastName="{Binding LastName}" />
       </ContentControl>
   </Grid>
</Window>      

MainWindow.XAML.cs: Leer

MainWindowViewModel.XAML:

namespace _22_Page_Navigation.ViewModel
{
   public class MainWindowViewModel:BaseViewModel
   {
       private Visibility page1Visibility;
       public Visibility Page1Visibility
       {
           get => page1Visibility;
           set
           {
               if (page1Visibility != value)
               {
                   page1Visibility = value;
                   this.RaisedPropertyChanged();
               }
           }
       }
       
       private Visibility page2Visibility;
       public Visibility Page2Visibility
       {
           get => page2Visibility;
           set
           {
               if (page2Visibility != value)
               {
                   page2Visibility = value;
                   this.RaisedPropertyChanged();
               }
           }
       }
       
       private Visibility page3Visibility;
       public Visibility Page3Visibility
       {
           get => page3Visibility;
           set
           {
               if (page3Visibility != value)
               {
                   page3Visibility = value;
                   this.RaisedPropertyChanged();
               }
           }
       }

       public Page1ViewModel Page1ViewModel { get; set; }
       public Page2ViewModel Page2ViewModel { get; set; }
       public Page3ViewModel Page3ViewModel { get; set; }
       private int id;
       public int Id
       {
           get => id;
           set
           {
               if (id != value)
               {
                   id = value;
                   this.RaisedPropertyChanged();
               }
           }
       }
       private string firstName;
       public string FirstName
       {
           get => firstName;
           set
           {
               if (firstName != value)
               {
                   firstName = value;
                   this.RaisedPropertyChanged();
               }
           }
       }
       private string lastName;
       public string LastName
       {
           get => lastName;
           set
           {
               if (lastName != value)
               {
                   lastName = value;
                   this.RaisedPropertyChanged();
               }
           }
       }


       public DelegateCommand NavigationCommand { get; set; }

       public MainWindowViewModel()
       {
           NavigationCommand = new DelegateCommand(
               (o) => NavigationThroughPages(o));
               
           Page1ViewModel = new Page1ViewModel();
           Page2ViewModel = new Page2ViewModel();
           Page3ViewModel = new Page3ViewModel();

           Id =1;
           FirstName = "FirstNameAusPage1";
           LastName = "LastNameAusPage1";
           
           Page1Visibility = Visibility.Visible;
           Page2Visibility = Visibility.Collapsed;
           Page3Visibility = Visibility.Collapsed;
       }
       
       void NavigationThroughPages(object o)
       {
           switch ((PageIdentification)o)
           {
               case PageIdentification.Page1:
               
                   Id = 1;
                   FirstName = "FirstNameAusPage1";
                   LastName = "LastNameAusPage1";
                   
                   Page1Visibility = Visibility.Visible;
                   Page2Visibility = Visibility.Hidden;
                   Page3Visibility = Visibility.Hidden;
                   
                   break;
                   
               case PageIdentification.Page2:
               
                   Id = 2;
                   FirstName = "FirstNameAusPage2";
                   LastName = "LastNameAusPage2";
                   
                   Page1Visibility = Visibility.Hidden;
                   Page2Visibility = Visibility.Visible;
                   Page3Visibility = Visibility.Hidden;
                   
                   break;
                   
               case PageIdentification.Page3:
               
                   Id = 3;
                   FirstName = "FirstNameAusPage3";
                   LastName = "LastNameAusPage3";
                   
                   Page1Visibility = Visibility.Hidden;
                   Page2Visibility = Visibility.Visible;
                   Page3Visibility = Visibility.Hidden;
                   
                   break;
           }
       }
   }
}
18.11.2023 - 17:55 Uhr

So ich habe jetzt geschaut und gesehen, dass ich den DataContext mehrmals gesetzt habe.

Jetzt klappt es mit

        <ContentControl Grid.Row="2" Content="{StaticResource local:Page1}">
           <local:Page1 FirstName="{Binding FirstName}" />
       </ContentControl>

Aber ich möchte ja auch die Pages wechseln und dann geht das irgendwie nicht. Die Pages wechseln kann ich so

        <ContentControl Grid.Row="2">
           <ContentControl.Style>
               <Style TargetType="ContentControl">
                   <Style.Triggers>
                       <DataTrigger 
                           Binding="{Binding CurrentPage, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}"
                           Value="{x:Static model:PageIdentification.Page1}">
                           <Setter Property="ContentTemplate" Value="{StaticResource Page1}"></Setter>
                       </DataTrigger>
                   </Style.Triggers>
               </Style>
           </ContentControl.Style>
       </ContentControl> 

Je nach CurrentPage ändert sich nun die Seite, aber ich schaff es einfach nicht jetzt wieder Die Variable FirstName zu übergeben.

Danke für die Hilfe.

18.11.2023 - 10:30 Uhr

Blonder Hans:

Der button ist im MainWindow. Also der Navigier Button. Im Prinzip sollen die Attribute der Pages mit einem Button Click aktualisiert werden.

In dem Fall öffne ich Page ein, bitte aktualisiere FirstName aus Page1.

18.11.2023 - 10:28 Uhr

Ich möchte das ändern von ViewModel durch das MainWindow initiieren, also muss ich das Attribit aus dem MainWindow mit den anderen Attributen irgendwie verknüpfen oder?

18.11.2023 - 10:25 Uhr

Das Community Toolkit funktionicht komplett, weil ich .net 4.7.2 nutze und demnach ein altes Cshsrp, da meckert der compiler bei den Relay Commands, aber das ist ja ich sag mal ‚nur‘ eine Optimierung.

Bei dem Delegate Command, das habe ich erstellt, da kannte ich mich mit den Lambda Expressions nicht so gut aus, aber will das noch optimieren.

Mein Wunsch ist es, sobald ich die Page öffne, sollen informationen aus dem Window und aus einer externen Klasse (entweder statsich oder DI Container) auf dem Fenster angezeigt werden, aber das klappt nicht.

18.11.2023 - 02:16 Uhr

Also erstmal Danke für eure Hilfe. Ich poste hier mal, was ich vorhabe.

Ich habe Ein Window mit einer beliebigen Anzahl an Pages... Der Einfachheitshalber 2. Die Page besteht aus einer "Custome Navigation Bar" und einem Content Control. Mit einem Klick auf Buttons ändert sich der Content des Content Controls. Soweit so gut. Aber ich möchte, dass mit einem Klick auf einen weiteren Button oder des NavigationsButtons, Inhalt auf den User Controls in dem Content Control ändern.

Hier die Dateien:

MainWindow.XAML:

<Window x:Class="_22_Page_Navigation.View.MainWindow"
       xmlns:local="clr-namespace:_22_Page_Navigation.View"
       xmlns:viewModel="clr-namespace:_22_Page_Navigation.ViewModel">
   <Window.DataContext>
       <viewModel:MainWindowViewModel></viewModel:MainWindowViewModel>
   </Window.DataContext>
   <StackPanel>
       <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center">
           <Button Height="50" Width="100 " Content="Page 1" Command="{Binding NavigationCommand}" CommandParameter="Page 1" Margin="10"></Button>
           <Button Height="50" Width="100" Content="Page 2" Command="{Binding NavigationCommand}" CommandParameter="Page 2" Margin="10"></Button>
       </StackPanel>
       <ContentControl Content="{Binding CurrentViewModel}" Grid.Row="2"></ContentControl>
    </StackPanel>   
</Window>

MainWindow.XAML.cs: Ohne Änderung

MainWindowViewModel.cs:

using WPFBasics;
using _22_Page_Navigation.Model;

namespace _22_Page_Navigation.ViewModel
{
   public class MainWindowViewModel:BaseViewModel
   {
       public Page1ViewModel Page1ViewModel { get; set; }
       public Page2ViewModel Page2ViewModel { get; set; }
       private BaseViewModel currentViewModel;
       
       public BaseViewModel CurrentViewModel
       {
           get => currentViewModel;
           set
           {
               if (currentViewModel != value)
               {
                   currentViewModel = value;
                   this.RaisedPropertyChanged();
               }
           }
       }
       
       private string firstName;
       public string FirstName
       {
           get => firstName;
           set
           {
               if (firstName != value)
               {
                   firstName = value;
                   this.RaisedPropertyChanged();
               }
           }
       }



       public DelegateCommand NavigationCommand { get; set; }

       public MainWindowViewModel()
       {
           NavigationCommand = new DelegateCommand(
               (o) => NavigationThroughPages(o));

           Page1ViewModel = new Page1ViewModel();
           Page2ViewModel = new Page2ViewModel();
           CurrentViewModel = Page1ViewModel;
           UserRepository UserRepository = new UserRepository();
           List<User> users = UserRepository.GetUserList();

       }
       void NavigationThroughPages(object o)
       {
           switch (o.ToString())
           {
               case "Page 1":
                   CurrentViewModel = Page1ViewModel;
                   FirstName = "Max";
                   break;
               case "Page 2":
                   CurrentViewModel = Page2ViewModel;
                   FirstName = users[1].FirstName ;
                   break;
           }
       }
   }
}

Page1.XAML:

<UserControl x:Class="_22_Page_Navigation.View.Page1"
            xmlns:local="clr-namespace:_22_Page_Navigation.View"
            xmlns:viewModel="clr-namespace:_22_Page_Navigation.ViewModel"
			Background="DarkRed">
   <UserControl.DataContext>
       <viewModel:Page1ViewModel></viewModel:Page1ViewModel>
   </UserControl.DataContext>
       <TextBox Text="{Binding FirstName}"></TextBox>
</UserControl>

Page1.XAML.cs: Ohne Änderung

Page1ViewModel.cs:

using WPFBasics;
namespace _22_Page_Navigation.ViewModel
{
   public class Page1ViewModel: BaseViewModel
   {
       private string firstName;
       public string FirstName
       {
           get => firstName;
           set
           {
               if (firstName != value)
               {
                   firstName = value;
                   this.RaisedPropertyChanged();
               }
           }
       }
       
       public Page1ViewModel()
       {
           FirstName = "-";
       }
   }
}

... Page 2 ist identisch, aber mit einer anderen Farbe.

Notwendige Klassen in WPFBasic:

NotifyableBaseObject.cs:


using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace WPFBasics
{
   public class BaseViewModel: INotifyPropertyChanged
   {

       public event PropertyChangedEventHandler PropertyChanged;

       protected virtual void RaisedPropertyChanged([CallerMemberName] string _propertyName = "")
       {
           if (!string.IsNullOrEmpty(_propertyName))
           {
               this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(_propertyName));
           }
       }
   }
}

DelegateCommand .cs:

using System;
using System.Windows.Input;
namespace WPFBasics
{
   public class DelegateCommand : ICommand
   {
       public event EventHandler CanExecuteChanged;
       private Action<object> ExecuteAction { get; set; }
       private Predicate<object> CanExecutePredicate { get; set; }

       public DelegateCommand(Predicate<object> _canExecutePredicate, Action<object> _executeAction)
       {
           ExecuteAction = _executeAction;
           CanExecutePredicate = _canExecutePredicate;
       }
       public DelegateCommand(Action<object> _executeAction) : this(null, _executeAction) { }

       public bool CanExecute(object _object)
       {
           if (CanExecutePredicate != null)
           {
              return CanExecutePredicate.Invoke(_object);
           }
           else
           {
               return true;
           }
       }
       public void Execute(object _object)
       {
           
           if (ExecuteAction != null)
           {
               ExecuteAction?.Invoke(_object);
           }
          
       }

       public void RaiseCanExecuteChanged()
       {
           CanExecuteChanged?.Invoke(this, EventArgs.Empty);
       }
   }
}
17.11.2023 - 08:10 Uhr

Hallo, erstmal danke ich dir für deinen hilfreichen Beitrag. Ich bin schon um einiges weiter, aber merke, dass ich etwas grundlegendes nicht verstanden habe. Ich habe das MVVM Pattern angewandt und auch die Dependency Injektion und wollte mein Projekt verbessern. Allerdings habe ich das Problem, dass ich auf einem Window mehrere Seiten hintereinander öffne und auf irgendeine Weise navigiere ich von Seite 1 auf Seite 2. Jetzt ist mein Problem, dass ich einen Button auf dem Window drücken möchte und je nachdem, was gedrückt ist, soll etwas in der page passieren. Der Seite soll Inhalt aus einer Klasse gegeben werden. Aber ich kriege das irgendwie nicht hin. Die Textboxen sollen nur gefüllt werden, wenn ich den Button drücke oder wenn ich andere Buttons drücke. Also eine User Interaction ist der Trigger. Ich habe mit dem MVVM Community Toolkit es geschafft messages zwischen dem Fenster und der Seite zu versenden, aber es funktioniert nicht zuverlässig und beim Start überhaupt nicht oder total zeitversetzt, was könnte man da machen?

08.11.2023 - 13:50 Uhr

Ich meinte aus 4 Pages entschuldige.

Ich seh mir das mVVM Pattern mal a Wochenende an. Hast du auch einen Vorschlag wie ich Buttons und Textblöcke dynamisch erzeugen kann? Ich mache das über eine Schleife.

Allerdings möchte ich eine Tabelle mit 75x8 Elementen (wobei die letzte Spalte aus Buttons besteht) bauen und dann laggt das ersetzen der Inhalte stark. Hast du da eine Idee?

08.11.2023 - 01:37 Uhr

Hallo,

Ich bin recht neu in der GUI Programmiereung in C# und arbeite mich in WPF ein. Ich habe eine grundsätzliche Frage. Und zwar, wenn man Ein Window bestehen aus 4 Fenstern bauen möchte und für jedes Fenster noch eine zusätzliche Klasse mit GUi Elementen (als Referenz übergeben) und Methoden baut ist das gut strukturiert?

Ich bin auf das Problem gestoßen, dass die Dui Elemente (Attribute) und Methoden teilweise auch in der 2., und 3. Klasse benötigt werden und andersrum. Ich müsste alle Klassen wild ineinander übergeben, weil es immer dieselben Daten seien sollen und nicht mehrere Instanzen von Klasse 1, 2, 3 und 4 geben sollen.

Geht das auch eleganter?