【问题标题】:Using a IConverter to deal with {NewItemPlaceholder} in WPF / XAML / MVVM在 WPF / XAML / MVVM 中使用 IConverter 处理 {NewItemPlaceholder}
【发布时间】:2017-08-27 07:40:13
【问题描述】:

这是我的数据模板:

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

应用程序:

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

错误:

...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}}"/>

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

【问题讨论】:

  • 我假设您想添加新行?
  • 是的@Blacktempel ...我只是不知道在哪里可以绑定转换器。 (底部显示是只读的。)
  • 我在你的代码中没有看到任何IConverter...你能提供更多关于你如何使用它的细节吗?
  • @Grx70 ... 这是你的 IConverter。我只是不知道要放, Converter={StaticResource PlaceholderConverter}
  • 请注意,Customer DataTemplate 中的 ContentControl 是多余的。

标签: c# wpf xaml mvvm


【解决方案1】:

您不需要绑定转换器。相反,将 ListBox 绑定到包装客户集合的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}}" .../>

【讨论】:

    【解决方案2】:

    尽管接受的答案对于 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;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2010-12-05
      • 1970-01-01
      • 1970-01-01
      • 2011-10-19
      • 1970-01-01
      • 1970-01-01
      • 2012-01-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多