Przedmioty dla mojego DataGrid jest Ilist. Jedynym sposobem aktualizacji Ilist jest metodami w klasie, której jest właściwość.

class SomeObject
{
    public ReadOnlyCollection<SomeType> Items { get; }

    public void AddItem(SomeType someType);

    public event Action<SomeType> ItemAdded;
}

Sama lista jest kolekcją tylko do odczytu i nie można go zaktualizować bezpośrednio - Iow, zdecydowanie nie jest to obserwableCollection. Czy istnieje sposób, w jaki mogę wiązać się z datagridem do celów wyświetlania, ale obsługiwać tworzenie, aktualizację i usunąć do elementów, używając niektórych wiązań / datagridowych haków?

1
Dan 17 luty 2017, 01:46

2 odpowiedzi

Najlepsza odpowiedź

Utwórz pełnomocnik do swojej kolekcji, która implementuje zarówno IBindinglist, jak i ICANCELAddNew. Dzięki tym dwoma możesz "przechwycić" połączenia, aby dodać nowe przedmioty i usunąć istniejące. Metoda Addnew () jest wywoływana po dodaniu nowej pozycji, a usuwanie / zaniechanie jest wywoływane, gdy element zostanie usunięty. Zamiast bezpośrednio modyfikować kolekcję, w tym czasie można wywołać metody API przeznaczone do tego celu. Poniżej znajduje się minimalna implementacja. Zauważ kilka rzeczy:

  1. Nowe elementy nie są natychmiast dodane do kolekcji - przechowywane są tymczasowo w polu _Newitem

  2. Jeśli ucieczka zostanie trafiona podczas edycji nowego przedmiotu, najpierw anallynew, a następnie nazywa się Endnew

  3. Zakłada się, że "przedmioty" jest obserwowalną kolekcją, wydarzenia, z których wywołuje odpowiednie zdarzenia liście

  4. Ta technika nie zapewnia środków do przechwytywania zmian wprowadzonych do właściwości istniejących przedmiotów

...

class SomeTypeBindingList : IBindingList, ICancelAddNew
{
    public SomeTypeBindingList(SomeObject someObject)
    {
        _someObject = someObject;

        var observableCollection = _someObject.Items as ObservableCollection<SomeType>;
        if (observableCollection != null)
            observableCollection.CollectionChanged += ObservableCollectionOnCollectionChanged;
    }

    public IEnumerator GetEnumerator()
    {
        return new SomeTypeEnumerator(this);
    }

    public int Count => _someObject.Items.Count + (_newItem == null ? 0 : 1);

    public object SyncRoot { get; } = new object();

    public bool IsSynchronized { get; } = false;

    public bool Contains(object value)
    {
        return IndexOf(value) != -1;
    }

    public int IndexOf(object value)
    {
        if (ReferenceEquals(value, _newItem))
            return _someObject.Items.Count;

        return _someObject.Items.IndexOf((SomeType)value);
    }

    public void Remove(object value)
    {
        var someType = (SomeType)value;
        _someObject.RemoveItem(someType);
    }

    public void RemoveAt(int index)
    {
        var someType = _someObject.Items[index];
        _someObject.RemoveItem(someType);
    }

    public object this[int index]
    {
        get
        {
            if (index >= _someObject.Items.Count)
            {
                if(_newItem == null)
                    throw new IndexOutOfRangeException();

                return _newItem;
            }

            return _someObject.Items[index];
        }
        set
        {
            throw new NotImplementedException();
        }
    }

    public object AddNew()
    {
        _newItem = new SomeType();

        ListChanged?.Invoke(this, new ListChangedEventArgs(ListChangedType.ItemAdded, _someObject.Items.Count));
        return _newItem;
    }

    public void CancelNew(int itemIndex)
    {
        _newItem = null;
        ListChanged?.Invoke(this, new ListChangedEventArgs(ListChangedType.ItemDeleted, itemIndex));
    }

