2009-04-03 9 views
2

je dois passer beaucoup de temps à essayer de comprendre comment lier des données dans mon fichier XML au contrôle TreeView, mais je ne sais pas où commencer. J'ai même essayé de passer par Two-way binding of Xml data to the WPF TreeView et l'exemple de code de Josh Smith sur codeproject, mais je n'arrive toujours pas à comprendre comment commencer !!!données XML Binding à WPF TreeView contrôle

Je XML dans un fichier « C: \ SPDependencies.xml » (je peux changer le format si nécessaire) !!!:

<node type="SPDependencies" Name="SPDependencies"> 
     <node type="StoredProc" Name="SP1"> 
       <node type="OperationType" Name="Type1"> 
         <node type="TableName" Name="Table1"/> 
         <node type="TableName" Name="Table2"/> 
       </node> 
       <node type="OperationType" Name="Type2"> 
         <node type="TableName" Name="Table1"/> 
         <node type="TableName" Name="Table2"/> 
       </node> 
       ..... 
     </node> 
     <node type="StoredProc" Name="SP2"> 
       <node type="OperationType" Name="Type1"> 
       ... 
       ... 
     </node> 
</node> 

J'ai besoin pour l'afficher dans le contrôle TreeView dans la format suivant:

<SP1> 
    <Type1> 
     <Table1> 
     <Table2> 
     <Table3> 
    <Type2> 
     <Table1> 
     <Table2> 
     <Table3> 
<SP2> 
    <Type1> 
........ 

Merci, Abhi.

Répondre

3

Heres l'arbre:

<Window.Resources> 
    <HierarchicalDataTemplate DataType="node" 
           ItemsSource="{Binding XPath=node}"> 
     <TextBox Width="Auto" 
       Text="{Binding [email protected], UpdateSourceTrigger=PropertyChanged}" /> 
    </HierarchicalDataTemplate> 

    <XmlDataProvider 
     x:Key="xmlDataProvider" 
     XPath="node" Source="C:\Data.XML"> 
    </XmlDataProvider> 
</Window.Resources> 
<Grid> 
    <StackPanel> 
     <Button Click="Button_Click">Save</Button> 
      <TreeView 
       Width="Auto" 
       Height="Auto" 
       Name="treeview" 
       ItemsSource="{Binding Source={StaticResource xmlDataProvider}, XPath=.}"/> 
    </StackPanel> 
</Grid> 

J'ai ajouté un simple bouton pour enregistrer les modifications. Donc, pour votre méthode Button_Click dans le code derrière:

XmlDataProvider dataProvider = this.FindResource("xmlDataProvider") as XmlDataProvider; 
dataProvider.Document.Save(dataProvider.Source.LocalPath); 

Voir here pour un article sur la liaison de données et WPF.

+0

Je m'intéresse également à ce sujet. Y a-t-il quelque chose qui manque dans votre réponse? Je ne peux pas obtenir de données à afficher ... Merci. – Number8

+0

Je vous suggère de créer une nouvelle question et de poster votre code afin que nous puissions voir quel est le problème. – Crippeoblade

5

Compte tenu du fichier XML suivant:

<node type="SPDependencies" Name="SPDependencies"> 
    <node type="StoredProc" Name="SP1"> 
    <node type="OperationType" Name="Type1"> 
     <node type="TableName" Name="Table1"/> 
    </node> 
    <node type="OperationType" Name="Type2"> 
     <node type="TableName" Name="Table1"/> 
    </node> 
    </node> 
    <node type="StoredProc" Name="SP2"> 
    <node type="OperationType" Name="Type1"> 
    </node> 
    </node> 
</node> 

Vue:

<Window x:Class="Tree.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:local="clr-namespace:Tree" 
     Title="MainWindow" Height="350" Width="525"> 
    <Window.DataContext> 
     <local:ViewModel /> 
    </Window.DataContext> 
    <Window.Resources> 
     <HierarchicalDataTemplate x:Key="template"> 
      <TextBlock Text="{Binding [email protected]}" /> 
      <HierarchicalDataTemplate.ItemsSource> 
       <Binding XPath="node" /> 
      </HierarchicalDataTemplate.ItemsSource> 
     </HierarchicalDataTemplate> 
    </Window.Resources> 
    <Grid DataContext="{Binding Path=XmlData}"> 
     <TreeView ItemsSource="{Binding}" ItemTemplate="{StaticResource template}"> 
     </TreeView> 
    </Grid> 
</Window> 

Voir modèle:

public class ViewModel 
{ 
    public XmlDataProvider XmlData { get; set; } 

    public ViewModel() 
    { 
     XmlData = new XmlDataProvider(); 
     XmlData.Source = new Uri(@"C:\input.xml"); 
     XmlData.XPath = "node"; 
    } 
} 

Sortie:

tree view output

Si vous voulez seulement afficher les nœuds ci-dessous la racine, modifiez simplement le XPath à:

