I'm trying to unit test a custom WPF control using NUnit. The control is a ListView of buttons, bound to a list (the DataContext is set up in the control's constructor).
I'd like to write tests which (e.g.) add items to the list and check that a new button gets added to the view, etc. However, when I add an item to the list in my NUnit test, it still reports that the ListView is empty. Everything works OK when I run my app, though.
I've included the relevant code below. What do I need to do to test this?
XAML:
<ListView x:Class="SoundBlock"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
ItemsSource="{Binding Path=Sounds}">
<ListView.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Title}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Class definition
public partial class SoundBlock : ListView
{
public SoundBlock(Board xiBoard)
{
// Initialize.
InitializeComponent();
// Set the data context for this block.
DataContext = xiBoard; // Board has an ObservableCollection<Sound>
// property called Sounds.
}
}
Test case
[Test]
public void TestAddSound()
{
board = new Board();
block = new SoundBlock(board);
Assert.AreEqual(0, block.Items.Count);
sound = new Sound("sound.mp3");
board.Sounds.Add(sound);
Assert.AreEqual(1, block.Items.Count); // This fails - count is still 0
}
See my question: Force binding in WPF
You have to force binding, it won't work until control is shown. You can put it into a new Window and show the window.
I'm not sure exactly what happens, and why it works, but if you use this little helper class to set the data context on the control that you wish to test, then it works.
public class DataContextHelper
{
public static void InjectDataContext(object element, object dataContext)
{
if (dataContext == null)
return;
if (element is FrameworkContentElement)
((FrameworkContentElement)element).DataContext = dataContext;
else if (element is FrameworkElement)
((FrameworkElement)element).DataContext = dataContext;
TriggerUpdateOfInMemoryView();
}
/// <summary>
/// Triggers an update to a view that exists only in memory, not on the screen.
/// </summary>
/// <remarks>
/// When setting data context or modifying the control tree of a view that exists in
/// memory, then those changes are not automatically visible when e.g. attempting to
/// print the view. This function will trigger the update of the view so, e.g. a print
/// will display the updated view.
/// </remarks>
public static void TriggerUpdateOfInMemoryView()
{
var dispatcher = Dispatcher.CurrentDispatcher;
dispatcher.Invoke(
DispatcherPriority.SystemIdle,
new DispatcherOperationCallback(arg => null),
null);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With