Table<> jako ItemsSource dla ListView- widok się nie odświeża

Table<> jako ItemsSource dla ListView- widok się nie odświeża
CN
  • Rejestracja:prawie 13 lat
  • Ostatnio:ponad 9 lat
  • Postów:38
0

Ustawiam obiekt Table<> rzutowany na IEnumerable<> jako ItemsSource dla ListView. Podczas startu aplikacji poprawnie wczytywana i wyświetlana jest lista z bazy danych. Jednak jeśli dokonam jakichkolwiek zmian (dodawanie, usuwanie) na liście w trakcie działania aplikacji, elementy wyświetlane w ListView nie zmieniają się, pomimo tego, że wszystkie zmiany w Table<> zachodzą poprawnie.

Kopiuj
public class ProfilesSourceSQLite : IProfilesSource
{
    public event EventHandler<ProfileActionEventArgs> ActionPerformed;

    private Table<ProfileManager.Profile> Profiles;
    public IEnumerable<ProfileManager.Profile> ProfilesCollection { get { return Profiles; } } 

    public ProfilesSourceSQLite()
    {
        ...
        dbContext = new DataContext(dbConnection);
        Profiles = dbContext.GetTable<ProfileManager.Profile>();
    }

    public bool AddProfile(ProfileManager.Profile profile)
    {
        try
        {
            Profiles.InsertOnSubmit(profile);
            dbContext.SubmitChanges();
        }
        ...

        if(ActionPerformed != null)
            ActionPerformed(this, new ProfileActionEventArgs(ProfileAction.Add, profile));
    }
}

public class ProfileManager
{
    private static ProfileManager instance;
    public static ProfileManager Instance
    {
        get
        {
            if (instance == null)
                instance = new ProfileManager();

            return instance;
        }
    }

    private IProfilesSource profilesSource;
    public IEnumerable<Profile> ProfilesCollection { get { return profilesSource.ProfilesCollection; } }

    public event EventHandler ProfilesListChanged;

    public ProfileManager()
    {
        ...
        profilesSource = new ProfilesSourceSQLite();
        profilesSource.ActionPerformed += OnActionPerformed;
    }

    private void OnActionPerformed(object sender, ProfileActionEventArgs e)
    {
        if(ProfilesListChanged != null)
            ProfilesListChanged(this, new EventArgs());
    }

    public bool AddProfile(Profile profile)
    {
        return profilesSource.AddProfile(profile);
    }
}

public class ProfileManagerWindow : Window
{
    public ProfileManagerWindow()
    {
        InitializeComponent();

        ProfileManager.Instance.ProfilesListChanged += RefreshList;
        listView.ItemsSource = ProfileManager.Instance.ProfilesCollection;
    }

    private void RefreshList(object sender, EventArgs e)
    {
        listView.Items.Refresh();

        // kiedy na liście znajduje się 1 profil, a ja dodaję kolejny:
        int x = ProfileManager.Instance.ProfilesCollection.Count; // x = 2, czyli wartość taka, jaka powinna być
        int y = listView.Items.Count;                                     // y = 1, błędna wartość, zmiana nie została uwzględniona

        // Próbowałem też tak:
        listView.ItemsSource = null;
        listView.ItemsSource = ProfileManager.Instance.ProfilesCollection;
        // ale to również nie działa
    }
}

Co robię źle?

dam1an
  • Rejestracja:prawie 12 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Warszawa
  • Postów:1589
0

Powinieneś bindować listview z ObservableCollection. Link

CN
Chcę uniknąć trzymania w pamięci 2 takich samych list, więc dlatego chciałem, żeby ListView bezpośrednio wyświetlał Table&lt;&gt;. Serio nie jest to możliwe w żaden sposób?
somekind
Na temat wątku piszemy w postach.
CN
  • Rejestracja:prawie 13 lat
  • Ostatnio:ponad 9 lat
  • Postów:38
0

Rozwiązałem problem pisząc taką klasę, której używam zamiast standardowego Table<>. Jest ustawiona jako ItemsSource (w moim kodzie jest jeszcze rzutowana na IEnumerable<>, ale nie wiem czy to ma jakieś znaczenie).

Kopiuj
    public sealed class ObservableTable<T> : INotifyCollectionChanged, ITable<T> where T : class
    {
        private Table<T> _table;

        public Expression Expression => _table.AsQueryable().Expression;
        public Type ElementType => _table.AsQueryable().ElementType;
        public IQueryProvider Provider => _table.AsQueryable().Provider;

        public event NotifyCollectionChangedEventHandler CollectionChanged;

        public IEnumerator<T> GetEnumerator()
        {
            return _table.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public ObservableTable(ref DataContext dbContext)
        {
            _table = dbContext.GetTable<T>();

            if(CollectionChanged != null)
                CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }

        public void InsertOnSubmit(T entity)
        {
            _table.InsertOnSubmit(entity);

            if (CollectionChanged != null)
                CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, entity));
        }

        public void Attach(T entity)
        {
            _table.Attach(entity);
        }

        public void DeleteOnSubmit(T entity)
        {
            var index = IndexOf(entity);

            _table.DeleteOnSubmit(entity);

            if (CollectionChanged != null)
                CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, entity, index));
        }

        public int IndexOf(T entity)
        {
            return _table.ToList().IndexOf(entity);
        }
    }

Dodatkowo, jeśli chcemy aby lista odświeżała się również po zmianie wartości w już istniejącym obiekcie na liście, klasa którą przechowujemy musi implementować INotifyPropertyChanged.

edytowany 4x, ostatnio: cnyk
QU
  • Rejestracja:ponad 9 lat
  • Ostatnio:prawie 9 lat
  • Postów:65
