Форум программистов, компьютерный форум, киберфорум
C#: WPF, UWP и Silverlight
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.89/35: Рейтинг темы: голосов - 35, средняя оценка - 4.89
0 / 0 / 0
Регистрация: 13.12.2011
Сообщений: 9
1
.NET 4.x

Не получается с вторичного потока обратиться напрямую к объекту из главного потока

19.04.2012, 18:15. Показов 6330. Ответов 18
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Добрый день. Проблема заключается в том что не могу в WPF с вторичного потока обратится на прямую к объекту из главного потока. WPF пока только начинаю учить и застрял на вот такой проблеме и не могу никак сдвинутся с места.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
        FlowDocument document;
        Paragraph doc_null;
        private void BackgroundWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
        {
            document = new FlowDocument();
            doc_null = new Paragraph();
 
            doc_null.Inlines.Add("123");
            document.Blocks.Add(doc_null);
          
            richTextBox2.Document = document; // вот в этом и проблема
        }
 
         private void button1_Click(object sender, RoutedEventArgs e)
        {
            backgroundWorker = new BackgroundWorker();
            backgroundWorker.DoWork += BackgroundWorker_DoWork;
            backgroundWorker.RunWorkerAsync();
        }
Ну какая ошибка вы и так понимаете, не возможен доступ к объекту другого потока.
Я пробовал использовать и Диспетчер с делегатом и пытался даже что то получить через Interlocked. Но видимо не хватает знаний и навыка что б решить эту проблему. Прошу помощи , а то я уже не первый день в тупике...
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
19.04.2012, 18:15
Ответы с готовыми решениями:

Изменение состояния контролов из вторичного потока
Здравствуйте, Столкнулся с такой проблемой: Имеется программа которая ищет локальные ссылки в...

Invoke из главного потока во второй
озникла такая задача. Есть компонент, созданный в отдельном потоке(плеер). При обращении к нему из...

Обращение к объекту из другого потока
Делаю сетевое приложение и ни как не получается обратиться к объекту(RichBox1) из другого потока....

Подписаться из главного потока на свойство дочернего
В рамках WPF упр. приложения создаются 4 потока (System.Threading.Thread), каждый из которых...

18
I ♥ C#
470 / 261 / 25
Регистрация: 07.05.2010
Сообщений: 567
19.04.2012, 20:02 2
.Invoke()
0
0 / 0 / 0
Регистрация: 13.12.2011
Сообщений: 9
20.04.2012, 00:16  [ТС] 3
Хм.. а можно по подробней?
Я пробовал таким макаром но нечего не выходило:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Dispatcher.Invoke(
DispatcherPriority.Normal,
(Action)delegate() { richTextBox2.Document = document; }
);
 
Dispatcher.Invoke(
DispatcherPriority.Normal,
(Action)(() => { richTextBox2.Document = document; })
);
 
Dispatcher.Invoke(
DispatcherPriority.Normal,
new Action( () => { richTextBox2.Document = document; } )
);
0
17 / 17 / 2
Регистрация: 23.02.2012
Сообщений: 132
20.04.2012, 10:19 4
На сколько я понял, там немного все сложнее.
Вот например так получается запросто:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public partial class MainWindow : Window
    {
        private BackgroundWorker backgroundWorker;
        private FlowDocument document;
 
        private void BackgroundWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
        {
            try
            {
                document.Dispatcher.BeginInvoke(new Action(() => { document.Blocks.Add(new Paragraph(new Run("Simple FlowDocument"))); }), null);
            }
            catch (Exception ex)
            {
            }
        }
 
        private void backgroundWorker_RunWorkerComplected(object sender, RunWorkerCompletedEventArgs e)
        {
        }
 
 
        public MainWindow()
        {
            InitializeComponent();
            document = new FlowDocument();
            richTextBox2.Document = document;
        }
 
        private void button1_Click(object sender, RoutedEventArgs e)
        {
            backgroundWorker = new BackgroundWorker();
            backgroundWorker.DoWork += BackgroundWorker_DoWork;
            backgroundWorker.RunWorkerAsync();
        }
    }
Как я понял, нельзя передавать объект, созданный в другом потоке, но если в другом потоке вызвать Invoke и там создать объект, то все норм.
1
I ♥ C#
470 / 261 / 25
Регистрация: 07.05.2010
Сообщений: 567
20.04.2012, 13:41 5
Invoke нужно вызывать из того потока, в котором вы хотите выполнить действие. Например, если вы хотите изменить содержимое кнопки из другого потока, то нужно вызвать Invoke из потока с кнопкой:
C#
1
2
3
4
MyButton.Dispatcher.Invoke(new Action(delegate()
{
  MyButton.Content = "It works!";
}));
1
17 / 17 / 2
Регистрация: 23.02.2012
Сообщений: 132
20.04.2012, 14:15 6
Смотри:
C#
1
button1.Dispatcher.Invoke(new Action(() => { button1.Content = "123"; }));
Вызвал в BackgroundWorker_DoWork и все норм работает. Когда я вызываю диспетчер, то код выполняется в том потоке, в котором кнопка. А вот ты попробуй присвоить richTextBox2.Document что нибудь. Не надо тут ссылаться на другие примеры. Речь идет не о том, как строчку передать, а о передаче более сложного объекта. Как я понимаю, когда ты передаешь строку, то она передается значением, а вот FlowDocument - это уже ссылка. И если ты создал его в другом потоке, то и ссылка исчезает после закрытия потока. И вот тогда основной поток и ругается, что объекта то нет.
0
I ♥ C#
470 / 261 / 25
Регистрация: 07.05.2010
Сообщений: 567
20.04.2012, 16:28 7
Исправленный пример автора:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
FlowDocument document;
Paragraph doc_null;
void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
    richTextBox2.Dispatcher.Invoke(new Action(delegate()
    {
        document = new FlowDocument();
        doc_null = new Paragraph();
        doc_null.Inlines.Add("123");
        document.Blocks.Add(doc_null);
        richTextBox2.Document = document;
    }));
}
 
private void Button_Click(object sender, RoutedEventArgs e)
{
    backgroundWorker = new BackgroundWorker();
    backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);            
    backgroundWorker.RunWorkerAsync();
}
0
17 / 17 / 2
Регистрация: 23.02.2012
Сообщений: 132
20.04.2012, 17:07 8
Delog, Извини конечно, но какой смысл тогда в другом потоке открывать документ в richTextBox, если ты все делаешь в основном? На сколько я понял, автор хотел в фоновом загрузить документ в richTextBox, а при выполнении этого кода прога все равно зависнет, пока не заполнит richTextBox. Да и написал ты то же самое, что и я предлагал.
0
0 / 0 / 0
Регистрация: 13.12.2011
Сообщений: 9
20.04.2012, 18:53  [ТС] 9
Wurgengel, твой пример без условно правильный. Если пользоватся твоим примером то он не будет не чем отличатся от последнего примера Delog'a, вся операция будет проводится в главном потоке. Во избежания этого я хочу сформировать параграф в отдельном потоке.
C#
1
2
doc_null = new Paragraph();
        doc_null.Inlines.Add("123");
И внести этот параграф в документ через диспетер. Но проблема в том что компилятор матюкается когда я пытаюсь через диспетчер связать параграф из одного потока с документом другого потока.
C#
1
document.Dispatcher.BeginInvoke(new Action(() => { document.Blocks.Add(doc_null); }), null);
Мне главное что б на заднем фоне занеслись данные в параграф. А вывод в richTextBox можно и в главном потоке, лиж бы компилятор мне позволил это.
0
Українець
424 / 318 / 16
Регистрация: 26.09.2009
Сообщений: 844
20.04.2012, 19:27 10
C#
1
Application.Current.Dispatcher.Invoke(Action);
0
0 / 0 / 0
Регистрация: 13.12.2011
Сообщений: 9
21.04.2012, 17:03  [ТС] 11
Хм.. а можно по подробней, как это использовать?
0
Українець
424 / 318 / 16
Регистрация: 26.09.2009
Сообщений: 844
21.04.2012, 17:25 12
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
        FlowDocument document;
        Paragraph doc_null;
        private void BackgroundWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
        {
            document = new FlowDocument();
            doc_null = new Paragraph();
 
            doc_null.Inlines.Add("123");
            document.Blocks.Add(doc_null);
            var action = new Action(()=>richTextBox2.Document = document);
            Application.Current.Dispatcher.Invoke(action); // вот в этом и проблема
        }
 
         private void button1_Click(object sender, RoutedEventArgs e)
        {
            backgroundWorker = new BackgroundWorker();
            backgroundWorker.DoWork += BackgroundWorker_DoWork;
            backgroundWorker.RunWorkerAsync();
        }
1
0 / 0 / 0
Регистрация: 13.12.2011
Сообщений: 9
21.04.2012, 23:44  [ТС] 13
Да но проблема таким способом не решается. Компилятор не может связать два объекта разных потоков.
0
Українець
424 / 318 / 16
Регистрация: 26.09.2009
Сообщений: 844
22.04.2012, 00:52 14
у вас ошибка вылетает?
C#
1
 Application.Current.Dispatcher.Invoke(action);
эта команда выполняет действие в мейн потоке( там где была создана гуишка). Все должно работать
1
I ♥ C#
470 / 261 / 25
Регистрация: 07.05.2010
Сообщений: 567
22.04.2012, 09:35 15
Автору уже видимо надоело отвечать на этот вопрос Да я и сам дал маху - на самом деле оказывается работа со ссылочными типами из разных потоков не то же самое, что работа с типами значений. Не знаю, как принято поступать в таких случаях, но я решил производить сериализацию и передвать строку в основной поток:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#region Основной поток
private void Button_Click(object sender, RoutedEventArgs e)
{
    BackgroundWorker bw = new BackgroundWorker();
    bw.DoWork += new DoWorkEventHandler(bw_DoWork);
    MyRichTextBox.Document = new FlowDocument();
    bw.RunWorkerAsync();
}
 
