JohnG79 JohnG79 - 2 months ago 53
C# Question

Using a IConverter to deal with {NewItemPlaceholder} in WPF / XAML / MVVM

Here is my DataTemplate:

<UserControl.Resources>
<converter:PlaceholderConverter x:Key="_placeholderConverter"/>

<!-- Data(Display)Template for data objects of x:Type Customer-->
<DataTemplate DataType="{x:Type model:Customer}">
<!-- Customer Properties will be vertically stacked -->
<ContentControl >
<StackPanel>
<TextBlock Text="{Binding FirstName}"/>
<TextBlock Text="{Binding LastName}"/>
<TextBlock Text="{Binding Phone}"/>
</StackPanel>
</ContentControl>
</DataTemplate>
<UserControl.Resources>


And the two different 'container's:

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="25"/>
<RowDefinition Height="100"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>

<Button Grid.Row="0"
Content="Delete"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="75"
Command="{Binding DeleteCommand}"/>

<DataGrid Grid.Row="1"
ItemsSource="{Binding Customers}"
SelectedItem="{Binding SelectedCustomer}"
AutoGenerateColumns="True"/>

<ListBox
Grid.Row="2"
ItemsSource="{Binding Customers, Mode=OneWay}"/>
</Grid>


And the app:

enter image description here


  1. How to remove the {NewItemPlaceholder}? [Done, solution below].

  2. How to prevent the binding error that mention "{NewItemPlaceholder}" when clicking in one of the empty rows in the table above intending on adding a new row (I can still add rows).



The errors:

...Cannot convert '{NewItemPlaceholder}' from type 'NamedObject' to type 'CustomerExample.Model.Customer'...


...ConvertBack cannot convert value '{NewItemPlaceholder}' (type 'NamedObject'). BindingExpression:Path=SelectedCustomer; DataItem='CustomerViewModel'...


I can write an IConverter implementation, but how to tie it in to the XAML?

thanks in advance :-)

Here is the implementation of the IConverter:

public class PlaceholderConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null && value.ToString() == "{NewItemPlaceholder}")
return DependencyProperty.UnsetValue;
return value;
}

public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}


and to bind to individual items, the XAML goes something like:

<TextBlock Text="{Binding Name, Converter={StaticResource PlaceholderConverter}}"/>


But I think I need to add it 'globally' to the data collection elements, not to where individual properties are being bound.

Answer Source

You don't need a Binding Converter. Instead, bind the ListBox to a CollectionViewSource that wraps the Custumers collection. The CollectionViewSource skips the NewItemPlaceholder element from the source collection.

<UserControl.Resources>
    ...
    <CollectionViewSource x:Key="CustomersCVS" Source="{Binding Customers}"/>
</UserControl.Resources>

...
<ListBox ItemsSource="{Binding Source={StaticResource CustomersCVS}}"/>

You also don't need a Converter for the SelectedItem Binding. Just set the Binding's TargetNullValue property:

<DataGrid SelectedItem="{Binding SelectedCustomer,
    TargetNullValue={x:Static CollectionView.NewItemPlaceholder}}" .../>