列中的WPF绑定不起作用

列中的WPF绑定不起作用,wpf,binding,Wpf,Binding,我有一个列表对象,如下所示: public class Device : ObjectBase { private int _DeviceNbr; public int DeviceNbr { get { return _DeviceNbr; } set { _DeviceNbr = value; } } private string _DeviceName; public string DeviceName

我有一个列表对象,如下所示:

public class Device : ObjectBase
{
    private int _DeviceNbr;

    public int DeviceNbr
    {
        get { return _DeviceNbr; }
        set { _DeviceNbr = value; }
    }

    private string _DeviceName;

    public string DeviceName
    {
        get { return _DeviceName; }
        set { _DeviceName = value; OnPropertyChanged(); }
    }

    private ObservableCollection<State> _DeviceStates;

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

public class State: ObjectBase
{
    public int StateNbr { get; set; }

    private string _stateType;

    public string StateType
    {
        get { return _stateType; }
        set { _stateType = value; }
    }

    private int _value;

    public int Value
    {
        get { return _value; }
        set { _value = value; OnPropertyChanged(); }
    }
}
public class CustomGrid : DataGrid
{
    public ObservableCollection<ColumnConfig> ColumnConfigs
    {
        get { return GetValue(ColumnConfigsProperty) as ObservableCollection<ColumnConfig>; }
        set { SetValue(ColumnConfigsProperty, value); }
    }

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

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

        dataGrid.Columns.Add(new DataGridTextColumn() { Header = "Nbr", Binding = new Binding("DeviceNbr") });
        dataGrid.Columns.Add(new DataGridTextColumn() { Header = "Device", Binding = new Binding("DeviceName") });

        foreach (var columnConfig in dataGrid.ColumnConfigs.Where(c => c.IsVisible))
        {
            var column = new DataGridTextColumn()
            {
                Header = columnConfig.ColumnHeader,
                Binding = new Binding("DeviceStates")
                {
                    ConverterParameter = columnConfig.ColumnName,
                    Converter = new DeviceStateConverter(),
                    UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
                    Mode =BindingMode.TwoWay
                }
            };
            dataGrid.Columns.Add(column);
        }
    }
}

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

            if (DeviceState != null)
                return DeviceState.Value;
        }
        return false;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
 public class MainViewModel : ObjectBase
{
    private ObservableCollection<Device> _Devices;

    public ObservableCollection<Device> Devices
    {
        get { return _Devices; }
        set { _Devices = value; OnPropertyChanged(); }
    }

    private ObservableCollection<ColumnConfig> _columnConfigs;

    public ObservableCollection<ColumnConfig> ColumnConfigs
    {
        get { return _columnConfigs; }
        set { _columnConfigs = value; OnPropertyChanged(); }
    }


    public MainViewModel()
    {
        Devices = new ObservableCollection<Device>();

        _columnConfigs = new ObservableCollection<ColumnConfig>()
        {
            new ColumnConfig(){ ColumnHeader = "On", ColumnName = "On", ColumnWidth= 100, IsVisible= true},
            new ColumnConfig(){ ColumnHeader = "Off", ColumnName = "Off", ColumnWidth= 100, IsVisible= true}

        };

        for ( int i = 0; i <= 100; i++)
        {
            _Devices.Add(new Device()
            {
                DeviceNbr = i,
                DeviceName = "Device " + i.ToString(),
                DeviceStates = new ObservableCollection<State>()
                {
                    new State() { StateType = "On", Value= i},
                    new State() { StateType = "Off", Value= i+1}
                }

            });
        }

        OnPropertyChanged("ColumnConfigs");
        OnPropertyChanged("Devices");

    }

    public void TestStateChange ()
    {

        Devices[2].DeviceName = "Device X";
        Devices[2].DeviceStates[0].Value = 5;
       // OnPropertyChanged("Devices");
    }


}
<local:CustomGrid
        AutoGenerateColumns="False"
        ColumnConfigs="{Binding ColumnConfigs}"
        ItemsSource="{Binding Devices, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}" />
因此,我必须在州的“父”集合中提出PropertyChangedEvent。真奇怪。 我现在唯一能想到的是在State类中实现一个事件,并让父集合对象订阅它


有人有更好的主意吗?

如果您的
状态
对象实现INotifyPropertyChanged(所有可绑定的数据对象都应该实现INotifyPropertyChanged),并且如果您的datagrid列直接绑定到状态对象(即通过包含索引的绑定路径),则您的UI将按预期更新


您可以从父
设备
对象订阅
状态
属性更改事件,然后从那里重新通知,但这是一种冗长的方法,意味着您必须订阅包含
状态
对象的
ObservableCollection
的CollectionChanged事件,以便您可以在每个
状态
对象上附加/取消附加PropertyChange事件(有时这是唯一的方法,但如果可以避免)。

如果您的
状态
对象实现了INotifyPropertyChanged(所有可绑定数据对象都应该实现),并且如果您的datagrid列直接绑定到状态对象(即通过包含索引的绑定路径),则您的UI将按预期更新


您可以从父
设备
对象订阅
状态
属性更改事件,然后从那里重新通知,但这是一种冗长的方法,意味着您必须订阅包含
状态
对象的
ObservableCollection
的CollectionChanged事件,以便您可以在每个
状态
对象上从PropertyChange事件附加/取消附加(有时这是唯一的方法,但如果可以,请避免).

什么是
ObjectBase
?它是否实现了
INotifyPropertyChanged
?您还需要为您的
DeviceName
列设置
TwoWayBinding
。您好。谢谢您的回答。是的,ObjectBase只是INotifyPropertyChanged实现。调用TestStateChange方法时,DeviceName会发生变化。噢,这是正确的ems我跳过了该部分。您是否尝试在Converter方法中设置断点,并观察值更改时是否调用该方法?值更改时将不会调用Convert方法。我还注释掉ConverterParameter,但没有成功。@XAMlMAX我不知道如何实现此目的。具体取决于设备将有不同数量的列。什么是
ObjectBase
?它是否实现了
INotifyPropertyChanged
?您还需要为
DeviceName
列设置
TwoWayBinding
。您好。感谢您的回答。是的,ObjectBase只是INotifyPropertyChanged实现。DeviceName更改为en TestStateChange方法被调用。哦,我似乎跳过了这一部分。您是否尝试在转换器方法中设置断点,并观察值更改时是否调用该方法?值更改时将不会调用Convert方法。我还注释了ConverterParameter,但没有成功。@XAMlMAX我不知道如何操作为了实现这一点。根据设备的类型,将有不同数量的列。“如果您的状态对象实现INotifyPropertyChanged(所有可绑定数据对象都应实现INotifyPropertyChanged),并且如果您的datagrid列直接绑定到状态对象(即通过包含索引的绑定路径),则您的UI将按预期更新。”您能告诉我如何在我的情况下做到这一点吗?“如果您的状态对象实现INotifyPropertyChanged(所有可绑定的数据对象都应该实现INotifyPropertyChanged),并且如果您的datagrid列直接绑定到状态对象(即通过包含索引的绑定路径),则您的UI将按预期更新。”你能告诉我如何在我的情况下做到这一点吗?
public void TestStateChange()
    {
        foreach (var device in Devices)
        {
            foreach (var state in device.DeviceStates)
            {
                state.Value = state.Value + 1;
            }

            device.OnPropertyChanged("DeviceStates");
        }
    }