This question already has answers here:
Accessing UI (Main) Thread safely in WPF
(3 answers)
Closed 8 years ago.
why this code wont work without Dispatcher.RunAsync and what does it do? without Dispatcher its throwing error at copying value to textv.Text " thats its on different thread"
async void Current_GeofenceStateChanged(GeofenceMonitor sender, object args)
{
var reports = sender.ReadReports();
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
foreach (var report in reports)
{
GeofenceState st = report.NewState;
Geofence gf2 = report.Geofence;
if (st == GeofenceState.Entered)
{
textv2.Text = "Hello"; //XAML TEXT
}
else if(st==GeofenceState.Exited)
{
textv2.Text = "Bye";
}
}
});
}
The Event Current_GeofenceStateChanged is being fired outside of the GUI thread and only the GUI thread can change GUI elements. Dispatcher.RunAsync says the code inside should run on the GUI thread so it works.
if you put the result on a string variable it will work if you only put:
Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => textv2.Text = StringVariable;);
EDIT: I only noticed that you have XAML code later you can just put the string on a property and bind the property to the text value of the text box letting you free from the Dispatcher.
<TextBox Text="{Binding StringVariable}"/>
and on the code just have
public string StringVariable { get; set; }
than on the method just set the value to the property
StringVariable = "bla bla";
Related
This question already has answers here:
How do I update the GUI from another thread?
(47 answers)
Closed 1 year ago.
Good morning everyone, I'm having trouble with rewriting a DataGrid, the function works, but I would need to do a faster search, so the thought was to add parallelism.
But upon applying the latter it generates an error for me: System.InvalidOperationException: 'Invalid cross-thread operation: the control 'dataGridView1' was accessed from a different thread than the one from which the creation was performed.'
The problem is clear to me, however I can't figure out how to solve it. Could you guys please help me out?
I've already tried applying Invoke but the program goes into an infinite loop.
private void inputSearch_TextChanged(object sender, EventArgs e)
{
Parallel.For(0, 7, i =>
{
Ricerca(i);
});
}
private void Ricerca(int i)
{
string searchValue = inputSearch.Text.ToUpper();
var re = from row in dataTable.AsEnumerable()
where
row[i].ToString().Contains(searchValue)
select row;
if (re.Count() != 0)
{
Invoke(new Action(() =>
{
dataGridView1.DataSource = re.CopyToDataTable();
dataGridView1.Columns[7].Visible = false;
}));
}
}
You have a deadlock because you are blocking the UI thread in inputSearch_TextChanged method (that is invoked by UI thread).
If your intent is to parallelize the Linq expression, split Ricerca method in two. The final part of the method should be invoked out of the parallel for, maybe directly on inputSearch_TextChanged.
A better solution could be to use .NET Task instead.
Otherwise, if you don't need parallel at all, you can replace it with a simple for.
I was able to solve the problem as suggested by Cherry by saving the content in another table and recopying it later.
private void inputSearch_TextChanged(object sender, EventArgs e)
{
Parallel.For(0, 7, i =>
{
Ricerca(i);
});
dataGridView1.DataSource = table;
dataGridView1.Columns[7].Visible = false;
}
private void Ricerca(int i)
{
string searchValue = inputSearch.Text.ToUpper();
var re = from row in dataTable.AsEnumerable()
where
row[i].ToString().Contains(searchValue)
select row;
if (re.Count() != 0)
{
table = re.CopyToDataTable();
}
}
Thanks to all!
I have recently made a Class Library (dll) for my other project to program a Bluetooth device via serial port (COM). The library is used to transfer firmware via COM port. It works fine until the requirement comes, which requires a WPF window to show the progress of programming. I have successfully created the progress bar using standard WPF app template. However, the standard WPF does not allow me to generate dll. After searching here, I found this link that teaches you how to add a WPF window to existing Class Library project. Also, someone teaches you how to show the window from here. Everything look good until I tried, there is nothing shows up when I call the method ProgrammBluetooth() from LabVIEW.
My main method, which is in a separate .cs file:
namespace BTMProg
{
public class BTMProgrammer
{
private bool _uut1Status = false;
private string _uut1Message = "";
public bool UUT1Status
{
get { return _uut1Status; }
set { _uut1Status = value; }
}
public string UUT1Message
{
get { return _uut1Message; }
set { _uut1Message = value; }
}
public void ProgramBluetooth (string ioPort, string firmwareFile)
{
List<UUT> uutList = new List<UUT>();
uutList.Add(new UUT(ioPort, "UUT1", 1));
Thread thread = new Thread(() =>
{
var wn = new MainWindow(uutList, firmwareFile);
wn.ShowDialog();
wn.Closed += (s, e) => wn.Dispatcher.InvokeShutdown();
Dispatcher.Run();
if (wn.TaskList[0].Result.ToUpper().Contains("SUCCESS"))
{
_uut1Status = true;
_uut1Message = wn.TaskList[0].Result;
}
else
{
_uut1Status = false;
_uut1Message = wn.TaskList[0].Result;
}
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
}
}
My WPF code in MainWindow.xaml.cs:
ProgrammingViewModel _pvm = new ProgrammingViewModel();
private List<string> _viewModeList = new List<string>();
private List<Task<string>> _taskList = new List<Task<string>>();
public List<Task<string>> TaskList {
get => _taskList;
set => _taskList = value;
}
public MainWindow(List<UUT> uutList, string firmwareFile)
{
InitializeComponent();
foreach (var uut in uutList)
{
_viewModeList.Add(uut.UutName);
}
_pvm.AddProcessViewModels(_viewModeList);
ProgressBarView.DataContext = _pvm.ProcessModels;
StartProgramming(uutList, firmwareFile);
Application.Current.MainWindow.Close();
}
The issue before was that if I don't use dispatcher to create a new thread, an exception saying "The calling thread must be STA, because many UI components require this...." thrown. After I use the new thread, no error but the window does not show up as expected. What could be the problem? Thanks.
The ShowDialog function will stop execution of the thread until the window closes, meaning the rest of that code may not run and the dispatcher may not be started. You should try the Show method instead, which returns as soon as the window is shown.
Also, what is going on with these lines in the constructor of the window?
StartProgramming(uutList, firmwareFile);
Application.Current.MainWindow.Close();
Whatever that first line does, it needs to return and not do a bunch of work if you want the window to finish getting constructed. The second line makes no sense at all. Why are you closing the main window of the application? Did you even set and open a window associated with that property at some point?
I suspect one or more of these things is preventing the thread from ever reaching the point where it can show the window.
This question already has an answer here:
Lifeupdate DataGrid from TextFile with good performance
(1 answer)
Closed 5 years ago.
I have a background thread to listen data, so this infinite loop to output data.
And, I using a ObservableCollection to bind a ListBox and show log.
I try to use Dispather.BeginInvoke, but no use, it not realtime. I have no idea for update UI in MVVM.
xaml
<ListBox x:Name="lsb_log" Width="auto" ItemsSource="{Binding DisplayLogs}"/>
code
private ObservableCollection<string> _displayLogs;
public ObservableCollection<string> DisplayLogs
{
get
{
return _displayLogs;
}
}
public Testpro()
{
_displayLogs = new ObservableCollection<string>();
Task.Run(new Action(outputData));
}
private void outputData()
{
string str = "";
while (true)
{
string newString = GetInfoString();
if (string.IsNullOrWhiteSpace(newString) || str == newString)
continue;
str = newString;
Debug.WriteLine(str);
Application.Current.Dispatcher.BeginInvoke((Action)delegate ()
{
DisplayLogs.Add(str);
});
}
}
*Edit:
GetInfoString will call by a dll file.
You should really use something designed for this kind of update. I'm going to suggest Microsoft's Reactive Framework (NuGet "System.Reactive" & "System.Reacive.Windows.Threading").
Then you can do this after the _displayLogs = new ObservableCollection<string>(); line:
IDisposable subscription =
Observable
.Interval(TimeSpan.FromSeconds(1.0))
.Select(_ => GetInfoString())
.Where(newString => !string.IsNullOrWhiteSpace(newString))
.DistinctUntilChanged()
.ObserveOnDispatcher()
.Subscribe(newString =>
{
Debug.WriteLine(newString);
DisplayLogs.Add(newString);
});
That should call GetInfoString() every second and update your UI. It handles all of the threading and marshalling issues.
If you keep a reference to subscription you can stop it at any stage by calling subscription.Dispose().
You can use async-await therefore. Your OutputData-Method can look something like:
private async void OutputData(IProgress<string> onProgress)
{
await Task.Factory.StartNew(() =>
{
string str = string.Empty;
while (true)
{
string newString = GetInfoString();
if(string.IsNullOrWhiteSpace(newString) || str == newString)
continue;
str = newString;
// call the UI to update your bound collection
onProgress.Report(str);
// give the ui some time to respond before continue with your endless-loop
Thread.Sleep(200);
}
});
}
And you can call this method with:
OutputData(new Progress<string>((str) =>
{
DisplayLogs.Add(str);
}));
That's it
This question already has answers here:
How to deal with cross-thread access exceptions?
(3 answers)
Closed 6 years ago.
getting right to it here is a few lines from the code I have in my code behind:
private static Factual factual = new Factual(FACTUAL_KEY, FACTUAL_SECRET);
private void OnStartQueries_Click(object sender, RoutedEventArgs e)
{
RunAsyncQuery(43.0120, -81.2003, 3000, 25).ContinueWith(task => rtbJsonData.AppendText(task.Result));
}
private static async Task<string> RunAsyncQuery(double lat, double lng, int radius, int limit)
{
return await Task.Run(() => factual.Fetch("places", new Query().WithIn(new Circle(lat, lng, radius)).Limit(limit)));
}
So as you can see when I click the Start Queries button I want to run a method asynchronously. When it returns with the result from that query, I want to set the text of the RichTextBox "rtbJsonData" to the result. However, currently when I run it I get the exception:
"The calling thread cannot access this object because a different thread owns it."
How can I do this?
The RichTextBox "rtbJsonData" is being owned by the main thread and cannot be accessed from another thread directly.
In WPF, your model can you can derive from the INotifyPropertyChanged interface. Any changes on the model will be updated on the view.
See MSDN for a full example: https://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged(v=vs.110).aspx
This is based on the MVVM (Model View View-Model) pattern, https://msdn.microsoft.com/en-us/library/hh848246.aspx
This question already has answers here:
How do I update the GUI from another thread?
(47 answers)
Closed 9 years ago.
I have a problem. I'm trying to make seekbar for BASS in C#.NET (WPF) but when I'm starting a new Thread it can't get access to modify value of my Slider or Label (with current song position value). Here is a Code:
//class:
Thread seekbar;
public Window1()
{
InitializeComponent();
Bass.BASS_Init(-1, 44100, BASSInit.BASS_DEVICE_DEFAULT, IntPtr.Zero);
seekbar = new Thread(new ThreadStart(this.ThreadTask));
seekbar.IsBackground = true;
seekbar.Start();
}
private void ThreadTask()
{
int value = (int)Bass.BASS_ChannelGetPosition(music);
while (true)
{
MusicSeekBar.Value = value; //MusicSeekBar is mine Slider
CurrentValue.Content = value; //CurrentValue is a Label
}
}
I always get an error that thread can't get access to this two objects. Thanks for help in advice.
You need to call the MusicSeekBar & CurrentValue in a Dispatcher beceause the new thread doesn't have access to them.
Dispatcher.BeginInvoke(new Action(() =>
{
MusicSeekBar.Value = value; //MusicSeekBar is mine Slider
CurrentValue.Content = value; //CurrentValue is a Label
}));