Canvas mit Scrollviewer und Dynamischen Controls

  • WPF

Es gibt 14 Antworten in diesem Thema. Der letzte Beitrag () ist von crash.

    Canvas mit Scrollviewer und Dynamischen Controls

    Guten Tag :) ,

    seit neusten versuche ich mich mit WPF und habe nun auch schon die erste Frage.
    Wie genau funktioniert der ScrollViewer in einem Canvas Control in dem dynamisch Controls erzeugt werden?
    Denn scheinbar bekommt der Scrollviewer nicht mit das das Canvas größer wird, bzw. wächst das Canvas überhaupt automatisch wenn ich ein Child hinzufüge? 8| ?(

    Habt ihr da Links, Tutorials oder Literatur zu damit ich die Grundlagen die ich scheinbar nicht verstehe aufarbeiten kann?

    Danke
    Hallo

    Ich glaube bevor ich die Fragen beantworte muss ich hier (leider) zuerst eine Gegenfrage stellen.

    Auf was willst du raus. Ich denke du hast so wie du das schreibst was ganz bestimmtes im Sinn. Das wäre interessant zu wissen, dann können wir dir sicher weiterhelfen.

    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. ##

    null Ich möchte einen Jahresplaner erstellen, unter WinForms habe ich damit meine Probleme, da meine Herangehensweise mit vielen Controls(Panel) die wohl nicht die beste war.

    Was ich bisher habe:
    Bilder
    • Jahresplan.PNG

      124,37 kB, 2.837×543, 19 mal angesehen

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

    OK, jetzt weis ich mal was du vor hast.

    Vorab, dein vorhaben ist nicht so trivial. Vorallem nicht wenn das ganze auch performand sein soll.
    Soll denn immer ein ganzes Jahr angezeigt werden? Oder soll das ganze eher selektierbar sein, also das man den Bereich wählen kann welcher angezeigt werden soll.

    Das Canvas ist hier wohl nicht das richtige Control. Wenn es dynamisch sein soll würde ich zu einem UniformGrid greifen.
    Wenn du deinen Code mal Postest (bitte genau wie Bilder auch immer mit der Forumsinternen uploadfunktion anstatt mit Hostern) können wir ja sehen wie wir das am besten auf die reihe bekommen können.
    Wichtig wäre nur genau zu wissen wann sich das ganze (siehe vorigen Absatz) wie verhalten soll.

    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. ##

    Schön wäre natürlich wenn sowohl das Ganze Jahr als auch eine Selektion angezeigt werden kann.
    Meinen bisherigen Code habe ich für das WinForm Panel Control geschrieben.

    Im Prinzip benötige ich nicht viele Funktionen, ich möchte später nach dem Markieren einzelner oder mehrerer Felder bestimmte Zustände auswählen und anzeigen lassen(Dargestellt durch Farben und Text im Feld) diese werden wiederum in einer Datenbank gespeichert.

    Das sollte ich auch programmtechnisch hinbekommen, zumindest hab ich es für WinForms :D geschafft.
    Nur die Anzeige soll halt dynamisch anhand der Anzahl der übergebenen Mitarbeitern mit deren Informationen erstellt werden.

    *EDIT Meinst du wirklich das Uniform ist das beste dafür?
    In meinem WinForm Projekt habe ich alle Controls selber Positioniert.

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

    crash schrieb:

    ich möchte später nach dem Markieren einzelner oder mehrerer Felder bestimmte Zustände auswählen und anzeigen lassen

    Das ist z.b. schon ein riesen(!!) unterscheid ob nur einzeln oder mehrere.

    crash schrieb:

    Schön wäre natürlich wenn sowohl das Ganze Jahr als auch eine Selektion angezeigt werden kann.

    Ich würde hier mit Selektionen arbeiten. Also das per Default z.b. ein Monat angezeigt wird, der User kann dann entweder vor oder zurückspringen oder einen anderen Datumsbereich wählen wobei ich generell die anzahl der angezeigten Monate begrenzen würde. Aber das bleibt dir ja dann überlassen.

    Wie gesagt, es gibt sicherlich einfacheres.
    Wie ICH vorgehen würde:

    Erstmal würde ich mir die Klassen so aufbauen das diese Datenkonform sind. Also das was du im Endeffekt dann in die DB speichern willst.
    Dann würde ich mir gedanken machen wie die ViewModels hierzu aussehen sollen. Das ganze würde ich im View dann mal versuchen mittels Uniformgrid zu lösen, das bietet sich hier an da es sehr dynamisch verwendet werden kann.

    Einfach ist sowas nicht, aber zugegeben sehr interessant.

    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. ##

    Also wenn du mir hier sagen könntest wie ich das scrollbar bekomme, würdest du mir schon riesig helfen.

    XAML

    VB.NET-Quellcode

    1. <Window x:Class="MainWindow"
    2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    6. xmlns:local="clr-namespace:WpfApp"
    7. mc:Ignorable="d"
    8. Title="MainWindow" Height="954.808" Width="1153.845">
    9. <Grid>
    10. <Button Click="Button_Click" Margin="10,10,0,889" HorizontalAlignment="Left" Width="210">Los</Button>
    11. <ScrollViewer Margin="0,40,0,0" HorizontalScrollBarVisibility="Visible">
    12. <Canvas Name="Hauptanzeige" Margin="0,40,0,0">
    13. </Canvas>
    14. </ScrollViewer>
    15. </Grid>
    16. </Window>


    MainWindow

    VB.NET-Quellcode

    1. Option Strict On
    2. Class MainWindow
    3. Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
    4. For i = 0 To 1
    5. For j = 0 To 200
    6. Dim pnl As New Canvas With {
    7. .Name = "name" & i.ToString & "_" & j.ToString,
    8. .Width = 15,
    9. .Height = 70,
    10. .Margin = New Thickness(j * .Width + j, i * .Height + i, 0, 0),
    11. .Background = Brushes.Green
    12. }
    13. AddHandler pnl.MouseDown, AddressOf ShowName
    14. Hauptanzeige.Children.Add(pnl)
    15. Next
    16. Next
    17. End Sub
    18. Private Sub ShowName(sender As Object, e As MouseButtonEventArgs)
    19. Dim tag As Canvas = CType(sender, Canvas)
    20. tag.Background = Brushes.Red
    21. 'MsgBox(tag.Name.ToString)
    22. End Sub
    23. End Class

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

    Hallo

    Ein Canvas ist hier eher ungeeignet. Zumindest aussen rum. Verwende ein Stackpanel:

    XML-Quellcode

    1. <ScrollViewer Margin="0,40,0,0" HorizontalScrollBarVisibility="Visible">
    2. <StackPanel Orientation="Horizontal" Name="Hauptanzeige">
    3. </StackPanel >
    4. </ScrollViewer>


    VB.NET-Quellcode

    1. For i = 0 To 1
    2. For j = 0 To 200
    3. Dim pnl As New Canvas With {
    4. .Name = "name" & i.ToString & "_" & j.ToString,
    5. .Width = 15,
    6. .Height = 70,
    7. .Background = Brushes.Green, .Margin = New Thickness(1)
    8. }
    9. AddHandler pnl.MouseDown, AddressOf ShowName
    10. Hauptanzeige.Children.Add(pnl)
    11. Next
    12. Next
    Dann klappt das auch mit dem Scrollen.


    Aber.... Unter WPF hast du viel mehr Luxus wenn du mit Klassen und Binding arbeitest.
    Ich habe dir mal ein schnelles rudimentäres Beispiel erstellt. Ich finde dieses übersichtlicher und es ist aalles an seinem Ort - somit gut wartbar.
    PS: Lass dich vom Projektnamen nicht verwirren, ich hatte ein vorhandenes PRojekt genommen und damit rumgespielt.

    Grüße
    Sascha
    Dateien
    • CheckboxTest.zip

      (723,53 kB, 6 mal heruntergeladen, zuletzt: )
    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. ##

    Ja, müsstest du. Ich würde aber hier wieder dynamisch mit Binding arbeiten und eine Auflistung an ein ItemsControl binden.

    Wie schon gesagt, versuche mit Binding zu arbeiten - siehe mein Beispielprojekt - sonst wirst du früher oder später an die Grenzen stoßen und spätestens dann darfst du wieder alles umwerfen. Ich will dir nur die Zeit sparen.

    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. ##

    Dann schau mal in meine Signatur. Dort ist das Tutorial zu den Videos.

    Grüße
    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 Könntest du mir vielleicht noch sagen wie ich die Zweite reihe Cluster hinbekommen?

    Ich verstehe noch nicht ganz wie ich da am besten vorgehen soll.

    Das itemControl ist ja mit Public Property Cluster As ObservableCollection(Of ClusterItem) gebindet, wenn ich dort weitere Cluster rein packe würden diese ja nur an die vorherigen angereiht.
    Nun vermute ich, das ich für jede Reihe Cluster neue Public Property Cluster As ObservableCollection(Of ClusterItem) benötige, sowie ItemsControl welches dann wiederum mit der neuen Cluster Collection gebindet wird??
    Ist das richtig oder bin ich da auf dem Holzweg? ;(
    Da hast du recht. Ich habe das Beispiel absichtlich so gewählt das es nur für eine Person geht. Mehr war auch nicht gefragt 8-)
    So ist es besser zu verstehen was da passiert. Wenn du dies nun verstanden hast und auch verstehst wie das Binding funktioniert kannst du zum nächsten schritt übergehen.

    OK, also ein Cluster stellt ja eine Person dar. Am besten wir benennen das gleich um - der Name ist von mir ja schlecht gewählt das ich allgemein halten wollte - damit wird hier nicht durcheinander kommen.
    Gut, nennen wir es mal PersonCalendar. Ein PersonCalendar stellt also eine Person und deren "Tage" dar. Also so gesehen eine "Zeile" in deiner Ansicht. Denn in der Zeile ist der Name der Person und die Tage.

    Gut. Jede Person hat X Tage. Je nachdem was selektiert/vorgegeben ist. In deinem Beispiel waren es glaube ich 200 Tage. Gut. Also hat jede Person X Tage. Super.
    Jeder Tag hat dann die Eigenschaft CurrentDate (das aktuelle Datum zu welchem dieser Tag gehört) und z.b. wie schon gehabt IsSelected.

    Gut... das Bilden wir in einem Model ab. Also in .Net Objekte:


    Wenn wir das haben können wir im MainWindows eine Eigenschaft erstellen auf welche wir dann binden.
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Private Sub MainWindow_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
    2. StartDate = DateTime.Now : EndDate = DateTime.Now.AddDays(50)
    3. InitCalendar()
    4. Me.DataContext = Me
    5. End Sub
    6. Private _calendar As ObservableCollection(Of PersonCalendar)
    7. Public Property Calendar As ObservableCollection(Of PersonCalendar)
    8. Get
    9. Return _calendar
    10. End Get
    11. Set(ByVal Value As ObservableCollection(Of PersonCalendar))
    12. _calendar = Value
    13. RaisePropertyChanged()
    14. End Set
    15. End Property
    16. Private _startDate As Date
    17. Public Property StartDate As Date
    18. Get
    19. Return _startDate
    20. End Get
    21. Set(ByVal Value As Date)
    22. _startDate = Value
    23. RaisePropertyChanged()
    24. InitCalendar()
    25. End Set
    26. End Property
    27. Private _endDate As Date
    28. Public Property EndDate As Date
    29. Get
    30. Return _endDate
    31. End Get
    32. Set(ByVal Value As Date)
    33. _endDate = Value
    34. RaisePropertyChanged()
    35. InitCalendar()
    36. End Set
    37. End Property
    38. Private Sub InitCalendar()
    39. Calendar = New ObservableCollection(Of PersonCalendar)
    40. Calendar.Add(New PersonCalendar("M. Mustermann", StartDate, EndDate))
    41. Calendar.Add(New PersonCalendar("S. Sorglos", StartDate, EndDate))
    42. End Sub


    Nun haben wir einmahl eine Auflistung vom Typ PersonCalendar und in dieser wiederum eine Auflistung vom Typ DayItem.
    Also zwei Auflistungen, infolge dessen benötigen wir im View nun auf zwei Listen.

    Einmahl hier das MainWindow:

    XML-Quellcode

    1. <DockPanel LastChildFill="True">
    2. <StackPanel Orientation="Horizontal" DockPanel.Dock="Top">
    3. <TextBlock Text="Start:" VerticalAlignment="Center"/>
    4. <DatePicker SelectedDate="{Binding StartDate}"/>
    5. <TextBlock Text="Start:" VerticalAlignment="Center" Margin="15,0,0,0"/>
    6. <DatePicker SelectedDate="{Binding EndDate}"/>
    7. </StackPanel>
    8. <ScrollViewer HorizontalScrollBarVisibility="Visible">
    9. <ItemsControl ItemsSource="{Binding Calendar}">
    10. <ItemsControl.ItemsPanel>
    11. <ItemsPanelTemplate>
    12. <StackPanel/>
    13. </ItemsPanelTemplate>
    14. </ItemsControl.ItemsPanel>
    15. <ItemsControl.ItemTemplate>
    16. <DataTemplate>
    17. <checkbox_test:uclCalendarRow/>
    18. </DataTemplate>
    19. </ItemsControl.ItemTemplate>
    20. </ItemsControl>
    21. </ScrollViewer>
    22. </DockPanel>


    Das DataTemplate habe ich ausgelagert in ein Usercontrol damit der Code übersichtlich bleibt:

    XML-Quellcode

    1. <UserControl x:Class="uclCalendarRow"
    2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    4. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    5. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    6. xmlns:local="clr-namespace:Checkbox_Test"
    7. mc:Ignorable="d" Language="de"
    8. d:DesignHeight="450" d:DesignWidth="800">
    9. <StackPanel Orientation="Horizontal">
    10. <Border BorderBrush="Black" Background="LightGray" BorderThickness="2" Width="100">
    11. <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding PersonName}"/>
    12. </Border>
    13. <ItemsControl ItemsSource="{Binding AviableDays}">
    14. <ItemsControl.ItemsPanel>
    15. <ItemsPanelTemplate>
    16. <StackPanel Orientation="Horizontal"/>
    17. </ItemsPanelTemplate>
    18. </ItemsControl.ItemsPanel>
    19. <ItemsControl.ItemTemplate>
    20. <DataTemplate>
    21. <Canvas Width="15" Height="70" Margin="1" ToolTip="{Binding CurrentDate, StringFormat=\{0:D\}}">
    22. <Canvas.Style>
    23. <Style TargetType="Canvas">
    24. <Setter Property="Background" Value="Green"/>
    25. <Style.Triggers>
    26. <DataTrigger Binding="{Binding IsSelected}" Value="True">
    27. <Setter Property="Background" Value="Red"/>
    28. </DataTrigger>
    29. </Style.Triggers>
    30. </Style>
    31. </Canvas.Style>
    32. <Canvas.InputBindings>
    33. <MouseBinding Gesture="LeftClick" Command="{Binding SelectCommand}"/>
    34. </Canvas.InputBindings>
    35. </Canvas>
    36. </DataTemplate>
    37. </ItemsControl.ItemTemplate>
    38. </ItemsControl>
    39. </StackPanel>
    40. </UserControl>




    Anbei auch das Beispielprojekt zum besseren nachvollziehen des Codes.

    Grüße
    Sascha
    Dateien
    • Test.zip

      (686,72 kB, 6 mal heruntergeladen, zuletzt: )
    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. ##