Laden...

Kompliziertes Template Problem

Erstellt von Felsen vor 14 Jahren Letzter Beitrag vor 14 Jahren 2.502 Views
Felsen Themenstarter:in
53 Beiträge seit 2009
vor 14 Jahren
Kompliziertes Template Problem

Hallo zusammen,
ich habe wieder ein Problem. Nach unzähligen Selbstversuchen hab ich jetzt aufgegeben und hoffe jemand von euch kann mir helfen, oder wenigstens den entscheidenden Denkanstoß geben. Mein Problem ist etwas komplexer, ich habe deswegen noch eine Skizze angefertigt und sie mit angehängt, trotzdem versuche ich es erst einmal mit Worten zu beschreiben:

Meine Hauptklasse enthält viele Daten, die alle grafisch dargestellt werden sollen. Dazu gehören ein paar Eigenschaften wie Name etc, und vor allem viele Kindelemente. Die Kindelemente können in zwei verschiedenen Gruppen auftauchen. Diese Gruppen habe ich mit List<> Instanzen umgesetzt, damit ich sie dynamisch jederzeit erweitern kann. In diesen beiden Gruppen (List<> liste1, List<> liste2) haben ich viele Instanzen der Klasse Objekt (nicht zu verwechseln mit Object, habe sie nur der Einfachheit halber mal abstrakt Objekt genannt). Für diese Objekte habe ich bereits ein Template erstellt. Wenn ich also z.B. ein einziges solches Objekt einem Canvas hinzufüge, wird es schon richtig angezeigt. Mein Problem ist jetzt ein Template für das ganze Objekt Klasse zu erstellen, das dann selbstständig irgendwie zwei "Panels" für die beiden Listen erstellt, die jeweils auch ein Design bekommen sollen, und letztenendes dann ihre Instanzen von Objekten da drin darstellen.

  • Das war jetzt alles etwas verwirrend, seht euch am besten das Diagramm an, da hab ich links gezeigt wie es grafisch aussehen soll, und rechts wie die Klassenstruktur ist.

Mein Ansatz ging von einem Einfachen ContentControl aus. Nur scheint dieses nicht geeignet zu sein für mehrere Daten. Dann habe ich ein DataTemplate versucht, das ich aber leider nur in Kombination mit einem ItemsControl verwenden kann, also hat das auch nicht recht funktioniert. Ich weiß nicht wie ich weiter machen soll ...

Ich bitte um Hilfe. Danke.

Mfg Felsen

5.742 Beiträge seit 2007
vor 14 Jahren

Hallo Felsen,

Mein Ansatz ging von einem Einfachen ContentControl aus. Nur scheint dieses nicht geeignet zu sein für mehrere Daten. Dann habe ich ein DataTemplate versucht, das ich aber leider nur in Kombination mit einem ItemsControl verwenden kann, also hat das auch nicht recht funktioniert.

Wenn ich das Problem richtig verstanden habe, hast du dir die Antwort schon selbst gegeben: Verwende zwei ItemsControls in deinem DataTemplate.

Schemenhaft also so:


<DataTemplate TargetType="{x:Type ...}">
   <!-- Code der allgemeines anzeigt -->
   <ItemsControl ItemSource="{Binding Path=Liste1}">
        <!-- DataTemplates, ItemsPanelTemplate etc. -->
   </ItemsControl>
</DataTemplate>

Felsen Themenstarter:in
53 Beiträge seit 2009
vor 14 Jahren

Hi WinSharp93,
danke für die Antwort. Du hast richtig gelegen. Ich hab mich so drauf fixiert irgendeine fertige Klasse zu nehmen die von ItemsControl ableitet, dass ich ganz vergessen hab, dass ich doch die Klasse selbst nehmen könnte ...

Hab dann zwischenzeitlich etwas rumexperimentiert und konnte auch kleine Erfolge verzeichnen, jedoch bin ich jetzt wieder in ner Sackgasse, und ich verstehe einfach nicht was an meinem Code nicht stimm. Vielleicht kannst du ja mal einen kurzen Blick drauf werfen:


<Window x:Class="Template_Test.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Test="clr-namespace:Template_Test"
    Title="Window1" Height="300" Width="300">
    <StackPanel>
        <StackPanel.Resources>
            <DataTemplate DataType="{x:Type Test:Objekt}">
                <Border Background="Beige" CornerRadius="8" Height="30" Margin="10">
                    <Label Content="{Binding ObjektName}"></Label>
                </Border>
            </DataTemplate>
        </StackPanel.Resources>
        <ItemsControl Background="Black">
            <ItemsControl.ItemTemplate>
                <DataTemplate DataType="{x:Type Test:Klasse}">
                    <Border Background="green" Height="100">
                        <StackPanel>
                            <Label Content="{Binding KlasseName}"></Label>
                            <ItemsControl ItemsSource="{Binding liste1}">                          
                            </ItemsControl>
                        </StackPanel>
                    </Border>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <Test:Klasse></Test:Klasse>
        </ItemsControl>
    </StackPanel>
</Window>

Wie gesagt, das Objekt wird bereits richtig formatiert dargestellt, wenn ich es ganz allein darstelle. Aber irgendwie findet er die Objekt Instanzen in liste1 nicht richtig und stellt deswegen auch nichts dar. Das Binding an KlasseName funktioniert übrigens hervorragend, der Name wird im Design richtig angezeigt, nur unter dem Name sollten eben dann die einzelnen Objekte angezeigt werden, von denen aber keines zu sehen ist. (Die Klassen Instanz ist übrigens auch richtig initialisiert, alle Variablen enthalten ihre Daten, das habe ich bereits überprüft)

Mfg Felsen

5.742 Beiträge seit 2007
vor 14 Jahren

Prüfe mal das Output-Fenster, vielleicht steht da was drin.

Ist liste1 auch tatsächlich public?

Felsen Themenstarter:in
53 Beiträge seit 2009
vor 14 Jahren

Keine Meldungen im Outputfenster. Hier mal der Code meiner CodeBehind Datei:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Template_Test
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
            
        }
    }

    public class Klasse
    {
        public string KlasseName { get; set; }

        public List<Objekt> liste1 = new List<Objekt>();
        public List<Objekt> liste2 = new List<Objekt>();
        public List<Objekt> liste3 = new List<Objekt>();

        public Klasse()
        {
            KlasseName = "Test...";

            for (int i = 0; i < 5; i++)
            {
                liste1.Add(new Objekt());
                liste2.Add(new Objekt());
                liste3.Add(new Objekt());
            }
        }
    }

    public class Objekt
    {
        public static int count = 0;
        public string ObjektName { get; set; }

        public Objekt()
        {
            ObjektName = String.Format("String{0}",count++);
        }
    }
}


Mfg Felsen

EDIT: @ Briefkasten, kleier Fehler beim abtippen, nun ausgebessert, daran lag es aber nicht.

446 Beiträge seit 2004
vor 14 Jahren

Hallo,

mach aus der Membervariable liste1 eine Propertie. Dann sollte es gehen.

Ich nehm mal an dass Net() der Konstruktor der Klasse Klasse ist und den falschen Namen hat. So sollte es nicht compelierfähig sein.

Schaut mal im IRC vorbei:
Server: https://libera.chat/ ##chsarp

Felsen Themenstarter:in
53 Beiträge seit 2009
vor 14 Jahren

Hi Briefkasten,
ja der Konstruktor war falsch abgetippt, hatte vorher noch andere Bezeichner. Da jetzt eigene Dependency Properties ins Spiel kommen übersteigt das etwas meinen bisherigen Erfahrungshorizont. Ich habs trotzdem mal nach bestem Wissen angepasst, jedoch wird beim Kompilieren eine XAMLParseException geworfen.

Hier mal soweit der angepasste Code:


    public class Klasse: DependencyObject
    {
       ...

        public List<Objekt> Liste1
        {
            get { return (List<Objekt>)GetValue(Liste1Property); }
            set { SetValue(Liste1Property, value); }
        }

        public static readonly DependencyProperty Liste1Property =
            DependencyProperty.Register("Liste1", typeof(List<Objekt>), typeof(Klasse));

        ...

        public Klasse()
        {
            ...

            for (int i = 0; i < 5; i++)
            {
                ...
                Liste1.Add(new Objekt());
            }
        }

Mfg Felsen

446 Beiträge seit 2004
vor 14 Jahren

Hi,

eine DependecyPropertie benötigst du in diesem Fall nicht. Eine normale tut es auch.

public  ObservableCollection<Objekt> liste1 { get; set; }

Und nicht vergessen die Propertie im Konstruktor zu initialisieren.

Schaut mal im IRC vorbei:
Server: https://libera.chat/ ##chsarp

Felsen Themenstarter:in
53 Beiträge seit 2009
vor 14 Jahren

OK, der Code sieht nun so aus:


    public class Klasse: DependencyObject
    {
        ...

        public ObservableCollection<Objekt> liste1;

        public Klasse()
        {
            liste1 = new ObservableCollection<Objekt>();

            for (int i = 0; i < 5; i++)
            {
                liste1.Add(new Objekt());
            }
        }
    }

Bedauerlicherweise hat sich nichts geändert. Aber es gibt trotzdem noch was zu berichten. Bei diesem Beispiel zeigt der Designer nicht das aktuelle Fenster an, sondern eine Fehlermeldung, dass das Dokument einen Fehler enthält. Trotz alle dem lässt es sich kompilieren und ausführen. Als Fehler wird folgender Code unterringelt:

            <DataTemplate DataType="{x:Type Test:Objekt}">
                <Border Background="Beige" CornerRadius="8" Height="30" Margin="10">
                    <Label Content="{Binding ObjektName}"></Label>
                </Border>
            </DataTemplate>

Die Fehlermeldung lautet:

Der öffentliche Typ mit dem Namen "Objekt" kann von dem Typverweis nicht gefunden werden.

Welche Rolle dieser Fehler genau spielt weiß ich nit, da erstens sich das Projekt ja kompilieren lässt, und zweitens, das in den Resourcen abgelegte Template auch wunderbar auf einzelne Objekt Instanzen im XAML Code anwendbar wäre.

Weiß jemand trotzdem noch nen Rat?

Mfg Felsen

446 Beiträge seit 2004
vor 14 Jahren

Hallo,

laut dem gepostetem Code hast du auch nach wie vor eine Membervariable und keine öffentliche Propertie.

Schaut mal im IRC vorbei:
Server: https://libera.chat/ ##chsarp

Felsen Themenstarter:in
53 Beiträge seit 2009
vor 14 Jahren

Das glaub ich jetzt nicht...
Wenn ich der Property tatsächlich { get; set; } anfüge funktionierts ... Aber ich verstehe nicht wieso. Diese beiden Methoden sind doch nur eine Art Wrapper um die eigentliche Property. Die Zuweisung funktioniert doch in beiden fällen Identisch, es wird letztenendes nur auf eine Membervariable zugegriffen. Dass es für XAML da einen Unterschied gibt hätte ich nie gedacht...

Man o man, aber aus sowas lernt man, das werd ich bestimmt nicht nochmal falsch machen. Danke für eure Geduld und Hilfe. Wirklich vielen Dank.

Schöne Grüße

Felsen

446 Beiträge seit 2004
vor 14 Jahren

Hallo,

Das glaub ich jetzt nicht...
Wenn ich der **Property **tatsächlich { get; set; } anfüge funktionierts ... Aber ich verstehe nicht wieso.

Erst in dem Moment als du das {get;set;} hinzugefügt hast, handelt es sich um eine Propertie / Eigenschaft.

Davor war es eine Membervariable.

Die Zuweisung funktioniert doch in beiden fällen Identisch, es wird letztenendes nur auf eine **Membervariable **zugegriffen. Dass es für XAML da einen Unterschied gibt hätte ich nie gedacht...

Beim Binding wird immer auf die Propertie zugegriffen. Die Propertie speichert oder gibt den Wert der Membervariable zurück (solang es sich um eine stink normale Propertie handelt).

Wenn du auf eine Membervariable zugreifen willst must du das so machen:


public static string name="asdf";


<TextBlock Text="{Binding Source={x:Static local:Window1.name}}"></TextBlock>

In deinem Fall ist aber die Propertie genau richtig.

Auf Statische Membervariablen zugreifen macht z.B. bei Converter sinn.

Informationen zum Binding findest du hier:

http://msdn.microsoft.com/de-de/library/ms752347.aspx

Schaut mal im IRC vorbei:
Server: https://libera.chat/ ##chsarp

Felsen Themenstarter:in
53 Beiträge seit 2009
vor 14 Jahren

Hi,
danke dir nochmal für die Mühe deines letzten Beitrags. Werde das bei meinen künftigen Aufgaben im Hinterkopf behalten.

Mfg Felsen