none
Master-Detail-View with databinding and various datatypes RRS feed

  • Frage

  • Hello Everybody,

    I was trying to write a Master-Detail-Viewer with WPF, where a Preview of all die items form a ObservableCollection<SampleDataInterface> should be showed in a ListBox. Whereas a ContentPresenter demonstrates all the Properties, of the Instance of the current selected Item. These Instances are of self defined types. There can be different Types in inherited from the Interface called SampleDataInterface in the ObservableCollection. Every Type needs his one DataTemplate. The Master View works fine, with a TemplateSelector, but the ContentPresenter does not. Using a TemplateSelector in the ContentPresenter looks very ugly in the SelectorFunction, because there is sent the whole list as a parameter to the function and it might be hard to get the selected index. It also seems not to be an elegant way of coding. My Second try was to use different Styles for the a Custom Control presenting the Detail-View. But then there might be only a Content-String to evaluate, where it was impossible to reconstruct the type of the origin data.


    Has anyone a suggestion, how to fix that gap ?

    Thank you very much!

    Matthias

    PS. I tried to add the code, but I got an unknown error there from that site.


    • Bearbeitet Matthias L_ Donnerstag, 4. April 2013 12:35
    Donnerstag, 4. April 2013 12:34

Antworten

  • Hallo Matthias,

    erstelle deine DetailTemplateA bzw. DetailTemplateB in XAML genauso wie Du's mit den MasterDataTemplates getan hast. Im Code erstelle dann den entsprechenden DetailDataTemplateSelector und verweise darauf im XAML-Code. Bleibt noch die letzte kleine Änderung:

    <ContentControl HorizontalAlignment="Right" Content="{Binding /}" 
    ContentTemplateSelector="{StaticResource DetailDataTemplateSelector}" />

    Über {Binding /} binden wir an das aktuell selektierte Objekt und gut ist.

    Gruß
    Marcel

    • Als Antwort markiert Matthias L_ Freitag, 5. April 2013 12:57
    Donnerstag, 4. April 2013 16:57

