0

I'm trying to dynamically add records to DataGrid (which I take from mails, running in the background). The problem is that when I use the method on the other thread to start the XAML window, it just prints some records not every one and not dynamically

What I tried:

Shorten the code to just this

AlertTable.ItemsSource = Alert.alerts;

but it throws this exception

System.InvalidOperationException: 'The ItemsControl object is not consistent with the source of its items.

I also tried adding a new train to contructor, I got this exception

Task.Run(() => AddItems());

System.InvalidOperationException: 'The calling thread cannot access this object because it is owned by another thread.'

AdminWindow.xaml.cs class

public AdminWindow() {
   InitializeComponent();
   foreach (Alert alert in Alert.alerts) {
      alertToDataGrid.Add(alert);
   }
   AlertTable.ItemsSource = Alert.alerts;
}

Contructor for Login(and first window, if users exist, it will create new one - AdminWindow and this will be hide)

 public MainWindow()
        {
            InitializeComponent();
            EmailParser parser = new EmailParser(true, "test@mail.cz", "password");
    //Could be problem with Name of the class? I had to rename it, but how I see, it didn't change it properly. But the window is showing...
        }

Method from MailParser class that is running at background for receiving mails

public void MailKitLib(EmailParser emailParser) {

            bool help = true;
            Timer t = new Timer();
            t.Interval = 500;
            do
            {

                using (var client = new ImapClient())
            {
                using (var cancel = new System.Threading.CancellationTokenSource())
                {
                    client.Connect(emailParser.ServerName, emailParser.Port, emailParser.isSSLuse,
                        cancel.Token);

                    //client.AuthenticationMechanisms.Remove("XOAUTH");

                    client.Authenticate(emailParser.Username, emailParser.Password, cancel.Token);


                    var inbox = client.Inbox;
                    inbox.Open(FolderAccess.ReadOnly, cancel.Token);

                    Console.WriteLine("Total messages: {0}", inbox.Count);
                    Console.WriteLine("Recent messages: {0}", inbox.Unread);



                    for (int i = 0; i < inbox.Count; i++)
                    {
                        var message = inbox.GetMessage(i, cancel.Token);
                        Console.WriteLine("ID:" + message.MessageId);
                       
                        if (message.MessageId != null)
                        {
                            Alert alert = new Alert(message.MessageId, message.Date.DateTime, message.From.ToString(), "PING" , "LOW");                             
                            Alert.alerts.Add(alert);

                        }


                    }
                   

                    client.Disconnect(true, cancel.Token);
                }
            }
                if (t.Interval == 0)
                {
                    help = false;
                }
            } while (help != false);

        }

EmailParser contructor

public EmailParser(bool isSSLuse, string username, string password) {
   this.ServerName = "imap.outlook.com";
   this.Port = 993;
   this.IsSSLuse = isSSLuse;
   this.Username = username;
   this.Password = password;
   Task.Run(() => MailKitLib(this));
}
Tvaruzek
  • 31
  • 1
  • 6
  • 1
    This looks confusing. However, you need to add items to the source collection, i.e. the collection that is assigned to the ItemsSource property. In order to automatically update the UI, this should be an ObservableCollection (or any other collection that implements the INotifyCollectionChanged interface). You should get rid of the HashSet, and use only an OnservableCollection as source. – Clemens Jul 26 '22 at 10:49
  • I have edited already – Tvaruzek Jul 26 '22 at 10:51
  • When you update the source collection from a thread other than the UI thread, you either need to call `BindingOperations.EnableCollectionSynchronization` first, or use the UI thread's `Dispatcher`. – Clemens Jul 26 '22 at 10:51
  • @Clemens I am so sorry, but I still don't understand... – Tvaruzek Jul 26 '22 at 13:33
  • What exactly I am doing wrong? I thought, that HashSet and easy for avoiding duplicates in array. – Tvaruzek Jul 26 '22 at 13:36
  • Sure, but it does not notify about changes. It is not the right thing to use here. Maybe take a look at [this recent answer](https://stackoverflow.com/a/73066611/1136211) for an example how to update a source collection from a background thread. Then add some logic that avoids duplicate entries yourself, it is simple. – Clemens Jul 26 '22 at 13:36
  • So I should create my own class? And what is readonly object? What is his purpose in this case? – Tvaruzek Jul 26 '22 at 13:42
  • Not sure what you mean. You would simply use an ObservableCollection instead of a HashSet. Before adding an element, check if the collection already contains an "equal" element. Either replace or remove/add the "equal" element. – Clemens Jul 26 '22 at 13:45
  • No, I know how to find duplicate. I was talking about your exampl question – Tvaruzek Jul 26 '22 at 13:48
  • The readonly object there is needed for the collection synchronization. Please take a look at the documentation of EnableCollectionSynchronization. – Clemens Jul 26 '22 at 13:51
  • `foreach (Alert alert in Alert.alerts) { alertToDataGrid.Add(alert); } AlertTable.ItemsSource = Alert.alerts; ` Is this also wrong? – Tvaruzek Jul 26 '22 at 13:53
  • Sure, that's useless, because it still uses the HashSet, and `alertToDataGrid` is not used at all. It really makes no sense. – Clemens Jul 26 '22 at 13:55
  • Can you at least show me a little about my problem, how to proceed,please? So I can get a grip. Especially with the part where I update the source – Tvaruzek Jul 26 '22 at 13:58
  • I still mainly don't know how to update programmatically `ObservableCollectio` – Tvaruzek Jul 26 '22 at 14:24
  • By adding, removing or replacing elements. Take a look at the many examples here on StackOverflow. – Clemens Jul 26 '22 at 14:26
  • I need to change DataGrid, when Application is already running. Is it just removing and adding elements? – Tvaruzek Jul 26 '22 at 14:30
  • Yes, of course. – Clemens Jul 26 '22 at 14:31

0 Answers0