简体   繁体   English

WPF Datagrid展平集合对象的集合

[英]WPF Datagrid flatten collections of collection objects

I need your help. 我需要你的帮助。 I have a collection of objects which has collections inside and I need to display the values inside of them. 我有一个对象集合,里面有集合,我需要在其中显示值。

This is my collection Element. 这是我的收藏元素。 A List of them will later be bound to the Datagrid (ObjectBase is only an implementation of INotifyPropertyChanged) 它们的列表稍后将绑定到Datagrid(ObjectBase只是INotifyPropertyChanged的实现)

public class ElementContainer : ObjectBase
{
    public Element ContainerElement { get; set; }

    private ObservableCollection<State> _elementStates;

    public ObservableCollection<State> ElementStates
    {
        get { return _elementStates; }
        set { _elementStates = value; OnPropertyChanged(); }
    }
}

My elements: 我的元素:

public class Element
{
    public int Nbr { get; set; }
    public string Name { get; set; }
}

public class State: ObjectBase
{
    public string Name { get; set; }

    private bool _value;

    public bool Value
    {
        get { return _value; }
        set { _value = value; OnPropertyChanged(); }
    }

    public string StateType { get; set; }
}

The "Value" property of the State object is what I need get updated if it changed. State对象的“ Value”属性是我需要更改的内容。

My ViewModel: 我的ViewModel:

public class MainViewModel : ObjectBase
{

    private IList<ElementContainer> _enitites;

    public IList<ElementContainer> Enitites
    {
        get { return _enitites; }
        set { _enitites = value; OnPropertyChanged(); }
    }

    public MainViewModel()
    {
        Enitites = new ObservableCollection<ElementContainer>()
        {
            new ElementContainer() {
                                       ContainerElement =  new Element() {Nbr= 1, Name= "Test 1" },
                                       ElementStates = new ObservableCollection<State>()
                                       {
                                            new State() { Name = "On", Value = true,  StateType= "On" },
                                            new State() { Name = "Open", Value = false,  StateType= "Open" }
                                       }
                                    },

             new ElementContainer() {
                                       ContainerElement =  new Element() {Nbr= 2, Name= "Test 2" },
                                       ElementStates = new ObservableCollection<State>()
                                       {
                                            new State() { Name = "On", Value = false, StateType= "On" },
                                            new State() { Name = "Open", Value = true, StateType= "Open" }
                                       }
                                    }

         };
    }
}

Now the Datagrid should have following columns: 现在,Datagrid应该具有以下列:

Nbr, Name, On, Open Nbr,名称,开启,打开

The CellValue of the "On" and "Open" columns should be the value property of the State. “打开”和“打开”列的CellValue应该是State的value属性。

Any ideas? 有任何想法吗?

Edit: 编辑:

What I also tried is to create an AttachedProperty for creating the columns: 我还尝试了创建用于创建列的AttachedProperty:

public class ElementStatesGridExtensions
{
    public static ObservableCollection<ColumnConfig> GetColumnConfigs(DependencyObject obj)
    {
        return (ObservableCollection<ColumnConfig>)obj.GetValue(ColumnConfigsProperty);
    }

    public static void SetColumnConfigs(DependencyObject obj, ObservableCollection<ColumnConfig> value)
    {
        obj.SetValue(ColumnConfigsProperty, value);
    }


    public static readonly DependencyProperty ColumnConfigsProperty =
        DependencyProperty.RegisterAttached("ColumnConfigs", typeof(ObservableCollection<ColumnConfig>), typeof(ElementStatesGridExtensions), new PropertyMetadata(new PropertyChangedCallback(OnColumnsChanged)));

    static DeviceStateConverter converter = new DeviceStateConverter();

    static FrameworkElementFactory CreateFrameWorkFactoryWithBinding(ColumnConfig config)
    {
        var fwf = new FrameworkElementFactory(typeof(TextBlock));

        if (config != null && config.ColumnType != null)
        {

            if (config.ColumnType.Equals("State"))
            {
                fwf.SetBinding(TextBlock.TextProperty, new Binding("ElementStates")
                {
                    ConverterParameter = config.ColumnName,
                    Converter = converter,

                    UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
                    Mode = BindingMode.OneWay
                });
            }
        }
        return fwf;
    }


    static void OnColumnsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var dataGrid = d as DataGrid;
        dataGrid.Columns.Clear();


        dataGrid.Columns.Add(new DataGridTextColumn() { Header = "Nbr", Binding = new Binding("ContainerElement.Nbr") });
        dataGrid.Columns.Add(new DataGridTextColumn() { Header = "Name", Binding = new Binding("ContainerElement.Name") });


        foreach (var columnConfig in GetColumnConfigs(d))
        {
            dataGrid.Columns.Add(new DataGridTemplateColumn()
            {
                Header = columnConfig.ColumnHeader,
                CellTemplate = new DataTemplate()
                {
                    VisualTree = CreateFrameWorkFactoryWithBinding(columnConfig)
                }
            });
        }
    }
}

public class DeviceStateConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is ObservableCollection<stof.State> deviceStates && parameter != null)
        {
            var deviceState = deviceStates.FirstOrDefault(s => s.StateType?.ToUpper() == parameter.ToString().ToUpper());

            if (deviceState != null)
                return deviceState.Value;

        }

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

Then I attached this property to the datagrid. 然后,我将此属性附加到datagrid。 And added this ... 并添加了这个...

public void RefeshStates(object sender, EventArgs e)
    {
        OnPropertyChanged("ElementStates");
    }

to the ElementContainer class. 到ElementContainer类。

Then in the ViewModel I iterate through all entities and register the PropertyChanged event of every state to the ElementContainer. 然后,在ViewModel中,我遍历所有实体,并将每个状态的PropertyChanged事件注册到ElementContainer。 So now if the state value changes, the Container gets notified and the Collection will refresh. 因此,现在如果状态值更改,则将通知Container,并且Collection将刷新。 That's not a good solution. 那不是一个好的解决方案。 Also the states of all other elements will be updated. 同样,所有其他元素的状态也将被更新。

 foreach (ElementContainer container in Enitites)
        {

            foreach (State s in container.ElementStates)
            {
                s.PropertyChanged += container.RefeshStates;
            }
        }

Is ElementStates length always 2? ElementStates长度是否总是2? if yes than tere are two options: 如果是,则有两个选择:

  1. Option 1: You can include both State values into your ElementContainer class as Properties. 选项1:您可以将两个State值作为Properties包括在ElementContainer类中。
  2. Option 2: You can call something like this var q = Enitites.Select(e=>e.Nbr, e.Name, e.ElementStates.Where(es=>es.Name="On").First().Value, e.ElementStates.Where(es=>es.Name="Open").First().Value); 选项2:您可以调用类似这样的var q = Enitites.Select(e=>e.Nbr, e.Name, e.ElementStates.Where(es=>es.Name="On").First().Value, e.ElementStates.Where(es=>es.Name="Open").First().Value);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM