Laden...

Wie kann ich ein ContentControl erstellen und auf den Inhalt zugreifen?

Erstellt von braesident vor 5 Jahren Letzter Beitrag vor 5 Jahren 4.316 Views
B
braesident Themenstarter:in
15 Beiträge seit 2017
vor 5 Jahren
Wie kann ich ein ContentControl erstellen und auf den Inhalt zugreifen?

Hallo Leute,

seit Tagen versuch ich mein erstes Control zu erstellen. Die letzten Tage gingen dabei drauf das mit einem UserControl zu machen. Zwischendurch waren auch erfolge dabei aber dafür neu Probleme. Das übliche halt. Am Ende kam ich zu der Erkenntnis das folgendes Konstrukt mit einem CustomControl zu machen ist. Jetzt hab ich soviel über diverse Controls und Templates gelesen das ich den Faden verloren haben.

Ziel:


<Grid>
  <Grid x:Name="ContentContainer">
    <ContentControl Content="{TemplateBinding Content}"/>
  </Grid>

  <Ellipse x:Name="MainCircle"/>
</Grid>

Das Grid ContentContainer soll diverse (mehrere) Controls aufnehmen können. Im UserControl hatte ich das lösen können, hier jedoch nicht. Desweiteren steh ich hier noch vor der Frage wie kann ich vom code behind auf ContentContainer zugreifen um die Elemente zu durchlaufen?
Oder ist ein ContentControl doch Falsch? Gelesen hatte ich noch was von ItemControl.


<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:MyControl">

    <Style TargetType="{x:Type local:CustomControlTest}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:CustomControlTest}">
                    <Grid>
                        <Grid x:Name="ContentContainer">
                            <!--<ContentPresenter/>-->
                            <ContentControl Content="{TemplateBinding Content}"/>
                        </Grid>

                        <Ellipse x:Name="MainCircle"
                                VerticalAlignment="Top" HorizontalAlignment="Left" 
                                Width="48" Height="48" Stroke="Black" Fill="Red"/>

                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>


    //[ContentProperty("Children")]
    public class CustomControlTest : ContentControl
    {
        static CustomControlTest()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControlTest), new FrameworkPropertyMetadata(typeof(CustomControlTest)));
        }

        public CustomControlTest()
        {
            //Children = ContentContainer.Children;
        }
    }

656 Beiträge seit 2008
vor 5 Jahren

Wenn du mehrere Inhalte möchtest, suchst du vermutlich das ItemsControl als Basisklasse/Container (oder eine dessen abgeleiteter Klassen).

B
braesident Themenstarter:in
15 Beiträge seit 2017
vor 5 Jahren

Hallo Bhaal,

ItemsControl hört sich erstmal logisch an.

Ich habe meine Dateien wie folgt abgeändert. Es gibt keine Fehler aber auch keine Ausgabe.

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:MyControl">

    <Style TargetType="{x:Type local:CustomControlTest}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:CustomControlTest}">
                    <Grid>
                        <Grid.Resources>
                            <local:MyData x:Key="dataList"/>
                        </Grid.Resources>
                        <Grid x:Name="ContentContainer" ><!--Children="{Binding Source={StaticResource dataList}}"-->
                           <ItemsControl ItemsSource="{Binding Source={StaticResource dataList}}"/>
                        </Grid>

                        <Ellipse x:Name="MainCircle"
                                VerticalAlignment="Top" HorizontalAlignment="Left" 
                                Width="48" Height="48" Stroke="Black" Fill="Red"/>

                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

    public class CustomControlTest : ItemsControl
    {
        MyData listData = new MyData();
        Binding binding1 = new Binding();

        static CustomControlTest()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControlTest), new FrameworkPropertyMetadata(typeof(CustomControlTest)));

        }

        public CustomControlTest()
        {
            binding1.Source = listData;
        }
    }

    public class MyData : ObservableCollection<object>
    {
        public MyData(){}
    }
}


    ...
        <local:CustomControlTest Grid.Row="1">
            <TextBlock x:Name="a">Ein sinnloser Text</TextBlock>
            <TextBlock x:Name="b">Noch mehr sinnloses</TextBlock>
        </local:CustomControlTest>
    ...

5.657 Beiträge seit 2006
vor 5 Jahren

Hi braesident,

um eine Liste mit Daten anzuzeigen, benötigst du:

  • eine Auflistung mit den Daten
  • ein ItemsControl
  • ein DataTemplate.

Du benötigst dafür kein eigenes Control, das von ItemsControl ableitet. Versuch lieber erstmal, die Beispiele in diesem Artikel nachzuvollziehen:
[Artikel] MVVM und DataBinding

Weeks of programming can save you hours of planning

2.078 Beiträge seit 2012
vor 5 Jahren

So wie Du dein Control verwendest, würde ich ein ControlTemplate für das ItemsControl (nicht für dein eigenes Control) schreiben, damit sparst Du das CustomControl ein. Außer Du hast komplexen eigenen Code, den müsstest Du dann über AttachedProperties abdecken und das wird schnell etwas umständlich.

