2010-07-23 9 views
1

Essayez de comprendre ce processus de liaison du fichier WPF.Problème de liaison simple WPF

Voir le code en bas.

Dans mon "viewmodel", voir le code en bas, j'ai une collection observable qui remplit la listview avec les éléments. C'est celui qui contient un chemin appelé symbole pour définir l'index sélectionné dans la liste déroulante. Maintenant, mon problème est que j'ai besoin de remplir la combobox à partir d'une autre liste avant qu'elle ne soit ajoutée à la liste (certaines valeurs par défaut). Depuis que je viens de commencer avec WPF, j'ai pensé que vous pourriez peut-être utiliser 2 ObservableCollections différentes dans la même classe pour y parvenir mais cela n'a pas fonctionné. Alors, comment puis-je remplir le datatemplate avec les valeurs par défaut avant qu'elles ne soient liées/ajoutées à la liste?

C'est ce que j'utilise pour peupler la liste, voir le viewmodelcontacts en bas. J'ai également essayé d'ajouter une autre classe avec un nouveau observablecollection que je pourrais utiliser dans ma combobox, mais je n'ai pas réussi à le faire fonctionner non plus. Les données qui doivent être renseignées proviennent d'un fichier XML situé en tant que ressource dans mon application.

Une autre question, est-il possible d'ajouter des commandes aux images? ou les commandes sont-elles uniquement disponibles à partir des contrôles qui héritent de la classe button_base? Je voulais utiliser une image à côté d'un élément et lorsque l'utilisateur cliquait sur cette image, il supprimait l'élément.

  • De la réponse ci-dessous, est-il possible sans l'ajout d'un bouton car je ne veux pas le sentiment de bouton (par exemple en vol stationnaire et en cliquant) *

Window.xaml:

<r:RibbonWindow x:Class="Onyxia_KD.Windows.ContactWorkspace" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:r="clr-namespace:Microsoft.Windows.Controls.Ribbon;assembly=RibbonControlsLibrary"   
    Title="Contact card" ResizeMode="NoResize" Height="600" Width="600" 
    Background="White"> 
<r:RibbonWindow.Resources> 
    <DataTemplate x:Key="cardDetailFieldTemplate"> 
     <TextBox Text="{Binding Path=Field}" MinWidth="150"></TextBox> 
    </DataTemplate> 
    <DataTemplate x:Key="cardDetailValueTemplate"> 
     <TextBox Text="{Binding Path=Value}" MinWidth="150"></TextBox> 
    </DataTemplate> 
    <DataTemplate x:Key="cardDetailSymbolTemplate"> 
     <!-- Here is the problem. Populating this with some default values for each entry before the selectedIndex is bound from the datasource --> 
     <ComboBox SelectedIndex="{Binding Path=Symbol}" DataContext="_vmSettings" ItemsSource="{Binding Symbols}" Grid.Column="1" Margin="0,0,10,0" VerticalAlignment="Center" HorizontalAlignment="Center"> 
      <ListViewItem Padding="0,3,0,3" Content="{Binding Path=Value}" />          
     </ComboBox> 
    </DataTemplate> 
    <DataTemplate x:Key="cardDetailCategoryTemplate"> 
     <ComboBox SelectedIndex="{Binding Path=Category}" Grid.Column="1" Margin="0,0,10,0" VerticalAlignment="Center" HorizontalAlignment="Center"> 
      <!--same as the combobox above but categories instead of symbols-->     
     </ComboBox> 
    </DataTemplate> 
</r:RibbonWindow.Resources> 
... 
<ListView ItemsSource="{Binding ContactData}" Foreground="Black" SelectionMode="Single" x:Name="cardDetailList" KeyDown="cardDetailList_KeyDown"> 
        <ListView.View> 
         <GridView> 
          <GridViewColumn Header="Category" Width="auto" CellTemplate="{StaticResource cardDetailCategoryTemplate}"></GridViewColumn> 
          <GridViewColumn Header="Field" Width="auto" CellTemplate="{StaticResource cardDetailFieldTemplate}"></GridViewColumn> 
          <GridViewColumn Header="Symbol" Width="70" CellTemplate="{StaticResource cardDetailSymbolTemplate}"></GridViewColumn> 
          <GridViewColumn Header="Value" Width="auto" CellTemplate="{StaticResource cardDetailValueTemplate}"></GridViewColumn>         
         </GridView> 
        </ListView.View>       
       </ListView> 

code sous-jacent:

private ViewModelContacts _vm; 

    public ContactWorkspace() 
    { 
     InitializeComponent(); 

     _vm = new ViewModelContacts();    
     this.DataContext = _vm; 

    } 

    private void Run_MouseUp(object sender, MouseButtonEventArgs e) 
    { 
     _vm.AddNewDetail(); 
    } 

    private void Image_MouseUp(object sender, MouseButtonEventArgs e) 
    { 
     _vm.AddNewDetail(); 
    } 

    private void cardDetailList_KeyDown(object sender, KeyEventArgs e) 
    { 
     if (e.Key == Key.Delete) 
     { 
      if (MessageBox.Show("Are you sure that you want to delete this?", "Warning!", MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes) 
      { 
       _vm.RemoveDetail(cardDetailList.SelectedIndex); 
      } 
     } 
    } 

ViewModelContacts:

public ObservableCollection<ContactCardData> ContactData { get; set; }    

    public ViewModelContacts() 
    { 

     ContactData = new ObservableCollection<ContactCardData>();    
     Populate(); 
    } 

    private void Populate() 
    { 
     ContactData.Add(new ContactCardData("Test", 0, 0, "Value123")); 
     ContactData.Add(new ContactCardData("Test2", 1, 1, "Value1234")); 
     ContactData.Add(new ContactCardData("Test3", 2, 2, "Value1235")); 
     ContactData.Add(new ContactCardData("Test4", 3, 3, "Value12356"));    
    } 

    public void UpdateNode() 
    { 
     ContactData.ElementAt(0).Value = "Giraff"; 
    } 

    public void AddNewDetail() 
    { 
     ContactData.Add(new ContactCardData()); 
    } 

    public void RemoveDetail(int position) 
    { 
     ContactData.RemoveAt(position); 
    } 

ViewModelContactData:

public class ContactCardData : DependencyObject 
{ 
    public int Category 
    { 
     get { return (int)GetValue(CategoryProperty); } 
     set { SetValue(CategoryProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for Category. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty CategoryProperty = 
     DependencyProperty.Register("Category", typeof(int), typeof(ContactCardData), new UIPropertyMetadata(0)); 

    public string Field 
    { 
     get { return (string)GetValue(FieldProperty); } 
     set { SetValue(FieldProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for Field. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty FieldProperty = 
     DependencyProperty.Register("Field", typeof(string), typeof(ContactCardData), new UIPropertyMetadata("")); 

    public string Value 
    { 
     get { return (string)GetValue(ValueProperty); } 
     set { SetValue(ValueProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for Value. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty ValueProperty = 
     DependencyProperty.Register("Value", typeof(string), typeof(ContactCardData), new UIPropertyMetadata("")); 

    public int Symbol 
    { 
     get { return (int)GetValue(SymbolProperty); } 
     set { SetValue(SymbolProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for Symbol. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty SymbolProperty = 
     DependencyProperty.Register("Symbol", typeof(int), typeof(ContactCardData), new UIPropertyMetadata(0)); 

    public ContactCardData() 
    { 
    } 

    public ContactCardData(string field, int category, int symbol, string value) 
    { 
     this.Symbol = symbol; 
     this.Category = category; 
     this.Field = field; 
     this.Value = value; 
    } 
} 

Répondre

4

Définir votre classe de fenêtre comme suit (explication sera plus tard):

<Window x:Class="TestCustomTab.Window1" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:TestCustomTab="clr-namespace:TestCustomTab" Title="Window1" Height="300" Width="300"> 
     <Window.Resources> 
      <DataTemplate x:Key="workingTemplate"> 
       <Grid> 
        <Grid.ColumnDefinitions> 
         <ColumnDefinition Width="Auto"/> 
         <ColumnDefinition Width="*"/> 
        </Grid.ColumnDefinitions> 
        <TextBlock Grid.Column="0" Text="{Binding Name}"/> 
        <ComboBox Grid.Column="1" SelectedIndex="0" ItemsSource="{Binding Symbols}"> 
         <ComboBox.ItemTemplate> 
          <DataTemplate> 
           <Grid> 
            <Grid.ColumnDefinitions> 
             <ColumnDefinition Width="Auto"/> 
             <ColumnDefinition Width="*"/> 
            </Grid.ColumnDefinitions> 
            <Ellipse Grid.Column="0" Fill="Red" Height="5" Width="5"/> 
            <TextBlock Grid.Column="1" Text="{Binding}" /> 
           </Grid> 
          </DataTemplate> 
         </ComboBox.ItemTemplate> 
        </ComboBox> 
       </Grid> 
      </DataTemplate> 

      <DataTemplate x:Key="personalTemplate"> 
       <Grid> 
        <Grid.ColumnDefinitions> 
         <ColumnDefinition Width="Auto"/> 
         <ColumnDefinition Width="*"/> 
        </Grid.ColumnDefinitions> 
        <TextBlock Grid.Column="0" Text="{Binding Name}"/> 
        <ComboBox Grid.Column="1" SelectedIndex="0" ItemsSource="{Binding Symbols}"> 
         <ComboBox.ItemTemplate> 
          <DataTemplate> 
           <Grid> 
            <Grid.ColumnDefinitions> 
             <ColumnDefinition Width="Auto"/> 
             <ColumnDefinition Width="*"/> 
            </Grid.ColumnDefinitions> 
            <Ellipse Grid.Column="0" Fill="Green" Height="5" Width="5"/> 
            <TextBlock Grid.Column="1" Text="{Binding}" /> 
           </Grid> 
          </DataTemplate> 
         </ComboBox.ItemTemplate> 
        </ComboBox> 
       </Grid> 
      </DataTemplate> 

      <TestCustomTab:ContactDataByTypeTemplateSelector x:Key="contactDataByTypeTemplateSelector"/> 
     </Window.Resources> 
     <Grid x:Name="grid">   
      <ListView ItemsSource="{Binding ContactDataCollection, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TestCustomTab:Window1}}}"> 
       <ListView.View> 
        <GridView> 
         <GridViewColumn Header="Column" Width="200" CellTemplateSelector="{StaticResource contactDataByTypeTemplateSelector}"> 

         </GridViewColumn> 
        </GridView> 
       </ListView.View> 
      </ListView> 
     </Grid> 
    </Window> 

laisse supposer que votre ContactData se présente comme suit:

public class ContactData : INotifyPropertyChanged 
    { 
     private string _name; 
     private int _homePhone; 
     private int _mobilePhone; 
     private string _value; 
     private ContactDataType _dataType; 

     public ContactData() 
     { 
     } 

     public ContactData(string name, int homePhone, int mobilePhone, string value, ContactDataType dataType) 
     { 
      _name = name; 
      _dataType = dataType; 
      _value = value; 
      _mobilePhone = mobilePhone; 
      _homePhone = homePhone; 
     } 

     #region Implementation of INotifyPropertyChanged 

     public ContactDataType DataType 
     { 
      get { return _dataType; } 
      set 
      { 
       if (_dataType == value) return; 
       _dataType = value; 
       raiseOnPropertyChanged("DataType"); 
      } 
     } 

     public string Name 
     { 
      get { return _name; } 
      set 
      { 
       if (_name == value) return; 
       _name = value; 
       raiseOnPropertyChanged("Name"); 
      } 
     } 

     public int HomePhone 
     { 
      get { return _homePhone; } 
      set 
      { 
       if (_homePhone == value) return; 
       _homePhone = value; 
       raiseOnPropertyChanged("HomePhone"); 
      } 
     } 

     public int MobilePhone 
     { 
      get { return _mobilePhone; } 
      set 
      { 
       if (_mobilePhone == value) return; 
       _mobilePhone = value; 
       raiseOnPropertyChanged("MobilePhone"); 
      } 
     } 

     public string Value 
     { 
      get { return _value; } 
      set 
      { 
       if (_value == value) return; 
       _value = value; 
       raiseOnPropertyChanged("Value"); 
       raiseOnPropertyChanged("Symbols"); 
      } 
     } 

     public ReadOnlyCollection<char> Symbols 
     { 
      get 
      { 
       return !string.IsNullOrEmpty(_value) ? new ReadOnlyCollection<char>(_value.ToCharArray()) : new ReadOnlyCollection<char>(new List<char>()); 
      } 
     } 

     public event PropertyChangedEventHandler PropertyChanged; 

     private void raiseOnPropertyChanged(string propertyName) 
     { 
      if (PropertyChanged != null) 
       PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
     } 

     #endregion 
    } 

Il a la propriété DataType de type ContactDataType qui est ENUM :

public enum ContactDataType 
    { 
     Working, 
     Personal 
    } 

En capacité d'avoir différents DataTemplates Pour les mêmes entités différenciées par certaines fonctionnalités, vous devez utiliser DataTemplateSelector. La technique hérite de DataTemplateSelector et de la méthode SelectTemplate de substitution.Dans notre cas:

public class ContactDataByTypeTemplateSelector : DataTemplateSelector 
    { 
     public override DataTemplate SelectTemplate(object item, DependencyObject container) 
     { 
      var contactData = item as ContactData; 
      var control = container as FrameworkElement; 
      if (contactData != null & control != null) 
       switch (contactData.DataType) 
       { 
        case ContactDataType.Working: 
         return control.TryFindResource("workingTemplate") as DataTemplate; 
        case ContactDataType.Personal: 
         return control.TryFindResource("personalTemplate") as DataTemplate; 
        default: 
         return base.SelectTemplate(item, container); 
       } 

      return base.SelectTemplate(item, container); 
     } 
    } 

est ici classe Window1 dans le code derrière:

public partial class Window1 : Window 
    { 
     private ObservableCollection<ContactData> _contactDataCollection = new ObservableCollection<ContactData>(); 

     public Window1() 
     { 
      ContactDataCollection.Add(new ContactData("test1", 0, 1, "value1", ContactDataType.Working)); 
      ContactDataCollection.Add(new ContactData("test2", 0, 1, "value2", ContactDataType.Working)); 
      ContactDataCollection.Add(new ContactData("test3", 0, 1, "value3", ContactDataType.Working)); 
      ContactDataCollection.Add(new ContactData("test4", 0, 1, "value4", ContactDataType.Personal)); 
      ContactDataCollection.Add(new ContactData("test5", 0, 1, "value5", ContactDataType.Personal)); 

      InitializeComponent(); 

     } 


     public ObservableCollection<ContactData> ContactDataCollection 
     { 
      get { return _contactDataCollection; } 
     } 
    } 

Maintenant explication:

  1. Nous avons créé une classe que nous devons représenter à l'utilisateur (ContactData) et laissez-lui avoir la caractéristique - ContactDataType.

  2. Nous avons créé 2 DataTemplates en ressources (x:Key est important) pour ContactDataType.Working et ContactDataType.Personal

  3. Nous avons créé DataTemplateSelector d'avoir des modèles de commutation de capacité par fonction.

  4. Dans notre premier GridViewColumn, nous avons défini et nous lions à notre ContactDataByTypeTemplateSelector. En exécution à chaque fois que la collection change ContactDataByTypeTemplateSelector sélectionnez-nous modèle basé sur la fonctionnalité de l'élément et nous pouvons avoir un certain nombre de modèles pour un certain nombre d'entités définies.

Remarque: modifiez TestCustomTab pour votre espace de noms.

+0

Salut, merci pour la réponse. Je ne me suis pas expliqué à 100%, il semble. J'ai ajouté mon XAML pour la liste et mon code, ce que je n'ai pas réussi à faire fonctionner, c'était les symboles. Je n'ai aucune idée de quoi lier cela. Je voudrais ajouter tout le contenu xml prédéfini à une classe externe et l'utiliser pour tous les liens de données prédéfinis. Mais comment puis-je lier une combobox à 2 datacontexts en même temps? Un pour appliquer l'index sélectionné en cours et un pour remplir la combobox avant qu'il ne soit affiché avec les valeurs par défaut. – Patrick

1

Pour la dernière question, vous pouvez utiliser un bouton et un modèle pour inclure une image.