Alle Antworten

  • Oh Entschuldigt,

    war mir zunächst nicht sicher, ob dieses Forum nun auf Deutschland beschränkt sei.

    Also nochmal auf Deutsch:

    Ich versuchte eine Master-Datail-View Anwendung zu schreiben. Diese klappt mithilfe der Datenbindung schon ganz gut. Ganz gut heißt: mit der Bedingung, dass die Daten in der ObservableCollection alle von der selben Klasse instanziert werden. Nun möchte ich allerdings, dass die Controls (zum einen eine ListBox für die Master-View, und einen abstrakten ContentPresenter für die Detail-View, indem das aktuell ausgewählte Element genauer durchleuchtet werden soll), auch mit Instantzen unterschiedlicher Klassen richtig umgehen können. D.h. jeweils andere Eigenschaften der Elemente darstellen. Bei der Master-View klappt dies mit einem TemplateSelector schon wunderbar. Nur hatte ich bisher noch kein Glück beim ContentPresenter: Verwende ich dort ebenfalls einen TemplateSelector, so wird dessen Auswahlfunktion nur beim Starten der Anwendung aufgerufen und als Parameter wird die komplette Liste übergeben. Eine Auswahl des zu verwendenden Templates ist somit nahezu unmöglich. Mit Styles für ein beliebiges Template (z.B. ein StackPanal) bietet sich eine Alternative zu dem ContentPresenter an. Allderdings sah ich dort keine Möglichkeit, die Auswahl verschiedener Styles in Triggern über den Typ des aktuellen Elements durchzuführen.

    Hier ein kleiner Screenshot:

    Hier mal der aktuelle Code. (Sollte selbsterklärend sein):

    MainWindow.xaml

    <Window x:Class="DataBinding.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:data="clr-namespace:DataBinding"
            xmlns:local="clr-namespace:DataBinding"
            Title="MainWindow" Height="350" Width="525">
        <Window.Resources>
            <ObjectDataProvider x:Key="objDs" ObjectType="{x:Type data:CDataAccess}" MethodName="GetSampleData"/>
            <local:MasterDataTemplateSelector x:Key="MasterDataTemplateSelector" />
            <DataTemplate x:Key="MasterDataTemplateA">
                <StackPanel>
                    <TextBlock Text="{Binding Vorname}"/>
                    <TextBlock Text="{Binding Nachnahme}"/>
                </StackPanel>
            </DataTemplate>
            <DataTemplate x:Key="MasterDataTemplateB">
                <StackPanel>
                    <TextBlock Text="{Binding Vorname}"/>
                    <TextBlock Text="{Binding Nachnahme}"/>
                    <TextBlock Text="{Binding Zinssatz}" />
                </StackPanel>
            </DataTemplate>
            <DataTemplate x:Key="MasterDataTemplateDefault">
                <StackPanel>
                    <TextBlock Text="Ungültiges Element"/>
                </StackPanel>
            </DataTemplate>
            <DataTemplate x:Key="DetailDataTemplate">
                <StackPanel Margin="10,10,10,10" HorizontalAlignment="Right" >
                    <TextBox Margin="10,5,10,5" VerticalAlignment="Top" Width="120" Text="{Binding Vorname}"/>
                    <TextBox Margin="10,5,10,5" VerticalAlignment="Top" Width="120" Text="{Binding Nachnahme}"/>
                    <TextBox Margin="10,5,10,5" VerticalAlignment="Top" Width="120" Text="{Binding Kontostand}"/>
                </StackPanel>
            </DataTemplate>
        </Window.Resources>
        <Grid DataContext="{Binding Source={StaticResource objDs}}">
            <ListBox Height="Auto" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="200" 
                      ItemsSource="{Binding}" ItemTemplateSelector="{StaticResource MasterDataTemplateSelector}" IsSynchronizedWithCurrentItem="True"/>
            <ContentControl HorizontalAlignment="Right" Content="{Binding}" ContentTemplate="{StaticResource DetailDataTemplate}" />
        </Grid>
    </Window>
    

    Dazu den Codebehind: MainWindow.xaml:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    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;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    
    namespace DataBinding
    {
        /// <summary>
        /// Interaktionslogik für MainWindow.xaml
        /// </summary>
        /// 
    
        public interface SampleDataInterface { 
        }
    
        public class SamplaDataClassA : SampleDataInterface
        {
            private string vorname, nachname;
            int kontostand;
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            public string Vorname
            {
                get { return vorname; }
                set
                {
                    if (vorname != value)
                    {
                        vorname = value;
                        OnPropertyChanged(new PropertyChangedEventArgs("Vorname"));
                    }
                }
            }
    
            public string Nachnahme
            {
                get { return nachname; }
                set
                {
                    if (nachname != value)
                    {
                        nachname = value;
                        OnPropertyChanged(new PropertyChangedEventArgs("Nachnahme"));
                    }
                }
            }
    
            public int Kontostand
            {
                get { return kontostand; }
                set
                {
                    if (kontostand != value)
                    {
                        kontostand = value;
                        OnPropertyChanged(new PropertyChangedEventArgs("Kontostand"));
                    }
                }
            }
    
            protected void OnPropertyChanged(PropertyChangedEventArgs e)
            {
                if (PropertyChanged != null)
                    PropertyChanged(this, e);
            }
        }
    
        public class SamplaDataClassB : SampleDataInterface
        {
            private string vorname, nachname;
            int kontostand;
            double zinssatz;
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            public string Vorname
            {
                get { return vorname; }
                set
                {
                    if (vorname != value)
                    {
                        vorname = value;
                        OnPropertyChanged(new PropertyChangedEventArgs("Vorname"));
                    }
                }
            }
    
            public string Nachnahme
            {
                get { return nachname; }
                set
                {
                    if (nachname != value)
                    {
                        nachname = value;
                        OnPropertyChanged(new PropertyChangedEventArgs("Nachnahme"));
                    }
                }
            }
    
            public int Kontostand
            {
                get { return kontostand; }
                set
                {
                    if (kontostand != value)
                    {
                        kontostand = value;
                        OnPropertyChanged(new PropertyChangedEventArgs("Kontostand"));
                    }
                }
            }
    
            public double Zinssatz
            {
                get { return zinssatz; }
                set
                {
                    if (zinssatz != value)
                    {
                        zinssatz = value;
                        OnPropertyChanged(new PropertyChangedEventArgs("Zinssatz"));
                    }
                }
            }
    
            protected void OnPropertyChanged(PropertyChangedEventArgs e)
            {
                if (PropertyChanged != null)
                    PropertyChanged(this, e);
            }
        }
    
        public class CDataAccess
        {
            ObservableCollection<SampleDataInterface> _SampleCollection;
    
            public ObservableCollection<SampleDataInterface> SampleCollection
            {
                get { return _SampleCollection; }
                set { _SampleCollection = value; }
            }
    
            public CDataAccess()
            {
                _SampleCollection = new ObservableCollection<SampleDataInterface>();
            }
    
            public ObservableCollection<SampleDataInterface> GetSampleData()
            {
                SampleCollection.Add(new SamplaDataClassA() { Vorname = "Anton", Nachnahme = "Altmeier", Kontostand = 100 });
                SampleCollection.Add(new SamplaDataClassA() { Vorname = "Berta", Nachnahme = "Biermeier", Kontostand = 30 });
                SampleCollection.Add(new SamplaDataClassB() { Vorname = "Cäsar", Nachnahme = "Chrismeier", Kontostand = 1000, Zinssatz = 0.12 });
                SampleCollection.Add(new SamplaDataClassA() { Vorname = "Dagobert", Nachnahme = "Dietmeier", Kontostand = 30 });
                return SampleCollection;
            }
        }
    
        #region TemplateSelektoren
    
        public class MasterDataTemplateSelector : DataTemplateSelector
        {
            public override DataTemplate SelectTemplate(object item, DependencyObject container)
            {
                FrameworkElement element = container as FrameworkElement;
    
                if (item is SamplaDataClassA)
                    return element.FindResource("MasterDataTemplateA") as DataTemplate;
                if (item is SamplaDataClassB)
                    return element.FindResource("MasterDataTemplateB") as DataTemplate;
                return element.FindResource("MasterDataTemplateDefault") as DataTemplate;
            }
        }
    
        #endregion
    
        public partial class MainWindow : Window
        {
            
    
            public MainWindow()
            {
                InitializeComponent();
            }
        }
    
    
    }
    

    Vielen Dank!

    Grüße,

    Matthias

    Donnerstag, 4. April 2013 15:45
  • Hallo Matthias,

    erstelle deine DetailTemplateA bzw. DetailTemplateB in XAML genauso wie Du's mit den MasterDataTemplates getan hast. Im Code erstelle dann den entsprechenden DetailDataTemplateSelector und verweise darauf im XAML-Code. Bleibt noch die letzte kleine Änderung:

    <ContentControl HorizontalAlignment="Right" Content="{Binding /}" 
    ContentTemplateSelector="{StaticResource DetailDataTemplateSelector}" />

    Über {Binding /} binden wir an das aktuell selektierte Objekt und gut ist.

    Gruß
    Marcel

    • Als Antwort markiert Matthias L_ Freitag, 5. April 2013 12:57
    Donnerstag, 4. April 2013 16:57
  • Vielen Tausend Danke Marcel!!

    Mit Schrägstrich klappt's!

    Wahnsinn, über diesen Schrägstrich hatte ich jetzt fast schon eine Woche lang gegrübelt.

    Donnerstag, 4. April 2013 18:40