Wenn Du nur CustomControls oder UserControls üben/lernen willst, dann musst Du dir erst Mal über den Unterschied im Klaren sein.
Bei UserControls empfielt sich MVVM mit einem ViewModel und passendem Binding.
Bei CustomControl arbeitest Du mit einem Template und TemplateBindings ohne ViewModel.
Dazu gibt's aber mehr als genug Quellen im Internet.

Wenn Du in XAML ein Binding definierst, dann schreibst Du dort den Namen der Property rein, an die Du binden willst. WPF sucht diese Property in dem Objekt, was der DataContext bereit hält oder welches Du über die Binding-Source bereit stellst. Deine Source ist in deinem Fall eine StaticResource und die sucht den mit gegebenen Namen (dataList) in den verfügbaren Resources. Wenn Du eine Resource mit dem Key "dataList" hast, funktioniert das, wenn nicht, dann nicht.
Die "listData"- und "binding"-Felder, die Du im CodeBehind hast, werden so wie sie sind, nie verwendet und daher auch völlig ignoriert.

Für deinen aktuellen Code brauchst Du vermutlich ein TemplateBinding, das sucht die Property an dem Control, was das Template bekommt. Sprich: {TemplateBinding ItemsSource}
Probiere das Mal, ich kann's gerade nicht testen.

B
braesident Themenstarter:in
15 Beiträge seit 2017
vor 5 Jahren

Danke erstmal für eure Information.

Leider sieht es nicht besser aus bei mir. Ich hab nochmal versucht in ein Projekt die verschiedenen Controls zu implementieren.

Ein eigenes Control kommt schon in Frage da ich es später schon in verschiedenen Projekten verwenden möchte. Der Code oben in meinem Test ist etwas unspezifisch. D.h. am Ende soll im Projekt ein oder mehr Button übergeben werden so wie in meinem Bsp. die TextBlock's.
In dem Testcode ist bis jetzt noch nicht berücksichtigt das die hinzugefügten Buttons ein Style vom Control und jeweils ein umschließendes Grid mit einer Animation bekommen sollen. Das Control hat also schon ein wenig was zu tun.
(Zum vorstellen: Am Ende soll es FAST wie ein Floatbutton sein)

Eine Übersicht meiner in Frage kommenden Möglichkeiten als Anhang. Im Grunde ist von der Anordnung her das UserControl das richtige. Nur kann den Controls die später hinzugefügt werden über XAML kein Name vergeben werden. Mach ich das im Control nachträglich, finden angewendete Animationen diesen Namen allerdings nicht.
So kam ich zum CustomControl. Das ContentControl nimmt allerdings nur ein Element auf und das ItemControl listet die Elemente halt wie eine List unpraktikabel untereinander.

B
braesident Themenstarter:in
15 Beiträge seit 2017
vor 5 Jahren

Mein Frage scheint erstmal geklärt zu sein. Das ItemsControl führte nun doch zum Erfolg. Die Lösung war das festlegen des ItemsPanel - dieses ist Standard ein StackPanel.

<Style TargetType="local:MyItemsControl">
        <Setter Property="ItemsPanel">
            <Setter.Value>
                <ItemsPanelTemplate>
                    <Grid/>
                </ItemsPanelTemplate>
            </Setter.Value>
        </Setter>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:MyItemsControl">
                    <Grid x:Name="ControlGrid">
                        <ItemsPresenter/>
                        <Ellipse x:Name="MainCircle"
                                VerticalAlignment="Top" HorizontalAlignment="Left" 
                                Width="24" Height="24" Stroke="Black" Fill="Red"/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
2.078 Beiträge seit 2012
vor 5 Jahren

Mir ist nicht ganz klar, was das bringen soll, aber wenn es für dich passt, OK.
Wozu brauchst Du den Namen "ControlGrid"? Du kannst sowieso nur in diesem Template darauf zugreifen und da Du das nicht tust, ist der Name überflüssig.

Beachte aber:
Wenn Du ein Grid als ItemsPanel nutzt, dann liegen alle Items "übereinander".
Du musst Column bzw. Row setzen, um sie zu positionieren.

Und bezüglich deinem Style:
Setz als TargetType von Style und Template einfach nur ItemsControl.
Ich könnte mir vorstellen, dass das genauso gut funktioniert.

Ach und schau dir Mal das UniformGrid an, tut das vielleicht, was Du brauchst?

B
braesident Themenstarter:in
15 Beiträge seit 2017
vor 5 Jahren

Ja das ist so gewollt, die Controls sollen erstmal übereinander liegen und werden später durch Animationen herausbewegt. (So hoffe ich zumindest 😁 ) Deswegen kommt das UniformGrid auch nicht für mich in Frage.

Ja ControlGrid ist überflüssig geworden. Das ist noch ein Überbleibsel aus meinem ersten Versuch mit einem UserControl.

Für das Template funktioniert das mit ItemsControl. Für Style nicht.