Reputation: 159
In my project, I wanted a UserControl
that can display different formats of an image (bitmap, svg), selected from a ListBox
. The SelectedItem
of the ListBox
is bound to the appropriate view model, which in turn changes the DataContext
of the UserControl
, and what I want to achieve is for it to change the displaying control (an Image
for bitmaps, a SharpVectors.SvgViewBox
for svg files) through data templates. It does so, but raises data binding errors, as if the templates were still intact whilst the UserControl
's DataContext
has already been changed.
I should like to a) avoid any data binding errors even if they cause no visible problems b) understand what is happening, so I prepared a MWE, which, to my surprise, displays the same behaviour, so I can present it here.
My minimal UserControl
is as follows:
<UserControl x:Class="BindingDataTemplateMWE.VersatileControl"
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"
xmlns:system="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<DataTemplate DataType="{x:Type system:String}">
<TextBlock Text="{Binding Content.Length, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContentControl}}" />
</DataTemplate>
<DataTemplate DataType="{x:Type system:DateTime}">
<TextBlock Text="{Binding Content.DayOfWeek, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContentControl}}" />
</DataTemplate>
</UserControl.Resources>
<Grid>
<ContentControl
Content="{Binding .}" />
</Grid>
</UserControl>
The MainWindow
that references this UserControl
has the following XAML:
<Window x:Class="BindingDataTemplateMWE.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:BindingDataTemplateMWE"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListBox
Grid.Column="0"
SelectedItem="{Binding Selected}"
ItemsSource="{Binding Items}" />
<local:VersatileControl
Grid.Column="1"
DataContext="{Binding Selected}" />
</Grid>
</Window>
with the following code-behind (to make the MWE indeed minimal, I made the window its own DataContext
, but originally there is a dedicated view model):
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
namespace BindingDataTemplateMWE
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
private object selected;
public event PropertyChangedEventHandler PropertyChanged;
public List<object> Items { get; }
public object Selected {
get { return selected; }
set {
selected = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Selected)));
}
}
public MainWindow()
{
InitializeComponent();
DataContext = this;
Items = new List<object>() { "a string", DateTime.Now, "another string" };
}
}
}
When I select a different item in the list, the desired effect takes place: the UserControl
displays the length if a string
is selected and the day of week when a DateTime
. Still, I get the following binding error when selecting a DateTime
after a string
:
Length property not found on object of type DateTime.
and conversely, selecting a string
after a DateTime
yields
DayOfWeek property not found on object of type String.
It is clear that what I am doing is not meant to be done, but I do not know what the correct paradigm is and what happens in the background. Please advise me. Thank you.
Upvotes: 2
Views: 399
Reputation: 21999
I've seen this problem often when creating complex data templates (several levels of nesting) when views are loaded/unloaded. Honestly, some of such errors I am ignoring completely.
In your case something similar happens because you are manipulating DataContext
directly. At the moment the new value is set, the previous value is still used in bindings, which monitor for source change and will try to update the target.
In your scenario you don't need this constant monitoring, so an easy fix is to use BindingMode.OneTime:
<DataTemplate DataType="{x:Type system:String}">
<TextBlock Text="{Binding Content.Length, RelativeSource={RelativeSource AncestorType=ContentControl}, Mode=OneTime}" />
</DataTemplate>
Upvotes: 2