【问题标题】:WPF DataGrid, customizing the display with a converterWPF DataGrid,使用转换器自定义显示
【发布时间】:2017-07-06 03:56:29
【问题描述】:

我有一个独特的情况。在一个类中,我有一个内部类,它几乎只是一个“显示”类。在外部类中有一个名为 GetDisplayObject 的方法,它返回内部类的类型。

我正在尝试使用外部类绑定到数据网格,但通过使用转换器,我想获得正确的显示。这样我就不必在我们的应用程序中更改一堆代码,只需在几个 .xaml 文件中添加几行即可。

我制作了一个小测试应用程序,它在最基本的层面上总结了我的问题。理想情况下,我想通过使用转换器并仅将值返回作为显示来解决问题,这样当我使用 SelectedItem 时,我就不必更改大量取决于该特定类型的代码(在这种情况是 DataObject 类型)。

这是我一直在处理的对象

namespace TestApp
{
    public class DataObject
    {
        public class DataObjectDisplay
        {
            public string ObjectDisplay { get; set; }
        }

        // props
        public int Id { get; set; }
        public object Object1 { get; set; }
        public object Object2 { get; set; }

        // method for getting the display
        public DataObjectDisplay GetDisplayObject()
        {
            DataObjectDisplay display = new DataObjectDisplay();

            // logic for determining which object should be displayed
            if(Object1 == null)
            {
                display.ObjectDisplay = "Object1";
            }
            else
            {
                display.ObjectDisplay = "Object2";
            }
            return display; 
        }
    }
}

这是我的 xaml 背后的代码

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;

namespace TestApp
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
    public MainWindow()
    {
        this.DataContext = this;
        InitializeComponent();

        this.DataObjectCollection = new ObservableCollection<DataObject>();
        this.DataObjectCollection.Add(new DataObject() { Id = 1, Object1 = "this", Object2 = "that" });
        this.DataObjectCollection.Add(new DataObject() { Id = 1, Object2 = "that" });

        this.SelectedItem = new DataObject();
    }

    private ObservableCollection<DataObject> dataObjectCollection;
    private DataObject selectedItem;

    public ObservableCollection<DataObject> DataObjectCollection
    {
        get { return this.dataObjectCollection; }
        set
        {
            dataObjectCollection = value;
            OnNotifyPropertyChanged("DataObjectCollection");
        }
    } 

    public DataObject SelectedItem
    {
        get { return this.selectedItem; }
        set
        {
            selectedItem = value;
            OnNotifyPropertyChanged("SelectedItem");
        }
    }


    public event PropertyChangedEventHandler PropertyChanged;
    private void OnNotifyPropertyChanged(string property = "")
    {
        if(PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(property));
        }
    }
}
}

这是 xaml(这就像我想要做的事情,使用 itemtemplate 或类似的东西,然后是调用此 GetDisplay 函数的转换器)

<Window x:Class="TestApp.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:TestApp"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">
<Window.Resources>
    <local:DataObjectToDisplayDataObjectConverter x:Key="ToDisplayConverter"/>
</Window.Resources>
<StackPanel>
    <DataGrid ItemsSource="{Binding DataObjectCollection}" SelectedItem="{Binding SelectedItem}" MinHeight="200">
        <DataGrid.ItemTemplate>
            <DataTemplate>
                <ContentPresenter Content="{Binding Converter={StaticResource ToDisplayConverter}}"/>
            </DataTemplate>
        </DataGrid.ItemTemplate>
    </DataGrid>
</StackPanel>
</Window>

最后是转换器

using System;
using System.Globalization;
using System.Windows.Data;

namespace TestApp
{
public class DataObjectToDisplayDataObjectConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value != null && value.GetType() == typeof(DataObject))
        {
            DataObject dataObj = (DataObject)value;
            dataObj.GetDisplayObject();
            return dataObj;
        }
        return "Invalid Value";
    }

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

我愿意接受建议,但这是一个小例子。在我们的实际应用程序中,更改一件事很可能会引发巨大的考验。

【问题讨论】:

  • 首先将this.DataContext = this; 放在InitializeComponent(); 之后。我希望您仅将窗口用作测试应用程序的视图模型,而不是其他任何地方。其次,你的问题是什么?您已经描述了您的应用程序的架构,但它有什么问题?不要使用value.GetType() == typeof(DataObject)。有is 和as 运算符。您不应为 DataGrid 指定 ItemTemplate。如果需要自定义单元格内容,则需要指定列并为它们定义CellTemplate。
  • 是的,我只是快速地把它放在一起,试图为这种情况提供一些体面的背景。澄清一下,我的问题是我不想将我的 itemsSource 更改为 DataObjectDisplay 的集合。但这就是我想要在 UI 上呈现的内容。
  • 是的,您不想更改ItemsSource。但是您已经自己为您的任务提供了解决方案:转换器。你问这样行吗?这是您的实际问题吗?
  • 我正在寻找一种简单的解决方案,因此我不必更改很多东西。我希望有像 itemTemplate 这样简单的东西。但是,看起来在每列单元格模板上都有一个转换器可能是最简单的解决方案

标签: c# wpf xaml datagrid ivalueconverter


【解决方案1】:

如果我是你,我会改变视图与 ViewModel 的紧密耦合,比如创建新的 MainWindowViewModel 类并在其中包含所有属性。

我看到GetDisplayObject方法的另一件事,从转换器调用这样的方法有什么需要。

您可以重构此代码并放入转换器中,如下所示。

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
    DataObject dataObj = value as DataObject;

    if (value == null)
    {
        return "Invalid Value";
    }

    if (dataObj.Object1 == null)
    {
        return "Object1";
    }

    return "Object2";
 }

【讨论】:

  • 我们确实遵循 MVVM 模式,但我认为将一个带有代码的测试应用程序放在一起很容易。老实说,我认为将逻辑移入转换器并不是一个坏主意。它只是逻辑,也可以用于任何其他类型的应用程序,因此它非常适合应用程序的较低层
【解决方案2】:

幸运的是,如果一个对象为 null 并且它的相关字符串为空,我能够将优先级绑定与转换器一起使用以返回 DependencyProperty.UnsetValue 并强制它使用下一个绑定。我认为这是针对这种情况的最佳解决方案。

所以 xaml 最终看起来像这样。

    <DataGrid ItemsSource="{Binding DataObjectCollection}" SelectedItem="{Binding SelectedItem}" MinHeight="200">
        <DataGrid.Columns>
            <DataGridTemplateColumn>
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock>
                            <TextBlock.Text>
                                <PriorityBinding>
                                    <Binding Path="Object1" Converter="{StaticResource EmptyStringToDependencyPropertyUnset}"/>
                                    <Binding Path="Object2"/>
                                </PriorityBinding>
                            </TextBlock.Text>
                        </TextBlock>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>

转换器最终变成了这样

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if(value == null || value as string == string.Empty)
        {
            return DependencyProperty.UnsetValue;
        }
        return value;
    }

【讨论】:

    猜你喜欢
    • 2023-04-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-13
    • 1970-01-01
    相关资源
    最近更新 更多