简体   繁体   English

ObservableCollection CollectionChanged更新datagrid

[英]ObservableCollection CollectionChanged to update datagrid

I have a DataGrid which is bounded to an ObservableCollection . 我有一个DataGrid ,它绑定到一个ObservableCollection I have a list of data that is dynamic so items are being edited/added/removed from the list. 我有一个动态数据列表,因此正在编辑/添加/删除列表中的项目。 At first, I was clearing and adding the ObservableCollection but then I found out that I can refresh the ObservableCollection and I need to implement CollectionChanged for that but I am not sure how so if any body can give some pointers or sample codes that would be great. 起初,我正在清理并添加ObservableCollection但后来我发现我可以刷新ObservableCollection并且我需要为此实现CollectionChanged但是我不知道如果有任何正文可以提供一些指针或示例代码。

    private List<OrderList> m_OrderListData = new List<OrderList>();
    public List<OrderList> OrderListData
    {
        get => m_OrderListData;
        private set => Set(ref m_OrderListData, value);
    }

    private ObservableCollection<OrderList> m_OrderListDataCollection;
    public ObservableCollection<OrderList> OrderListDataCollection
    {
        get => m_OrderListDataCollection;
        private set => Set(ref m_OrderListDataCollection, value);
    }

    ...
    ...

    m_OrderListDataCollection = new ObservableCollection<OrderList>(m_OrderListData as List<OrderList>);

    ...
    ...


    foreach (OrderListViewModel Order in OrderList)
    {
        OrderListData.Add(new OrderList(Order.Description, Order.OrderId));
    }

This is what i had before 这就是我以前的所作所为

OrderListData.Clear();
foreach (OrderListViewModel Order in OrderList)
{
      OrderListData.Add(new OrderList(Order.Description, Order.OrderId));
}
m_OrderListDataCollection.Clear();
OrderListData.ToList().ForEach(m_OrderListDataCollection.Add);

XAML XAML

                <Label Content="OrderList"/>
                <DataGrid Name="dgOrderList" 
                          AutoGenerateColumns="False" 
                          ItemsSource="{Binding Path=OrderListDataCollection}" 
                          IsReadOnly="True"
                          SelectionMode="Single"
                          SelectionUnit="FullRow">
                    <DataGrid.Columns>
                        <DataGridTextColumn Width="Auto" Header="ID" Binding="{Binding OrderId}"/>
                        <DataGridTextColumn Width="*" Header="Description" Binding="{Binding OrderDescription}"/>
                    </DataGrid.Columns>
                </DataGrid>

EDIT: OrderList Class 编辑:OrderList类

public class OrderList : INotifyPropertyChanged
{

    private string m_OrderDescription;
    private string m_OrderId;

    public string OrderDescription
    {
        get => m_OrderDescription;
        set => Set(ref m_OrderDescription, value);
    }

    public string OrderId
    {
        get => m_OrderId;
        set => Set(ref m_OrderId, value);
    }

    #region Constructor
    public OrderList()
    {
    }
    public OrderList(string description, string id)
    {
        m_OrderDescription = description;
        m_OrderId = id;
    }
    #endregion

    #region INotifyPropertyChanged

    /// <summary>Updates the property and raises the changed event, but only if the new value does not equal the old value. </summary>
    /// <param name="PropName">The property name as lambda. </param>
    /// <param name="OldVal">A reference to the backing field of the property. </param>
    /// <param name="NewVal">The new value. </param>
    /// <returns>True if the property has changed. </returns>
    public bool Set<U>(ref U OldVal, U NewVal, [CallerMemberName] string PropName = null)
    {
        VerifyPropertyName(PropName);
        return Set(PropName, ref OldVal, NewVal);
    }

    /// <summary>Updates the property and raises the changed event, but only if the new value does not equal the old value. </summary>
    /// <param name="PropName">The property name as lambda. </param>
    /// <param name="OldVal">A reference to the backing field of the property. </param>
    /// <param name="NewVal">The new value. </param>
    /// <returns>True if the property has changed. </returns>
    public virtual bool Set<U>(string PropName, ref U OldVal, U NewVal)
    {
        if (Equals(OldVal, NewVal))
        {
            return false;
        }

        OldVal = NewVal;
        RaisePropertyChanged(new PropertyChangedEventArgs(PropName));
        return true;
    }

    /// <summary>Raises the property changed event. </summary>
    /// <param name="e">The arguments. </param>
    protected virtual void RaisePropertyChanged(PropertyChangedEventArgs e)
    {
        var Copy = PropertyChanged;
        Copy?.Invoke(this, e);
    }

    /// <summary>
    /// Raised when a property on this object has a new value.
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// Warns the developer if this object does not have
    /// a public property with the specified name. This
    /// method does not exist in a Release build.
    /// </summary>
    [Conditional("DEBUG")]
    [DebuggerStepThrough]
    protected virtual void VerifyPropertyName(string PropertyName)
    {
        // Verify that the property name matches a real,
        // public, instance property on this object.
        if (TypeDescriptor.GetProperties(this)[PropertyName] == null)
        {
            string ErrorMsg = "Invalid Property Name: " + PropertyName + "!";

            if (ThrowOnInvalidPropertyName)
            {
                throw new Exception(ErrorMsg);
            }

            Debug.Fail(ErrorMsg);
        }
    }

    /// <summary>
    /// Returns whether an exception is thrown, or if a Debug.Fail() is used
    /// when an invalid property name is passed to the VerifyPropertyName method.
    /// The default value is false, but subclasses used by unit tests might
    /// override this property's getter to return true.
    /// </summary>
    protected virtual bool ThrowOnInvalidPropertyName { get; } = true;

If you just bind the ObservableCollection to the Source of your DataGrid then it should work as intended. 如果只是将ObservableCollection绑定到DataGridSource ,那么它应该按预期工作。
When ever a new item is added or an item is being removed then your view will get notified to update his data. 当添加新项目或删除项目时,将通知您的视图以更新其数据。

To track actual changes you do not need to implement a CollectionChanged event of your list, but you'll have to make the actual objects inside the list observable . 要跟踪你不需要实现您的列表的CollectionChanged事件的实际变化,但你必须做出的排行榜中的实际对象观测 To make an object observable, you have to implement the INotifyPropertyChanged interface. 要使对象可观察,您必须实现INotifyPropertyChanged接口。
Once the objects are observable, and a property sends out a PropertyChanged notification, the observable collection will catch this. 一旦对象可观察,并且属性发出PropertyChanged通知,observable集合将捕获此信息。

Here is some quick sample code to get you started: 以下是一些快速示例代码,可帮助您入门:

1. Make your own implementation for an observable object 1.为可观察对象创建自己的实现

public class ObservableObject : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged([CallerMemberName] string propName = null)
    {
        var handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propName));
        }
    }

    protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
    {
        if (Equals(storage, value)) return false;
        storage = value;
        OnPropertyChanged(propertyName);
        return true;
    }
}

2. Have your actual object class extend this implementation, and make sure the necessary properties send out a PropertyChanged notification 2.让您的实际对象类扩展此实现,并确保必要的属性发送PropertyChanged通知

public class Order : ObservableObject 
{
    private long _orderId;
    public long OrderId
    {
        get { return _orderId; }
        set { SetProperty(ref _orderId, value); }
    } 

    private string _description;
    public string Description
    {
        get { return _description; }
        set { SetProperty(ref _description, value); }
    } 
}

3. Make sure your ViewModel has an ObservableCollection 3.确保您的ViewModel具有ObservableCollection

(The ViewModel is supposed to be observable as well. Which I hope is already the case for you, else it would be pretty hard to have MVVM work.) (ViewModel也应该是可观察的。我希望你已经是这种情况了,否则让MVVM工作会非常困难。)

public class MyViewModel : ObservableObject 
{

    public MyViewModel()
    {
        //this is just an example of some test data:
        var myData = new List<Order> {
            new Order { OrderId = 1, Description = "Test1"},
            new Order { OrderId = 2, Description = "Test2"},
            new Order { OrderId = 3, Description = "Test3"}
        };
        //Now add the data to the collection:
        OrderList = new ObservableCollection<Order>(myData);

    }
    private ObservableCollection<Order> _orderList;
    public ObservableCollection<Order> OrderList
    {
        get { return _orderList; }
        set { SetProperty(ref _orderList, value); }
    } 
}

4. Bind the DataGrid's source to the ObservableCollection 4.将DataGrid的源绑定到ObservableCollection

<DataGrid Name="dgOrderList" 
        AutoGenerateColumns="False" 
        ItemsSource="{Binding OrderList, Mode=TwoWay}" 
        IsReadOnly="True"
        SelectionMode="Single"
        SelectionUnit="FullRow">
    <DataGrid.Columns>
        <DataGridTextColumn Width="Auto" Header="ID" Binding="{Binding OrderId}"/>
        <DataGridTextColumn Width="*" Header="Description" Binding="{Binding OrderDescription}"/>
    </DataGrid.Columns>
</DataGrid>

You should be adding to the bound collection directly. 您应该直接添加到绑定集合。 Adding to OrderListData doesn't affect the one you are binding to: 添加到OrderListData不会影响您绑定的那个:

OrderListDataCollection.Add(new OrderList(Order.Description, Order.OrderId));

Honestly, the other one seems worthless, at least in as much as it affects your bound control. 老实说,另一个看起来毫无价值,至少在它影响你的约束力的同时也是如此。 All it did was initialize the ObservableCollection . 它所做的就是初始化ObservableCollection It does not serve as an on-going source for data. 它不是数据的持续来源。

You need first to implement INotifyPropertyChanged in your OrderList class 首先需要在OrderList类中实现INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;

    // This method is called by the Set accessor of each property.
    // The CallerMemberName attribute that is applied to the optional propertyName
    // parameter causes the property name of the caller to be substituted as an argument.
    private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

then you can just use the collection as it is. 然后你可以按原样使用该集合。 The CollectionChangedEvent is used if you want to make your custom event, but by default the ObservableCollection already notifies the UI on the change of the number of their items. 如果要创建自定义事件,则使用CollectionChangedEvent,但默认情况下,ObservableCollection已通知UI更改其项目数。 You just need to notify the UI about the change on the single item 您只需要通知UI有关单个项目的更改

EDIT: Use the collection as a property in a viewmodel that implements also INotifyPropertyChanged 编辑:将集合用作视图模型中的属性,该视图模型也实现INotifyPropertyChanged

private ObservableCollection<MyItem> _myCollection = new ObservableCollection<MyItem>();

public ObservableCollection<MyItem> MyCollection
{
    get {return _myCollection;}
    set  
        {
           _myCollection = value;
           OnPropertyChanged("MyCollection");
        }
}

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

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