none
SelectedItem einer Combobox an einen TextBlock übergeben... RRS feed

  • Frage

  • Guten Morgen,

    Ich habe ein DataGrid, dessen Spalten beinhalten jeweils ein <DataGridTemplateColumn.CellTemplate> und ein <DataGridTemplateColumn.CellEditingTemplate>,
    also nichts anderes als den normalen Ansichtmodus und Editiermodus in einem DataGrid.

    Der Ansichtmodus der Spalte aus dem Grid beinhaltet eine Textbox :

    <DataGridTemplateColumn.CellTemplate>
    <DataTemplate>
    <TextBlock Text="{Binding Activitytype}"/>
    </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>  

    der Editiermodus der Spalte beinhaltet eine ComboBox :

    <DataGridTemplateColumn.CellEditingTemplate>
    <ComboBox ItemsSource="{Binding Path=DataContext.ActivityList, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"                                                    
    SelectedValue="{Binding Activitytype}" SelectedValuePath="Id"                                                                                                                                                                
    DisplayMemberPath="Title" IsEditable="True">
    </ComboBox>    
    </DataGridTemplateColumn.CellEditingTemplate>  


    Der Activitytype ist eine "ID", die aus der Haupttabelle kommt. Die ActivityList ist eine Nebentabelle, die eine "ID" und einen "Title" beinhaltet.
    Die Combobox wird mit der ActivityList gefüllt, die "ID" aus der ActivityList ist gleich dem Activitytype, also der "ID" aus der Haupttabelle.
    Im Editiermodus des DataGrid wird die ComboBox richtig gesetzt. Mein Problem besteht nun darin, dass der TextBlock in der normalen Ansicht die "ID"
    aus der Haupttabelle anzeigt, also den Activitytepe, ich möchte aber, dass der "Title" aus der Nebentabelle angezeigt wird, also nichts anderes als
    der Text aus der ComboBox, die ja richtig gesetzt ist.

    Dieser Lösungsweg brachte mit in jeder Zeile des DataGrid denselben Text, ist ja auch klar, jeder TextBlock wird mit dem "Titel" der ActivityList mit dem
    Index 2 gefüllt :

    <TextBlock Text="{Binding DataContext.ActivityList[2].Title, RelativeSource ={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" />

    Nun habe ich mir gedacht, da der Activitytype gleich dem Index der ActivityList ist, müsste dieser Lösungsweg funktionieren :

    <TextBlock Text="{Binding DataContext.ActivityList[Activitytype].Title, RelativeSource ={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" />

    Leider wird nichts mehr angezeigt. Gibt es eine Möglichkeit den Activitytype als Variable zu übergeben ( Lösung 2 ) ? oder dem TextBlock direkt den Inhalt der
    ComboBox zuzuweisen ? Kennt der TextBlock überhaupt die ComboBox ?

    Dienstag, 14. August 2012 08:13

