Click here to Skip to main content
15,569,547 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
Following the MVVM pattern, I have a list that contains radio buttons, the state of which is bound to a boolean property in a list of items in the view-model. I have logic that makes changes in the view-model, but I cannot get the UI to recognise these changes.

I have found variations of this problem online but cannot find anything as simple as my example. I don't want to over-complicate the code, so I am hoping there is a simple solution to this.

My real code is way too big to post, but I have managed to construct the following minimal example that shows the same behaviour.

My XAML:

<Window x:Class="WpfApplication2.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:WpfApplication2"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">

	<Control.Resources>
		<ResourceDictionary>
			<DataTemplate x:Key="ProgressTrackingDataTemplate">
				<Grid Margin="0">
					<Grid.RowDefinitions>
						<RowDefinition Height="Auto"/>
					</Grid.RowDefinitions>
					<Grid.ColumnDefinitions>
						<ColumnDefinition Width="*"/>
						<ColumnDefinition Width="Auto"/>
					</Grid.ColumnDefinitions>
					<Label Grid.Row="0" Grid.Column="0" Content="{Binding Name}" Margin="0" Padding="2,2,2,2"/>
					<Viewbox Grid.Row="0" Grid.Column="1" Height="20">
						<RadioButton IsChecked="{Binding State,Mode=TwoWay}"/>
					</Viewbox>
				</Grid>
			</DataTemplate>
			<Style x:Key="ProgressTrackingListBoxItemStyle" TargetType="ListBoxItem">
				<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
				<Setter Property="Focusable" Value="False"/>
			</Style>
		</ResourceDictionary>
	</Control.Resources>

	<Grid>
		<Grid.RowDefinitions>
			<RowDefinition/>
			<RowDefinition Height="30"/>
		</Grid.RowDefinitions>
		<Grid.ColumnDefinitions>
			<ColumnDefinition/>
		</Grid.ColumnDefinitions>
		<ListView Grid.Row="0" Grid.Column="0"
			ItemsSource="{Binding ProgressValues}"
			ItemTemplate="{StaticResource ProgressTrackingDataTemplate}"
			ItemContainerStyle="{StaticResource ProgressTrackingListBoxItemStyle}"/>
		<Button Grid.Row="1" Grid.Column="0" Content="Change selected item" Click="Button_Click"/>
	</Grid>
</Window>


The code-behind:

C#
public partial class MainWindow : Window
{
	public MainWindow()
	{
		InitializeComponent();
		DataContext = _myViewModel;
	}
	private void Button_Click( object sender, RoutedEventArgs e )
	{
		_myViewModel.ChangeSelectedItem();
	}
	private MyViewModel _myViewModel = new MyViewModel();
}


The view-model:

C#
using System.Collections.Generic;
using System.ComponentModel;

namespace WpfApplication2
{
	public class ProgressValue
	{
		public string Name { get; set; } = "";
		public bool State { get; set; } = false;
	}

	public class MyViewModel : INotifyPropertyChanged
	{
		public MyViewModel()
		{
			_progressValues[ _selectedItemIndex ].State = true;
		}

		public event PropertyChangedEventHandler PropertyChanged;

		public List<ProgressValue> ProgressValues { get { return _progressValues; } }

		public void ChangeSelectedItem()
		{
			_selectedItemIndex++;
			if( _selectedItemIndex == _progressValues.Count )
				_selectedItemIndex = 0;
			foreach( var progressValue in _progressValues )
				progressValue.State = false;
			_progressValues[ _selectedItemIndex ].State = true;
			PropertyChanged?.Invoke( this, new PropertyChangedEventArgs( nameof(ProgressValues) ) );
		}

		private int _selectedItemIndex = 0;

		private List<ProgressValue> _progressValues = new List<ProgressValue>()
		{
			new ProgressValue() { Name = "Not Started", State = false },
			new ProgressValue() { Name = "Underway", State = false },
			new ProgressValue() { Name = "Completed", State = false },
			new ProgressValue() { Name = "Invoiced", State = false }
		};
	}
}


Note that the initial selection that I perform in the constructor of the view-model is reflected in the UI, which implies there must be a way to get this working.

Any help would be very much appreciated.

Kind wishes ~ Patrick

What I have tried:

I have tried having the radio buttons in a group and not having them in a group.

I have tried various ways to raise the PropertyChanged event, including passing an empty string in the PropertyChangedEventArgs, which I thought was a way to tell the UI to re-evaluate all bindings?
Posted
Updated 30-Mar-17 6:41am
Comments
Richard Deeming 30-Mar-17 10:28am    
I suspect your ProgressValue class will also need to implement INotifyPropertyChanged, and raise the PropertyChanged event when the State property changes.
Patrick Skelton 30-Mar-17 11:37am    
You, sir, are a star! I just didn't think of that. Works a treat (and has saved me hours over the horrible alternatives I was looking at). Thank you!

I'd accept this as the answer, but I don't know how to do that with a comment.
Richard Deeming 30-Mar-17 11:38am    
I'll post it as an answer then! :)

1 solution

As mentioned in the comments, the ProgressValue class needs to implement INotifyPropertyChanged, and raise the PropertyChanged event when the State property changes.
public class ProgressValue : INotifyPropertyChanged
{
    private bool _state;
    
    public event PropertyChangedEventHandler PropertyChanged;
    
    public string Name { get; set; } = "";
    
    public bool State 
    { 
        get
        {
            return _state;
        }
        set
        {
            _state = value;
            PropertyChanged?.Invoke( this, new PropertyChangedEventArgs( nameof(State) ) );
        }
    }
}
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900