Databinding between XML file and GUI
I've got a problem with my little app here that is conceptually very simple. I have an XML file that essentially just maps strings to strings.
Long-winded explanation warning
A sample file would look something like this:
<Candies>
<Sees>Box1</Sees>
<Hersheys>Box2</Hersheys>
<Godiva>Box3</Godiva>
</Candies>
Although I could use a different schema, like:
<Candies>
<Candy>
<Name>Sees</Name>
<Location>Box1</Location>
</Candy>
</Candies>
...I opted not to, since the former didn't have any forseeable adverse side effects.
开发者_运维问答In code behind, I load the contents of my XML file into an XDocument with LINQ. I also have a List variable defined, because this is what I'm databinding my GUI to. CandyLocation looks like this:
public class CandyLocation
{
public string Brand { get; set; }
public string Location { get; set; }
}
And my simple GUI is just this:
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="Auto" Width="Auto">
<Page.Resources>
<DataTemplate x:Key="CandyTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0" Text="{Binding Brand}" Margin="3"></TextBox>
<ComboBox Grid.Column="1" SelectedValue="{Binding Location}" ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Page}}, Path=DataContext.LocationNames}" Text="{Binding Location, Mode=TwoWay}" Margin="3"></ComboBox>
</Grid>
</DataTemplate>
</Page.Resources>
<DockPanel>
<Button DockPanel.Dock="Bottom" Margin="3" Command="{Binding SaveCandiesCommand}">Apply Changes</Button>
<Button DockPanel.Dock="Bottom" Margin="3" Command="{Binding AddNewCandyCommand}">Add Candy</Button>
<ListBox DockPanel.Dock="Top" ItemsSource="{Binding CandyLocations}" ItemTemplate="{StaticResource CandyTemplate}" />
</DockPanel>
</Page>
So the overview is this:
The application loads and then uses LINQ to load the XML file. When the GUI is presented, it calls "GetCandyLocations", which traverses the XML data and populates the List object with the contents of the XML file. Upon initial loading of the XML, the GUI renders properly (i.e. the candy brands and their locations appear correctly), but that's where the success story ends.
If I start from a blank XML file and add a brand, I do so by adding a new XElement to my XDocument root. Then I call OnPropertyChanged( "CandyLocations") to make the GUI update. The initial value for Location is "", so it's up to the user to select a valid location from the combobox. The problem is, I can't figure out how to get their selection databound correctly, such that I can update the XElement value. Because of this, when I save the candy locations, everything ends up with a blank location value. In addition, anytime the user clicks Add Candy, all of the previously selected location comboboxes get blanked out.
In summary:
- How should I handle the selection change in the GUI? I am using MVVM for this application, so I have avoided using the ComboBox's SelectionChanged event.
- Is there a way to databind directly from the GUI to the XDocument? I haven't tried it yet, but it would be best to avoid having multiple sources of data (i.e. XDocument for serialization and List for GUI rendering). Perhaps I can have the getter return the result of a LINQ query and pair it with a value converter???
- How would you change my implementation if you were to write this application? I'm still learning MVVM and WPF, so any advice would be really great.
Thanks!
On your ComboBox, it looks like you might be getting a conflict between the SelectedValue and Text properties. Text is usually only used with IsEditable="True". Try using just SelectedItem:
<ComboBox SelectedItem="{Binding Location}" ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Page}}, Path=DataContext.LocationNames}" ></ComboBox>
If you want to use the XDocument directly as your data source you can use this (assuming XDocument is exposed from the VM as AvailableLocations):
<ComboBox ItemsSource="{Binding Path=AvailableLocations.Root.Elements}" SelectedValue="{Binding Location}"
SelectedValuePath="Value" DockPanel.Dock="Top" DisplayMemberPath="Value"/>
If you'd rather do something like display the company names, just change DisplayMemberPath to "Name".
Also try using an ObservableCollection instead of a List for CandyLocations so you can get automatic change notifications when items are added or removed.
精彩评论