XmlData.XPath = "/node/node"; 
0

J'ai compris comment faire cela sans marcher sur TreeView.DataContext. La liaison est facile. Codebehind est presque aussi facile, mais avec un peu de chance.

Vous n'obtenez rien si vous liez ou affectez XmlDataProvider à ItemsSource. Ce n'est pas un IEnumerable (bien que ce soit INotifyPropertyChanged) et il n'y a pas de conversion implicite. Ce que vous voulez est XmlDataProvider.Data, qui est déclaré comme Object, mais je vois un type d'exécution de XmlDataCollection (qui hérite de ReadOnlyObservableCollection<XmlNode>).

MVVM

Reliure Data est facile. Je ne sais pas si c'est MVVM pur de mettre XmlDataProvider dans votre viewmodel, peut-être pas.

viewmodel:

public XmlDataProvider ViewModelXMLDataProp { ... } 

XAML

<TreeView 
    ItemsSource="{Binding ViewModelXMLDataProp.Data}" 
    ... 
    /> 

Fait - qui est, à moins que vous devez utiliser la propriété XPath du Binding. Si vous le faites, vous devez utiliser le DataContext kludge. Vous ne pouvez pas définir à la fois Path et XPath sur la même liaison. La propriété XPath de XmlDataProvider fait la même chose. Si vous pouvez travailler avec ça, vous êtes bon.

Vous penseriez que fonctionnerait, car cela fonctionne lorsque votre XmlDataProvider est une ressource statique. Lorsque Binding.Source est un DataSourceProvider et Path est non spécifiée, Path par défaut Data:

<TreeView 
    ItemsSource="{Binding Source={StaticResource MyXmlDataProviderResource}}" 
    ... 
    /> 

... mais cela fonctionne parce que vous donnez une ressource statique. Ce qui suit se lie réellement à la chaîne "ViewModelXMLDataProp" plutôt que de rechercher le DataContext pour une propriété de ce nom. Ce n'est pas bon.

<TreeView 
    ItemsSource="{Binding Source=ViewModelXMLDataProp}" 
    ... 
    /> 

Peut-être que vous pourriez écrire un MarkupExtension qui ferait ce travail, mais il n'y a pas besoin.

Codebehind

Vous devriez apprendre et à utiliser MVVM, mais les choses se passent pour beaucoup de raisons et vous ne suis pas venu ici pour un sermon.

Codebehind est un peu plus compliqué. TreeView.ItemsSource ne nécessite rien de plus que l'objet que vous lui donnez doit implémenter System.Collections.IEnumerable, alors lancez provider.Data à System.Collections.IEnumerable et ne vous inquiétez pas de savoir quel est le type d'exécution exact.

Maintenant, voici le gotcha: XmlDataProvider.Data est rempli de manière asynchrone.

protected void LoadXML(String path) 
{ 
    var provider = 
     new XmlDataProvider() 
     { 
      Source = new Uri(path, UriKind.Absolute), 
      XPath = "./*" 
     }; 

    // FAIL: provider.Data is still null 
    treeView.ItemsSource = (IEnumerable)provider.Data; 
} 

Je trouve cela un problème, même lorsque je crée un XmlDocument, appelez XmlDocument.Load(), et affecter le document à XmlDataProvider.Document. Un Binding restera toujours en attente lorsque la propriété Data sera définie, et il mettra à jour ItemsSource ensuite. Mais une affectation à ItemsSource dans votre code derrière le fichier ne fait pas une telle chose.

Contrairement à la croyance populaire omniprésente à Stack Overflow, dans le code suivant il n'y a pas de liaison qui se passe, et en aucun sens n'a rien été lié:

// NOT A BINDING 
treeView.ItemsSource = someRandomCollectionOfStuff; 

Si personne ne la création d'une instance de System.Windows.Data.Binding ou x:Bind, il est pas une liaison. Cette distinction est importante: "Utiliser la valeur actuelle de x" n'est pas le même concept que "mettre à jour indéfiniment avec les valeurs futures de y.x chaque fois y soulève PropertyChanged".

Vous pouvez créer par programme un Binding ou même gérer PropertyChanged, mais ils sont allés de l'avant et vous ont donné une option beaucoup plus simple. Il suffit de gérer l'événement XmlDataProvider.DataChanged.

protected void LoadXML(String path) 
{ 
    var provider = 
     new XmlDataProvider() 
     { 
      Source = new Uri(path, UriKind.Absolute), 
      XPath = "./*" 
     }; 

    provider.DataChanged += (s,e) 
     => treeView.ItemsSource = (IEnumerable)provider.Data; 
} 

Et c'est tout. Vous pouvez même conserver ce fournisseur, charger du nouveau code XML et avoir l'événement DataChanged pour garder l'arborescence à jour. On dirait que c'est un gaspillage d'effort.