    public void EndNew(int itemIndex)
    {
        if (_newItem != null)
        {
            var someType = _newItem;
            _newItem = null;
            _someObject.AddItem(someType);
        }
    }

    private void ObservableCollectionOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.Action == NotifyCollectionChangedAction.Remove)
            Enumerable.Range(e.OldStartingIndex, e.OldItems.Count).ForEach(i => ListChanged?.Invoke(this, new ListChangedEventArgs(ListChangedType.ItemDeleted, i)));
        else if(e.Action == NotifyCollectionChangedAction.Add)
            Enumerable.Range(e.NewStartingIndex, e.NewItems.Count).ForEach(i => ListChanged?.Invoke(this, new ListChangedEventArgs(ListChangedType.ItemAdded, i)));
    }

    private readonly SomeObject _someObject;
    private SomeType _newItem;

    class SomeTypeEnumerator : IEnumerator
    {
        public SomeTypeEnumerator(SomeObject someObject)
        {
            _someObject = someObject;
            Reset();
        }

        public void Dispose()
        {

        }

        public bool MoveNext()
        {
            _index++;
            return _someObject.Items.Count < _index;
        }

        public void Reset()
        {
            _index = -1;
        }

        public object Current => _someObject.Items[_index];

        object IEnumerator.Current
        {
            get { return Current; }
        }

        private readonly SomeObject _someObject;
        private int _index;
    }
}
1
Dan 20 luty 2017, 19:06

Możesz wiązać właściwość ItemsSource {X1}} do dowolnego obiektu publicznego , który zwraca IEnumerable, inclucing IList<T> właściwość. Nie możesz powiązać się z dziedzinami, więc powinieneś zrobić nieruchomość Items, jeśli zamierzasz się z tym wiązać:

public IList<SomeType> Items { get; private set; }

Ale abyś mógł dynamicznie dodać przedmioty do kolekcji źródła w czasie wykonywania i mieć nowe elementy automatycznie pojawiają się w DataGrid, kolekcja źródła musi zaimplementować interfejs {x0}}. Tylko klasa ObservableCollection<T> robi to w ramach .NET. Nie ma List<T> nie.

A IList<T> nie jest kolekcją tylko do odczytu, ponieważ ma metodę dodawania: https://msdn.microsoft.com/en-us/library/system.collections.ilist.add%28V=vs.110%29.aspx. Więc myślę, że równie dobrze możesz użyć ObservableCollection<T>.

Edytuj:

Jeśli naprawdę chcesz odświeżyć DataGrid "ręcznie", możesz subskrybować zdarzenie ItemAdded swojego obiektu w kodzie-ty z widokiem i użyj metody {x2}} {{{} X3}}:

someObject.ItemAdded += (se, ee) => 
{
    var be = BindingOperations.GetBindingExpression(theDataGrid, DataGrid.ItemsSourceProperty);
    if (be != null)
        be.UpdateTarget();

};

Albo możesz po prostu zresetować nieruchomość ItemsSource:

someObject.ItemAdded += (se, ee) => 
{
    theDataGrid.ItemsSource = someObject.Items;

};

Edycja 2:

Moim problemem jest to, że potrzebuję niezawodnego sposobu przechwytywania mechanizmu wiązania wychodzącego siatki, dzięki czemu mogę połączyć dodatek (), gdy nowy wiersz jest dodawany na przykład. Eksperymentowałem z Ibindinglist, aby zobaczyć, czy mógłbym użyć tego, ale co jeszcze nie powstało.

Jeśli wiązasz właściwość ItemsSource DataGrid do ObservableCollection<SomeType>, możesz obsługiwać wydarzenie CollectionChanged tej kolekcji:

observableCollection.CollectionChanged += (ss, ee) =>
{
    if(ee.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
    {
        SomeType newItem = ee.NewItems[0] as SomeType;
        someObject.AddItem(newItem);
    }
};

To zdarzenie zostanie podniesione, gdy DataGrid doda nowy element do kolekcji źródła.

1
mm8 17 luty 2017, 17:52