Antworten

  • Hi,

    dann machen wir es halt etwas ausführlicher. Also zuerst die Prämissen (ich verwende eigene, maximal vereinfachte Datenstrukturen):

    • Du hast in Deinem UserControl als DataContext ein Objekt, dass (u. a.) eine Liste von Elementen enthält (die heißt bei Dir "ActivityList, bei mir TheList). Das Objekt heißt bei mir "ViewModel".
    • Diese Liste enthält Entitäten, die (zumindest) die Eigenschaften "Id" und "Text" enthalten (hierfür habe ich die Klasse "Entity" erstellt).

    Jetzt muss ich einwenig 'zurückrudern: Man kann einen ConverterParameter nicht binden. Daher müssen wir die Liste mit den Entitäten als Ressource bereitstellen - als CollectionViewSource. Wir haben also (INotifyPropertyChanged-Implementierung lasse ich weg):

    Das ViewModell und die Entity-Klasse:

    public class ViewModel : Common.Core.NotifyObject
    {
       public ViewModel()
       {
          this.TheList = new List<Entity>();
          this.TheList.Add(new Entity { Id = 1, Text = "eins" });
          this.TheList.Add(new Entity { Id = 2, Text = "zwei" });
          this.TheList.Add(new Entity { Id = 3, Text = "drei" });
       }
    
       public int TheId { get; set; }      // INPC
    
       public List<Entity> TheList { get; private set; }
    }
    
    public class Entity
    {
       public int Id { get; set; }
       public string Text { get; set; }
    }

    Der Xaml-Code für eine TextBox und eine ComboBox (sollte für das Prinzip reichen):

    <Page x:Class="..."
       xmlns:local="clr-namespace:...dein namespace...">
    
       <Page.Resources>
          <local:IndexToTextCoverter x:Key="IndexToTextCoverter" />
          <CollectionViewSource x:Key="TheViewSource" Source="{Binding Path=TheList}" />
       </Page.Resources>
    
       <StackPanel>
          <TextBox
             Text="{Binding Path=TheId, 
                Converter={StaticResource ResourceKey=IndexToTextCoverter}, 
                ConverterParameter={StaticResource ResourceKey=TheViewSource}}" />
          <ComboBox
             DisplayMemberPath="Text"
             SelectedValuePath="Id"
             ItemsSource="{Binding Source={StaticResource ResourceKey=TheViewSource}}"
             SelectedValue="{Binding Path=TheId}" />
       </StackPanel>
    </Page>

    Ich habe (hoffentlich zur Steigerung der Übersichtilichkeit) in der Page-Definition einiges wegggelassen,was Du leicht wieder einfügen kannst (es trägt nicht zur Lösung des Problems bei).

    Der DataContext dieser Seite ist eine Instanz von ViewModel (geerbt vom UserControl). Wenn Du den Code jetzt ohne die Zeilen "Converter..." und "ConverterParameter..." (und die Converter-Resource) laufen lässt, erhälst Du eine TextBox mit einer "0" und eine ComboBox mit leerem Eintrag (und den Auswahlmöglichkeiten "eins", "zwei", "drei").

    Fehlt also noch der Converter selbst, mit dem wir die Id in den zugehörigen Text umwandeln. Das Text-Binding der TextBox verwendet den Converter und übergibt ihm a) den gebundenen Wert ("TheId") und b) die Liste mit den Entitäten (im ConverterParameter). Der Converter selbst sieht so aus:

    public class IndexToTextCoverter : IValueConverter
    {
       public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
       {
          int id = (int)value;
          CollectionViewSource cvs = parameter as CollectionViewSource;
          List<Entity> list = cvs.Source as List<Entity>;
          Entity entity = list.FirstOrDefault(e => e.Id == id);
    
          return (entity == null) ? null : entity.Text;
       }
    
       public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
       { throw new NotImplementedException(); }
    }

    Er ermittelt aus der CollectionViewSource (als parameter übergeben) den Eintrag, dessen "Id" mit dem zu konvertierenden Wert (übergeben als value) übereinstimmt.

    Ich hoffe, ich habe nicht zu sehr reduziert. Sonst frag einfach nochmal.

    Gruß
    Jürgen

    Dienstag, 14. August 2012 12:59

