I want to store my own Emojis before using them in a (Rich)TextBlock.
Now it does not allow me to create a Windows.Controls.Image outside of the Main thread...
private static void AddEmojiToString(List<Inline> block, BitmapImage source)
{
var textRun = new Run("");
System.Windows.Controls.Image emoji = new System.Windows.Controls.Image();
emoji.Height = 15;
emoji.Width = 15;
emoji.VerticalAlignment = VerticalAlignment.Center;
emoji.Source = source;
block.Add(new InlineUIContainer(emo));
}
Is there any way to run this code outside of the main thread? I just want to prepare and store the text to be able to display it later.
Everything works fine if called from the Main thread.
Thank you for your help!
Related
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.
I have an issue that I think has not been covered in the multitude of other WPF image loading issues. I am scanning in several images and passing them to a "Preview Page". The preview page takes the image thumbnails and displays what a printout would look like via a generated bitmap.
The weird thing to me is, it will work fine if I run the program the first time. Upon reaching the end of the process and hitting "start over", the preview will return blank. I am creating the BitmapImage in a method that saves the bitmap as a random file name so I do not believe theres a lock on the file the second time around. Also, if I go to look at the temporary file created through explorer, it is drawn correctly so I know the appropriate data is getting to it.
Finally, when I navigate away from this page, I am clearing necessary data. I'm really perplexed and any help would be appreciated.
//Constructor
public Receipt_Form() {
InitializeComponent();
printData = new List<Object>();
this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
void MainWindow_Loaded(object sender, RoutedEventArgs e) {
// populates global variable fileName
var task = System.Threading.Tasks.Task.Factory.StartNew(() => outputToBitmap()); task.ContinueWith(t => setImage(fileName),
System.Threading.Tasks.TaskScheduler.FromCurrentSynchronizationContext());
// I started the image creation in a separate thread because I
// thought it may be blocking the UI thread, but it didn't matter
}
private void setImage(string imageURI) {
BitmapImage image;
using (FileStream stream = File.OpenRead(imageURI)) {
image = new BitmapImage();
image.BeginInit();
image.StreamSource = stream;
image.CacheOption = BitmapCacheOption.OnLoad;
image.EndInit();
}
receiptPreview.Source = image;
//this works the first iteration but not the second, though the temp file is created successfully
}
Found the issue - the Modern UI container was getting cleared when transitioning off the page.
I've got a chart that I've created and formatted (no data yet) in the public form, right after the initcomponent().
the series is populated upon event triggered (camera firing a imageavailable event), at that point I gather some pixels and fill the series with data.
This should work fine but I get a "cross thread error", saying that my chart was created in a different thread.
here's what my code looks like:
public Form1()
{
InitializeComponent();
CreateStarProfileChart();
.../...
here's my function:
private void CreateStarProfileChart()
{
// Y axis init
StarProfile.ChartAreas.Add("Area1");
StarProfile.ChartAreas["Area1"].AxisY.Title = "Pixel Values";
StarProfile.ChartAreas["Area1"].AxisY.Minimum = 0;
StarProfile.ChartAreas["Area1"].AxisY.Maximum = 4096;
StarProfile.ChartAreas["Area1"].AxisY.Interval = 500;
StarProfile.ChartAreas["Area1"].AxisY.MajorGrid.Enabled = true;
StarProfile.ChartAreas["Area1"].AxisY.MajorGrid.LineColor = Color.Gray;
StarProfile.ChartAreas["Area1"].AxisY.MajorGrid.LineDashStyle = System.Windows.Forms.DataVisualization.Charting.ChartDashStyle.Dash;
StarProfile.ChartAreas["Area1"].AxisY.MinorTickMark.Enabled = true;
// X axis init
StarProfile.ChartAreas["Area1"].AxisX.Title = "Pixels Accross Centroid";
StarProfile.ChartAreas["Area1"].AxisX.MajorGrid.Enabled = true;
StarProfile.ChartAreas["Area1"].AxisX.MajorGrid.LineColor = Color.Gray;
StarProfile.ChartAreas["Area1"].AxisX.MajorGrid.LineDashStyle = System.Windows.Forms.DataVisualization.Charting.ChartDashStyle.Dash;
StarProfile.ChartAreas["Area1"].AxisX.MinorTickMark.Enabled = true;
// Series init
StarProfile.Series.Add("StarProfile");
StarProfile.Series["StarProfile"].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
StarProfile.Series["StarProfile"].BorderWidth = 2;
StarProfile.Series["StarProfile"].Color = Color.Black;
}
and finally, here's the code in the event that's triggered every few seconds:
// Clear Chart and Re-init Chart
StarProfile.Series["StarProfile"].Points.Clear();
StarProfile.ChartAreas["Area1"].AxisX.Minimum = -BoxDim;
StarProfile.ChartAreas["Area1"].AxisX.Maximum = BoxDim;
StarProfile.ChartAreas["Area1"].AxisX.Interval = 5;
for (int sl = -BoxDim; sl <= BoxDim; sl++)
{
// Add points to chart
StarProfile.Series["StarProfile"].Points.AddXY(sl, PixelMap[CoordX + sl, CoordY]);
../.. // some more maths following but that's all that's related to the chart itself...
any idea how I could do what I want to do?
thanks
Steve
Put your update code into a method like this (it probably already is, but you haven't posted it...):
void RefreshChart()
{
// Clear Chart and Re-init Chart
StarProfile.Series["StarProfile"].Points.Clear();
StarProfile.ChartAreas["Area1"].AxisX.Minimum = -BoxDim;
...
}
Then, from your code (assumedly running in a background thread) which is calling RefreshChart(), call it like this, using an anonymous delegate:
Invoke((Action)(() => RefreshChart()));
This should eliminate your error, by marshalling the function call back to the main UI thread.
(Note that in this particular case, you can simplify the call further using method group syntax:)
Invoke((Action)RefreshChart);
I currently have a working program which displays a preview from my webcam and uses the ISampleGrabberCB interface.
Using the SampleCB my program converts the image to a bitmap and then processes the image for a barcode which is then decoded. This works perfectly when I show the result using a MessageBox however when I wish to edit a textbox on my main form with this result I get a few errors when I start my program.
I am trying to update my text box using the following code within the ISampleGrabberCB interface:
public int SampleCB(double sampletime, IMediaSample sample)
{
if (sample == null)
{
return -1;
}
try
{
int length = sample.GetActualDataLength();
IntPtr buffer;
BitmapData bitmapData = new BitmapData();
Form1 f1 = new Form1("", "", "");
if (sample.GetPointer(out buffer) == 0 && length > 0)
{
Bitmap bitmapOfFrame = new Bitmap(width, height, pitch, PixelFormat.Format24bppRgb, buffer);
}
The method changeTextBox1 is in my main form and is as follows:
public void changeTextBox1(string text)
{
textBox1.Text = text;
}
The errors I get are firstly A device attached to the system in not functioning properly and then no such supported interface. This seems to only happen when I use the Form1 f1 = new Form1("","",""); line.
So as I said if i remove the line Form1 f1 = new Form1("","",""); and replace changeTextBox1(result.Text); with MessageBox.Show(result.Text.ToString()); this works.
How would I go about updating the textbox instead of using a MessageBox?
You should make UI changes in the main UI thread, however your callback SampleCB is getting called from another system thread, hence the errors. Use message posting or other ways to safely pass data from callback's thread to main UI thread and update UI with new data in the main UI thread.
I have been dealing with a problem with a backgroundWorker these last couple of days. I have been looking through forums and documentation on MSDN but still haven't found the answer so now I want to ask you clever people.
Long story short, I have a custom user control consisting of a WrapPanel inside a ScrollViewer. The WrapPanel contains some elements that are notified when they are scrolled into view.
The elements are then supposed to load and display an image, and this is where the problem comes in. In order to not lock the gui thread i load the images in a BackgroundWorker, but the GUI stalls anyways. This is the code for the class that represents the elements contained in the WrapPanel:
class PictureThumbnail : INotifyingWrapPanelElement
{
private string path;
private Grid grid = null;
private BackgroundWorker thumbnailBackgroundCreator = new BackgroundWorker();
private delegate void GUIDelegate();
private Image thumbnailImage = null;
public PictureThumbnail(String path)
{
this.path = path;
visible = false;
thumbnailBackgroundCreator.DoWork += new DoWorkEventHandler(thumbnailBackgroundCreator_DoWork);
}
void thumbnailBackgroundCreator_DoWork(object sender, DoWorkEventArgs e)
{
BitmapImage bi = LoadThumbnail();
bi.Freeze(); //If i dont freeze bi then i wont be able to access
GUIDelegate UpdateProgressBar = delegate
{
//If this line is commented out the GUI does not stall. So it is not the actual loading of the BitmapImage that makes the GUI stall.
thumbnailImage.Source = bi;
};
grid.Dispatcher.BeginInvoke(UpdateProgressBar);
}
public void OnVisibilityGained(Dispatcher dispatcher)
{
visible = true;
thumbnailImage = new Image();
thumbnailImage.Width = 75;
thumbnailImage.Height = 75;
//I tried setting the thumbnailImage.Source to some static BitmapImage here, and that does not make the GUI stall. So it is only when it is done through the GUIDelegate for some reason.
grid.Children.Add(thumbnailImage);
thumbnailBackgroundCreator.RunWorkerAsync();
}
private BitmapImage LoadThumbnail()
{
BitmapImage bitmapImage = new BitmapImage();
// BitmapImage.UriSource must be in a BeginInit/EndInit block
bitmapImage.BeginInit();
bitmapImage.UriSource = new Uri(path);
bitmapImage.DecodePixelWidth = 75;
bitmapImage.DecodePixelHeight = 75;
bitmapImage.EndInit();
return bitmapImage;
}
}
I have added some comments in the code explaining some stuff I tried and what leads I have. But I'll write it again here. If I just load the BitmapImage in the backgroundWorker, but don't apply it as the Source of the thumbnailImage the GUI doesn't stall (but no image is displayed obviously). Also if I set the Source of the thumbnailImage to some preloaded static BitmapImage in the OnVisibilityGained method (So in the GUI thread), then the GUI wont stall, so it is not the actual setting of the Image.Source that's the culprit.
You should make use of the reporting feature of the backgroundworker which lets you directly access the controls of your form without invoking.