0
cnyk napisał(a):

Rozwiązałem problem pisząc taką klasę, której używam zamiast standardowego Table<>. Jest ustawiona jako ItemsSource (w moim kodzie jest jeszcze rzutowana na IEnumerable<>, ale nie wiem czy to ma jakieś znaczenie).
Dodatkowo, jeśli chcemy aby lista odświeżała się również po zmianie wartości w już istniejącym obiekcie na liście, klasa którą przechowujemy musi implementować INotifyPropertyChanged.

A czy implementowałeś INotifyPropertyChanged? I czy to działa? Czy w ogóle istnieje możliwość ustawienia takiego ItemsSource, jeśli dane są pobierane z bazy?

Mam na myśli to, że baza CHYBA i tak będzie za każdym razie pobierała całość, kasowała obecne obiekty i wgrywała nowe do kolekcji, chyba że się mylę?

CN
  • Rejestracja:prawie 13 lat
  • Ostatnio:ponad 9 lat
  • Postów:38
0

Jeśli chodzi o INotifyPropertyChanged to oczywiście, że działa. Dlaczego miałoby nie działać? Akurat ja użyłem ReSharpera do automatycznej implementacji, ale jest wiele przykładów jak to zrobić: http://stackoverflow.com/questions/1315621/implementing-inotifypropertychanged-does-a-better-way-exist

Table<> implementuje IEnumerable<> czyli dokładnie typ, jakim jest ItemsSource więc znów, dlaczego miałoby nie działać?

U mnie wygląda to tak, że cała tabela jest pobierana tylko raz przez DataContext.GetTable<T>(). Dodawanie rekordu wygląda tak:

Kopiuj
var table = new ObservableTable<T>();
...
table.InsertOnSubmit(new T());
dbContext.SubmitChanges();

I na liście od razu pojawia się to co dodałem bez żadnego dodatkowego kodu.

edytowany 2x, ostatnio: cnyk
QU
  • Rejestracja:ponad 9 lat
  • Ostatnio:prawie 9 lat
  • Postów:65
0
cnyk napisał(a):

I na liście od razu pojawia się to co dodałem bez żadnego dodatkowego kodu.

Ciekawa jestem czy dałoby się to coś takiego zrobić w oparciu o TreeView. Aby dodany potomek automatycznie był dodawany bez odświeżania wszystkiego.

CN
  • Rejestracja:prawie 13 lat
  • Ostatnio:ponad 9 lat
  • Postów:38
0

@quechua: akurat tak się składa, że w swoim projekcie również używam TreeView i właśnie przed chwilą zajmowałem się tą częścią. Więc: tak, z TreeView też da się to zrobić, wszystko automatycznie się dodaje. Pisałem to w oparciu o ten artykuł: http://blog.clauskonrad.net/2011/04/how-to-make-hierarchical-treeview.html
A u mnie wygląda to dokładnie tak (wyświetlane jest drzewko folderów i plików):

Kopiuj
        public class Item : INotifyCollectionChanged, INotifyPropertyChanged
        {
            public event NotifyCollectionChangedEventHandler CollectionChanged;
            public event PropertyChangedEventHandler PropertyChanged;

            private string _name;
            private string _path;

            public Item()
            {
                Items = new ObservableCollection<Item>();
                Items.CollectionChanged += delegate(object sender, NotifyCollectionChangedEventArgs action)
                {
                    CollectionChanged?.Invoke(sender, action);
                };
            }

            public string Name
            {
                get { return _name; }
                set { _name = value; NotifyPropertyChanged("Name"); }
            }

            public string Path
            {
                get { return _path; }
                set { _path = value; NotifyPropertyChanged("Path"); }
            }

            public ObservableCollection<Item> Items { get; }

            protected void NotifyPropertyChanged(string property)
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
            }
        }

        public class File : Item
        { }

        public class Directory : Item
        { }

XAML:

Kopiuj
<Window xmlns:local="clr-namespace:NaszNamespace" ... >

        <Window.DataContext>
            <local:Item />
        </Window.DataContext>

        <TreeView x:Name="treeView">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding Items}" DataType="{x:Type local:Item}">
                    <TreeViewItem Header="{Binding Name}" />
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>

</Window>

Użycie:

Kopiuj
    public partial class MainWindow : Window
    {      
        private ObservableCollection<Item> _items;

        public MainWindow()
        {
            InitializeComponent();

            _items = new ObservableCollection<Item>();
            _items.Add(new Directory { Name = "folder", Path = "/" });
            _items[0].Items.Add(new File { Name = "plik.jpg", Path = "/folder/" });
            _items.Add(new File { Name = "plik.pdf", Path = "/" });

            treeView.ItemsSource = _items;
        }

        // wszelkie zmiany na _items są teraz od razu widoczne w TreeView bez żadnego dodatkowego kodu
edytowany 4x, ostatnio: cnyk
QU
  • Rejestracja:ponad 9 lat
  • Ostatnio:prawie 9 lat
  • Postów:65
0

Możesz jeszcze pokazać kod, który podczas pracy programu dodaje element (w Twoim przypadku jak rozumiem tworzący folder/plik)?

CN
  • Rejestracja:prawie 13 lat
  • Ostatnio:ponad 9 lat
  • Postów:38
0

To tylko

Kopiuj
_items.Add(new File { Name = "plik.ext", Path = "/" });

Żeby dodać do konkretnej gałęzi wystarczy ją znaleźć i zrobić tak samo

Kopiuj
_items[index].Items.Add(new File { Name = "plik.pdf", Path = "/" });
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)