Alle Antworten

  • Hi,

    Du könntest es mit einem Converter für die TextBox versuchen. Den ConverterParameter bindest Du an "DataContext.ActivityList" und kannst so den Text der über ActivityType indizierten Listenzeile ermitteln lassen.

    Gruß
    Jürgen

    Dienstag, 14. August 2012 08:48
  • Hi Jürgen,

    danke für deine Antwort, da ich mich in WPF erst einarbeite, weiss ich nicht wirklich was du meinst.

    Dienstag, 14. August 2012 10:19
  • Hi,

    dann machen wir es halt etwas ausführlicher. Also zuerst die Prämissen (ich verwende eigene, maximal vereinfachte Datenstrukturen):

    • Du hast in Deinem UserControl als DataContext ein Objekt, dass (u. a.) eine Liste von Elementen enthält (die heißt bei Dir "ActivityList, bei mir TheList). Das Objekt heißt bei mir "ViewModel".
    • Diese Liste enthält Entitäten, die (zumindest) die Eigenschaften "Id" und "Text" enthalten (hierfür habe ich die Klasse "Entity" erstellt).

    Jetzt muss ich einwenig 'zurückrudern: Man kann einen ConverterParameter nicht binden. Daher müssen wir die Liste mit den Entitäten als Ressource bereitstellen - als CollectionViewSource. Wir haben also (INotifyPropertyChanged-Implementierung lasse ich weg):

    Das ViewModell und die Entity-Klasse:

    public class ViewModel : Common.Core.NotifyObject
    {
       public ViewModel()
       {
          this.TheList = new List<Entity>();
          this.TheList.Add(new Entity { Id = 1, Text = "eins" });
          this.TheList.Add(new Entity { Id = 2, Text = "zwei" });
          this.TheList.Add(new Entity { Id = 3, Text = "drei" });
       }
    
       public int TheId { get; set; }      // INPC
    
       public List<Entity> TheList { get; private set; }
    }
    
    public class Entity
    {
       public int Id { get; set; }
       public string Text { get; set; }
    }

    Der Xaml-Code für eine TextBox und eine ComboBox (sollte für das Prinzip reichen):

    <Page x:Class="..."
       xmlns:local="clr-namespace:...dein namespace...">
    
       <Page.Resources>
          <local:IndexToTextCoverter x:Key="IndexToTextCoverter" />
          <CollectionViewSource x:Key="TheViewSource" Source="{Binding Path=TheList}" />
       </Page.Resources>
    
       <StackPanel>
          <TextBox
             Text="{Binding Path=TheId, 
                Converter={StaticResource ResourceKey=IndexToTextCoverter}, 
                ConverterParameter={StaticResource ResourceKey=TheViewSource}}" />
          <ComboBox
             DisplayMemberPath="Text"
             SelectedValuePath="Id"
             ItemsSource="{Binding Source={StaticResource ResourceKey=TheViewSource}}"
             SelectedValue="{Binding Path=TheId}" />
       </StackPanel>
    </Page>

    Ich habe (hoffentlich zur Steigerung der Übersichtilichkeit) in der Page-Definition einiges wegggelassen,was Du leicht wieder einfügen kannst (es trägt nicht zur Lösung des Problems bei).

    Der DataContext dieser Seite ist eine Instanz von ViewModel (geerbt vom UserControl). Wenn Du den Code jetzt ohne die Zeilen "Converter..." und "ConverterParameter..." (und die Converter-Resource) laufen lässt, erhälst Du eine TextBox mit einer "0" und eine ComboBox mit leerem Eintrag (und den Auswahlmöglichkeiten "eins", "zwei", "drei").

    Fehlt also noch der Converter selbst, mit dem wir die Id in den zugehörigen Text umwandeln. Das Text-Binding der TextBox verwendet den Converter und übergibt ihm a) den gebundenen Wert ("TheId") und b) die Liste mit den Entitäten (im ConverterParameter). Der Converter selbst sieht so aus:

    public class IndexToTextCoverter : IValueConverter
    {
       public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
       {
          int id = (int)value;
          CollectionViewSource cvs = parameter as CollectionViewSource;
          List<Entity> list = cvs.Source as List<Entity>;
          Entity entity = list.FirstOrDefault(e => e.Id == id);
    
          return (entity == null) ? null : entity.Text;
       }
    
       public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
       { throw new NotImplementedException(); }
    }

    Er ermittelt aus der CollectionViewSource (als parameter übergeben) den Eintrag, dessen "Id" mit dem zu konvertierenden Wert (übergeben als value) übereinstimmt.

    Ich hoffe, ich habe nicht zu sehr reduziert. Sonst frag einfach nochmal.

    Gruß
    Jürgen

    Dienstag, 14. August 2012 12:59
  • Hallo Jürgen,

    es hat funktioniert, besten dank für deine Hilfe !!

    Gruß

    Peter

    Donnerstag, 16. August 2012 05:54