locked
How do I update WPF ListBox with elements of selected XML file RRS feed

  • Question

  • I have a WFP window that contains a ComboBox that is bound to a list of XML files and a ListBox that should display the elements of the selected XML file in the Combox. The TextBlock between the ComboBox and the ListBox is used to demonstrate that the binding to the selected value works. However, the ListBox displays the elements of the first XML file but does not update when the selection changes.

    What have I done wrong and what am I missing?

    Dialog window:

    <Window x:Class="ReinforcementPlates.Views.SteelPipesDialog"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:Views="clr-namespace:ReinforcementPlates.Views"
            mc:Ignorable="d"
            Title="Steel Pipe Catalogue" Height="400" Width="400"
        DataContext="{Binding ViewModel, RelativeSource={RelativeSource Self}}">
        <Grid Margin="5">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="95*"/>
                <RowDefinition Height="15*"/>
            </Grid.RowDefinitions>
            <StackPanel Orientation="Horizontal" Margin="2">
                <Label>Select a catalogue:</Label>
                <ComboBox ItemsSource="{Binding Files, Mode=OneWay}" DisplayMemberPath="Name" SelectedValue="{Binding SelectedFile}" SelectedValuePath="Name" Width="240" Height="20" Margin="0,3" IsSynchronizedWithCurrentItem="True"/>
            </StackPanel>
            <StackPanel Grid.Row="1" Orientation="Horizontal" Margin="2">
                <TextBlock Text="Selected catalog:" Margin="0,0,3,0"/>
                <TextBlock Text="{Binding SelectedFile}" Margin="3,0,0,0"/>
            </StackPanel>
            <ListBox Grid.Row="2" HorizontalAlignment="Center" ItemsSource="{Binding PipesFromSelectedFile, UpdateSourceTrigger=PropertyChanged}" Margin="2" IsSynchronizedWithCurrentItem="True">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="50"/>
                                <ColumnDefinition Width="60"/>
                                <ColumnDefinition Width="60"/>
                            </Grid.ColumnDefinitions>
                            <TextBlock Grid.Column="0" Text="{Binding Bore}"/>
                            <TextBlock Grid.Column="1" Text="{Binding OutsideDiameter}"/>
                            <TextBlock Grid.Column="2" Text="{Binding WallThickness}"/>
                        </Grid>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
            <StackPanel Grid.Row="3" Grid.ColumnSpan="3" Orientation="Horizontal" HorizontalAlignment="Center">
                <Button IsDefault="True" Height="20" MinWidth="75" Margin="10" Padding="1" Click="Accept_Button">Accept</Button>
                <Button IsCancel="True" Height="20" MinWidth="75" Margin="10" Padding="1" Click="Cancel_Button">Cancel</Button>
            </StackPanel>
        </Grid>
    </Window>

    Code behind:

    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.Shapes;
    using ReinforcementPlates.ViewModels;
    using System.IO;
    
    namespace ReinforcementPlates.Views {
        /// <summary>
        /// Interaction logic for SteelPipesDialog.xaml
        /// </summary>
        public partial class SteelPipesDialog : Window {
            public SteelPipesDialogViewModel ViewModel { get; set; } = new SteelPipesDialogViewModel();
    
            public SteelPipesDialog() {
                InitializeComponent();
            }
    
            private void Accept_Button(object sender, RoutedEventArgs e) {
    
            }
    
            private void Cancel_Button(object sender, RoutedEventArgs e) {
    
            }
    
        }
    }

    View model:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Collections.ObjectModel;
    using ReinforcementPlates.SteelPipeCatalogs;
    using System.IO;
    using System.Xml.Serialization;
    
    namespace ReinforcementPlates.ViewModels {
         public class SteelPipesDialogViewModel : BaseViewModel {
            private string dir = @".\SteelPipeCatalogs";
            private DirectoryInfo catalogDirectory;
            private string selectedFile;
            private readonly List<FileInfo> catalogFiles = new List<FileInfo>();
            private ObservableCollection<SteelPipeSize> pipesFromSelectedFile;
    
            public SteelPipesDialogViewModel() {
                catalogDirectory = new DirectoryInfo(dir);
                catalogFiles.AddRange(catalogDirectory.GetFiles("*.xml"));
            }
    
            public List<FileInfo> Files {
                get { return catalogFiles; }
            }
    
    
            public string SelectedFile {
                get { return selectedFile; }
                set {
                    if (selectedFile != value) {
                        selectedFile = value;
                        OnPropertyChanged(nameof(SelectedFile));
                        LoadFromXmlFile(selectedFile);
                    }
                }
            }
    
            public ObservableCollection<SteelPipeSize> PipesFromSelectedFile {
                get { return pipesFromSelectedFile; }
                set {
                    if (pipesFromSelectedFile != null) {
                        pipesFromSelectedFile = value;
                        OnPropertyChanged(nameof(PipesFromSelectedFile));
                    }
                }
            }
    
            private void LoadFromXmlFile(string selectedFile) {
                SteelPipeSizeList pipes = new SteelPipeSizeList();
                string fileName = Path.Combine(dir, selectedFile);
                XmlSerializer serializer = new XmlSerializer(typeof(SteelPipeSizeList));
    
                using (FileStream fStream = File.OpenRead(fileName)) {
                    pipes = serializer.Deserialize(fStream) as SteelPipeSizeList;
                }
    
                pipesFromSelectedFile = new ObservableCollection<SteelPipeSize>();
                foreach (var pipe in pipes.SteelPipes) {
                    pipesFromSelectedFile.Add(pipe);
                }
            }
        }
    }

    XML collection:

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Xml.Serialization;
    using ReinforcementPlates.Models;
    
    namespace ReinforcementPlates.SteelPipeCatalogs {
        [XmlRoot("SteelPipeSizeList")]
        public class SteelPipeSizeList {
    
            public SteelPipeSizeList() {
                SteelPipes = new List<SteelPipeSize>();
            }
    
            [XmlArray("SteelPipeSizes")]
            [XmlArrayItem("SteelPipeSize", typeof(SteelPipeSize))]
            public List<SteelPipeSize> SteelPipes { get; set; }
    
            public void AddPipeSize(SteelPipeSize size) {
                SteelPipes.Add(size);
            }
        }
    }
    

    XML element;

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace ReinforcementPlates.SteelPipeCatalogs {
        [Serializable]
        public class SteelPipeSize {
            public double Bore { get; set; }
            public double OutsideDiameter { get; set; }
            public double WallThickness { get; set; }
    
            public SteelPipeSize() { }
        }
    }

    Tuesday, October 13, 2020 11:44 AM

All replies

  • Try these changes:

    set {

       if (pipesFromSelectedFile != value) {

          pipesFromSelectedFile = value;

          OnPropertyChanged(nameof(PipesFromSelectedFile));

       }

    }

    . . .

    private void LoadFromXmlFile(string selectedFile) {

       . . .

       var selectedPipes = new ObservableCollection<SteelPipeSize>();

       foreach (var pipe in pipes.SteelPipes) {

          selectedPipes.Add(pipe);

       }

       PipesFromSelectedFile = selectedPipes;

    }


    • Edited by Viorel_MVP Tuesday, October 13, 2020 12:43 PM
    Tuesday, October 13, 2020 12:43 PM
  • Thanks for the excellent corrections.
    Tuesday, October 13, 2020 2:16 PM
  • This forum is for C#-specific questions only. Please post questions related to WPF in the new Microsoft Q&A forums

    Michael Taylor http://www.michaeltaylorp3.net

    Tuesday, October 13, 2020 4:57 PM