I have a Windows WPF application, following a fairly standard MVVM architecture. I am having trouble getting a list of items to respond to changes in the properties they are bound to. Here are some key components that are involved in my problem:
public class Contact
{
public string Name
{
get
{
return _name;
}
set
{
SetProperty( ref _name, value );
OnPropertyChanged( nameof(IsNotPlaceholder) );
}
}
public string Telephone
{
get
{
return _telephone;
}
set
{
SetProperty( ref _telephone, value );
}
}
public bool IsNotPlaceholder { get { return _name.Length > 0; } }
private string _name = String.Empty;
private string _telephone = String.Empty;
}
public class CustomerViewModel : INotifyPropertyChanged
{
public ObservableCollection<Contact> Contacts { get { return SelectedCustomer.Contacts } }
}
So far, so good. Now, in the view, I want to display a list of contacts, and I want the
Name
field to be editable always but I want the
Telephone
field to become editable only when the contact record is not a placeholder. (The placeholders are used when the user creates a new contact, sort-of a dummy record that only gets saved to the database once it has been given a valid name.)
My XAML looks something like this:
<Control.Resources>
<ResourceDictionary>
<!--- One of two templates for displaying a contact. This version blocks editing of the record. -->
<DataTemplate x:Key="DataTemplateContact">
<Grid Grid.Row="0" Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="75"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Name:"/>
<TextBox Grid.Row="0" Grid.Column="1" Focusable="False" Text="{Binding Name}"/>
<TextBlock Grid.Row="1" Grid.Column="0" Text="Telephone:"/>
<TextBox Grid.Row="1" Grid.Column="1" Focusable="False" Text="{Binding Telephone}"/>
</Grid>
</DataTemplate>
<!--- One of two templates for displaying a contact. This one is switched in to allow record editing. -->
<DataTemplate x:Key="DataTemplateContactSelected">
<Grid Grid.Row="0" Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="75"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Name:"/>
<TextBox Grid.Row="0" Grid.Column="1" Focusable="True" Text="{Binding Name,UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock Grid.Row="1" Grid.Column="0" Text="Telephone:"/>
<TextBox Grid.Row="1" Grid.Column="1" Focusable="True" IsEnabled="{Binding IsNotPlaceholder}"
Text="{Binding Telephone,UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</DataTemplate>
<!--- This list item container is used to swap between the editable and non-editable templates above. -->
<Style TargetType="{x:Type ListBoxItem}" x:Key="ContactListContainerStyle">
<Setter Property="ContentTemplate" Value="{StaticResource DataTemplateContact}"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource DataTemplateContactSelected}" />
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</Control.Resources>
<ListBox
ItemsSource="{Binding Contacts}"
ItemContainerStyle="{StaticResource ContactListContainerStyle}"
SelectedItem="{Binding SelectedContact}"/>
Everything works as expected, with the exception that the
IsEnabled="{Binding IsNotPlaceholder}"
does not 'realise' when it needs to update itself. The
Contact
is in my model-layer. I have set this up so that any data changes fire an event, which in turn fires an event in the view-model layer. The event in the view-model layer calls
OnPropertyChanged( "" )
, which should cause all UI elements to re-evaluate their bindings. But still the
Telephone
field does not become editable.
How can I get this working?
What I have tried:
I have tried setting the
IsEnabled="true"
in
x:Key="DataTemplateContactSelected"
, and the
Telephone
field is editable when I select the contact in the list, so this part is working fine.
It is my binding to
IsNotPlaceholder
that is failing.