To get the values in your txt/csvfile is trivial (What's the fastest way to read a text file line-by-line?). To get these values to update your ComboBox
's contents is actually fairly involved, but the process is at the heart of WPF and MVVM.
The basic idea is to bind an ObservableCollection<string>
object to the ItemSource
property in your ComboBox
and fill it with the items in your text file. I read the file in the constructor of my view model (more on that below) and put all the lines in the Collection (which is bound to the box) on startup. You can also do this elsewhere dynamically if need be.
The usual way to wire it up to the GUI is to use a DataContext
in your MainWindow.xaml
file. Typically this is done using the Model View ViewModel (MVVM) pattern. The ViewModel is responsible for communicating between the business logic (Model) and the GUI (View). In WPF this is done by having the ViewModel handle event changes in the GUI and also notify the GUI when the data from the model changes. Here I do this with a class called Notifier
that implements INotifyPropertyChanged
. Then, ViewModel
inherits this class and can talk to the GUI via bindings in the xaml. There are other ways of doing the View notifying in WPF/MVVM, but I find this is the simplest. Every WPF project I work on has this class in it.
You will also need bindings for the SelectedItem
and SelectedIndex
properties in your ComboBox
. By binding these to properties that notify in your view model (via calling Update
in the setter) you can control all the behavior you need.
Note that I had to remove the StartupUri="MainWindow.xaml"
line in the App.xaml
file because I instantiate the MainWindow
object in the code behind.
Here is how I did it using the standard Visual Stduio WPF application template. Just to clarify how this conforms to MVVM: the 'View' is the GUI itself (defined in MainWindow.xaml
), the 'ViewModel' is the ViewModel
class, and the 'Model is trivial. It is just a static set of items from the text file to shove into the combobox.
App.xaml:
<Application x:Class="ComboBoxDemo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ComboBoxDemo">
<Application.Resources>
</Application.Resources>
</Application>
App.xaml.cs:
using System.Windows;
namespace ComboBoxDemo
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
ViewModel viewModel = new();
MainWindow mainWindow = new() { DataContext = viewModel };
mainWindow.Show();
}
}
}
MainWindow.xaml:
<Window x:Class="ComboBoxDemo.MainWindow"
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:local="clr-namespace:ComboBoxDemo"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance local:ViewModel}"
Title="MainWindow" Height="450" Width="800">
<Grid>
<ComboBox Grid.Column="0"
ItemsSource="{Binding Items, Mode=OneWay}"
SelectedItem="{Binding SelectedItem, Mode=OneWay}"
SelectedIndex="{Binding ItemIndex}"
Margin="5"/>
</Grid>
</Window>
MainWindow.xaml.cs
using System.Windows;
namespace ComboBoxDemo
{
public partial class MainWindow : Window
{
public MainWindow() => InitializeComponent();
}
}
ViewModel.cs
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Runtime.CompilerServices;
namespace ComboBoxDemo
{
public abstract class Notifier : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged = null;
protected void Update<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return;
field = value;
OnPropertyChanged(propertyName);
}
private void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChangedEventHandler? handler = PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class ViewModel : Notifier
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
// event handling for gui/business logic
// private fields for bound variables
private string _selectedItem = "";
private int _itemIndex = 0;
// properties for binding to GUI
public ObservableCollection<string> Items { get; } = new();
public string SelectedItem
{
get => _selectedItem;
set => Update(ref _selectedItem, value);
}
public int ItemIndex
{
get => _itemIndex;
set
{
Update(ref _itemIndex, value);
// here you can use the index to affect business logic as well
}
}
public ViewModel()
{
Items = new();
string textFile = "items.txt";
using StreamReader file = new StreamReader(textFile);
string? line;
while ((line = file.ReadLine()) is not null)
Items.Add(line);
}
}
}
and my combobox items come from 'items.txt' which I put in the same directory as the .exe:
item1
item2
item3
Screenshot of output:
