使用IConverter处理WPF / XAML / MVVM中的{NewItemPlaceholder}

时间:2017-08-27 07:40:13

标签: c# wpf xaml mvvm

这是我的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>

两个不同的'容器':

<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>

应用程序:

enter image description here

  1. 如何删除{NewItemPlaceholder}? [完成,下面的解决方案]。
  2. 如果在单击上表中的一个空行时单击添加新行(我仍然可以添加行),如何防止提及“{NewItemPlaceholder}”的绑定错误。
  3. 错误:

    ...Cannot convert '{NewItemPlaceholder}' from type 'NamedObject' to type 'CustomerExample.Model.Customer'...
    
    
    ...ConvertBack cannot convert value '{NewItemPlaceholder}' (type 'NamedObject'). BindingExpression:Path=SelectedCustomer; DataItem='CustomerViewModel'...
    

    我可以写一个IConverter实现,但是如何将它绑定到XAML?

    提前感谢: - )

    以下是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();
        }
    }
    

    并绑定到单个项目,XAML类似于:

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

    但我认为我需要将它“全局”添加到数据集合元素中,而不是添加到单个属性的位置。

2 个答案:

答案 0 :(得分:6)

您不需要绑定转换器。而是将ListBox绑定到包装Custumers集合的CollectionViewSource。 CollectionViewSource从源集合中跳过NewItemPlaceholder元素。

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

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

您也不需要为SelectedItem绑定创建转换器。只需设置Binding的TargetNullValue属性:

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

答案 1 :(得分:-1)

即使接受的答案对于 OP 的问题是正确的,但我需要一种更通用的方法来防止错误并仍然允许用户添加行。由于我将此 ValueConverter 用于许多不同的对象类型,因此我使用反射来确定目标类型的构造函数,并返回该类型的新对象。

public class IgnoreNewItemPlaceHolderConverter : IValueConverter
{
    private const string NewItemPlaceholderName = "{NewItemPlaceholder}";
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value == DependencyProperty.UnsetValue)
            return DependencyProperty.UnsetValue;

        return value;
    }
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value != null && value.ToString() == NewItemPlaceholderName)
        {
            var ctors = targetType.GetConstructors();

            // invoke the first public constructor with no parameters.
            return ctors[0].Invoke(new object[] { });
        }
        return value;
    }
}