delegate void RichTextBoxUpdater(string s);
 
void AddParInBG(string s)
{
    StringReader stringReader = new StringReader(s);
    XmlTextReader xmlTextReader = new XmlTextReader(stringReader);
    MyRichTextBox.Document.Blocks.Add((Paragraph)XamlReader.Load(xmlTextReader));
}
#endregion
 
#region Фоновый поток
void bw_DoWork(object sender, DoWorkEventArgs e)
{
    for (int i = 1; i < 6; i++)
    {
        Paragraph p = new Paragraph();
        p.Inlines.Add(new Bold(new Run("Параграф №" + i.ToString())));
        p.Inlines.Add("\r\nРусские не сдаются!");                                
        MyRichTextBox.Dispatcher.BeginInvoke(new RichTextBoxUpdater(AddParInBG), XamlWriter.Save(p));
        System.Threading.Thread.Sleep(1000); //убрать               
    }
}
#endregion
1
0 / 0 / 0
Регистрация: 13.12.2011
Сообщений: 9
23.04.2012, 00:40  [ТС] 16
Спасибо , всё работает. Только придётся много времени потратить что б понять =) с Xml не знаком совершенно.
0
meatlow
08.08.2014, 05:47 17
Я, конечно, опоздал на обсуждение. Но недавно нарвался на эту новость на форуме, т.к. у самого возникла такая проблема. Я в wpf недавно, скажу больше, я вообще в потоках не давно, так же как и в c#. Но, когда я работал с формами мне пришлось использовать делегаты, что бы обратиться к данным другого потока, и делал я так:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public delegate void tbDelegate(TextBox obj, string value);
public void SetText(TextBox obj, string value)
        {
            if (obj.InvokeRequired)
            {
                tbDelegate DDD = new tbDelegate(SetText);
                obj.Invoke(DDD, new object[] { obj, value });
            }
            else
            {
                obj.Text = obj.Text + Environment.NewLine + value;
                obj.SelectionStart = obj.Text.Length;
                obj.ScrollToCaret();
                obj.Refresh();
            }
 
        }
Но в wpf этот способ не подошел. Даже когда используешь Dispatcher объект не имеет такой функции как InvokeRequired. Если туда в тупую поставить true - ничего не будет работать. Но, если сделать так, то все работает без проблем(я понимаю, что это костыль, но он куда проще):
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private delegate void labelDelegate(Label info, string value);
        private void setLabel(Label Info, string value)
        {
            if (countOfInvoke == 0)
            {
                labelDelegate DDD = new labelDelegate(setLabel);
                countOfInvoke++;
                Info.Dispatcher.Invoke(DDD, new object[] { Info, value });
            }
            else
            {
                Info.Content = value;
                countOfInvoke = 0;
            }
 
        }
74 / 62 / 26
Регистрация: 05.08.2014
Сообщений: 218
08.08.2014, 23:40 18
Цитата Сообщение от meatlow Посмотреть сообщение
Я, конечно, опоздал на обсуждение. Но недавно нарвался на эту новость на форуме, т.к. у самого возникла такая проблема. Я в wpf недавно, скажу больше, я вообще в потоках не давно, так же как и в c#. Но, когда я работал с формами мне пришлось использовать делегаты, что бы обратиться к данным другого потока
Action - это тоже делегат.
0
Эксперт .NET
4432 / 2092 / 404
Регистрация: 27.03.2010
Сообщений: 5,657
Записей в блоге: 1
10.08.2014, 10:10 19
Цитата Сообщение от meatlow Посмотреть сообщение
Даже когда используешь Dispatcher объект не имеет такой функции как InvokeRequired
Для этого в WPF есть Dispatcher.CheckAccess.
0
10.08.2014, 10:10
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
10.08.2014, 10:10
Помогаю со студенческими работами здесь

Task. Обработка объектов главного потока.
Имеется дерево (treeview) лекций, каждый элемент состоит из названия и ссылки на файл. Необходимо...

Обращение к объекту из другого потока. И немного WCF
Здравствуйте! Нужна ваша помощь, прежде небольшой ввод в задачу которую я решаю) Есть веб служба...

Безопасное обращение к объекту из потока его не создавшего
Не знаю, в правильной ли ветке пишу, ибо не нашел где тут обсуждают C# на Mono. Задача такая....

Не получается обратиться к объекту после десериализации
что такое?, не могу понять обьект нормально десериализовал а к его подобъектам обратиться не могу...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
19
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru