How to start a thread with Dispatcher - wpf

I am writing a code for Folder Browser Application on WPF, I wrote a code to get the folders using expend button, and wrote a function to calculate the size of the folders, now it looks like this Example of what am I have right now
This is the main part of the code:
This is my item_Expanded method:
void item_Expanded(object sender, RoutedEventArgs e)
{
var item = (TreeViewItem)sender;
if (this.HasDummy(item))
{
this.Cursor = Cursors.Wait;
this.RemoveDummy(item);
this.ExploreDirectories(item);
this.ExploreFiles(item);
this.Cursor = Cursors.Arrow;
}
}
it calls ExploreDirectories method:
private void ExploreDirectories(TreeViewItem item)
{
var directoryInfo = (DirectoryInfo)item.Tag;
foreach (var directory in directoryInfo.GetDirectories())
{
var isHidden = (directory.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden;
var isSystem = (directory.Attributes & FileAttributes.System) == FileAttributes.System;
if (!isHidden && !isSystem)
{
item.Items.Add(this.GetItem(directory));
}
}
}
it calls GetItem method:
private TreeViewItem GetItem(DirectoryInfo directory)
{
var item = new TreeViewItem
{
Header = $"Name: {directory.Name} \t Size: {ConvertSize(GetDirectorySize(directory))}",
DataContext = directory,
Tag = directory
};
this.AddDummy(item);
item.Expanded += new RoutedEventHandler(item_Expanded);
return item;
}
and finaly it calls GetDirectorySize method:
static long GetDirectorySize(DirectoryInfo root, bool recursive = true)
{
FileInfo[] files = null;
DirectoryInfo[] subDirs = null;
var startDirectorySize = default(long);
{
files = root.GetFiles("*.*");
}
if (files != null)
{
foreach (var fileInfo in files)
Interlocked.Add(ref startDirectorySize, fileInfo.Length);
}
if (recursive)
{
subDirs = root.GetDirectories();
Parallel.ForEach(subDirs, (subDirectory) =>
Interlocked.Add(ref startDirectorySize, GetDirectorySize(subDirectory,
recursive)));
}
return startDirectorySize;
}
How should I rewrite my code to use a Dispatcher, so it stops freezing while calculating the size or expanding the folders and files? Should I rewrite my GetDirectorySize method, and change the Parallel.ForEach(), and Interlock.Add("recursive call here")?

Related

Use BitmapDecoder in an other thread then where is created. (The calling thread cannot access this object because a different thread owns it.)

So, i have a function that will load an image from disk async in an other thread ( big images will be loaded and I don't want the UI THread to be locked while loading).
Loading is done like this
public override void LoadFile()
{
using (var imageStream = new FileStream(_filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
Decoder = new TiffBitmapDecoder(imageStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
InitializeFile();
}
}
Then I want to use the Decoder on the main thread
public List<ThumbnailModel> LoadPages()
{
var result = new List<ThumbnailModel>();
foreach (var frame in Decoder.Frames) <--// this line throws exception
{
result.Add(new ThumbnailModel
{
Name = _metadataLoader.GetPageName((BitmapMetadata)frame.Metadata),
Bitmap = new WriteableBitmap(frame)
});
}
return result;
}
Now here is the problem, whenever I reach the line where I try to access the Decoder.Frames it throws exception (The calling thread cannot access this object because a different thread owns it.)
Is there a way I can use my Decoder in the main thread if not, the only possible solution is to load all the image information in the other thread?
Full code version :
// this is the task, that calls the imageFactory LoadFile method - NewThread
private async Task OpenFileAsync(string strFilePath)
{
var newFile = _imageFileFactory.LoadFile(strFilePath);
if (newFile != null)
{
_imagefile = newFile;
}
}
//image factory load file - NewThread
public IImageFile LoadFile(string filePath)
{
if (string.IsNullOrWhiteSpace(filePath))
{
return null;
}
var fileExtension = Path.GetExtension(filePath); // .tiff or .jpeg
var file = new ImageFileTiff(filePath, _metatadaFactory, _metadataVersioner);
file.LoadFile();
return file;
}
// ImageFileTiff LoadFile will create a decoder - NewThread
public override void LoadFile()
{
using (var imageStream = new FileStream(_filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
Decoder = new JpegBitmapDecoder(imageStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
InitializeFile();
}
}
After we have an IImageFile we call on MainThread(UIThread)
var pages = _imagefile.LoadPages();
Where LoadPages is the place where the app breaks. also called on UIThread
public List LoadPages()
{
var result = new List();
foreach (var frame in Decoder.Frames)
{
result.Add(new ThumbnailModel
{
Name = _metadataLoader.GetPageName((BitmapMetadata)frame.Metadata),
Bitmap = new WriteableBitmap(frame)
});
}
return result;
}
I thought you could simply return the decoder from the thread to be able to access it but your decoder is a TiffBitmapDecoder which inherits from DispatcherObject (https://learn.microsoft.com/en-gb/dotnet/api/system.windows.threading.dispatcherobject?view=netcore-3.1).
So you won't be able to access it from a different thread than the one where it was created msdn:"Only the thread that the Dispatcher was created on may access the DispatcherObject directly"
What you could do instead is use the decoder in it's thread and return the final result:
I couldn't build on your sample since there was to much missing for me to test it but I built a similar project to give an exemple:
public partial class MainWindow : Window
{
public MainWindow()
{
}
public TiffBitmapDecoder LoadFile()
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.InitialDirectory = "c:\\";
openFileDialog.Filter = "tiff files (*.tif)|*.tif|All files (*.*)|*.*";
openFileDialog.FilterIndex = 2;
openFileDialog.RestoreDirectory = true;
if (openFileDialog.ShowDialog() == true && !string.IsNullOrEmpty(openFileDialog.FileName))
{
//I didn't bother to check the file extension since it's just an exemple
using (var imageStream = openFileDialog.OpenFile())
{
return new TiffBitmapDecoder(imageStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
}
}
else
{
//User cancelled
return null;
}
}
public List<ThumbnailModel> LoadPages(TiffBitmapDecoder decoder)
{
//TiffBitmapDecoder" inherits from DispatcherObject/>
//https://learn.microsoft.com/en-gb/dotnet/api/system.windows.threading.dispatcherobject?view=netcore-3.1
var result = new List<ThumbnailModel>();
if (decoder != null)
{
try
{
foreach (var frame in decoder.Frames)
{
result.Add(new ThumbnailModel
{
//set the variables
});
}
}
catch(InvalidOperationException e)
{
MessageBox.Show(e.Message, "Error");
}
}
else
{
//Nothing to do
}
return result;
}
private async Task AsyncLoading()
{
this.thumbnailModels = await Task.Run<List<ThumbnailModel>>(() =>
{
var decoder = this.LoadFile();
return this.LoadPages(decoder);
});
}
private List<ThumbnailModel> thumbnailModels = null;
private async void AsyncLoadingButton_Click(object sender, RoutedEventArgs e)
{
await this.AsyncLoading();
}
}
public class ThumbnailModel
{
}
Content of MainWindow.xaml just in case:
<Grid>
<StackPanel Orientation="Vertical">
<Button x:Name="NoReturnButton" Margin="10" HorizontalAlignment="Center" Content="Call AsyncLoadingNoReturn" Click="AsyncLoadingButton_Click" />
</StackPanel>
</Grid>

How to parallelize 3d model creation?

Bit of a special case here, because I'm using a library called helix-toolkit but bear with me.
The thing is I would like to parallelize the creation of model objects in my code using a backgroundworker.
I know that there is a big issue with mutlithreading and working on UI elements but maybe somehow there is a workaround.
Here is the rough structure of my code:
First file in which I create the Backgroundwoker, splitting the workload for creating Geometry3D objects and finally calling SetModelGeometry to bind the geometries to the viewport. The second file shows how the binding is done.
MainWindow.xaml.cs
private void Draw_Building()
{
_worker = new BackgroundWorker { WorkerReportsProgress = true, WorkerSupportsCancellation = true };
_worker.DoWork += Draw_Building_DoWork;
_worker.ProgressChanged += DrawBuilding_ProgressChanged;
_worker.RunWorkerCompleted += DrawBuilding_RunWorkerCompleted;
_worker.RunWorkerAsync(10000);
}
private void Draw_Building_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
// returns a list containing Geometry3D objects ( created by meshBuilder.ToMeshGeometry3D() )
Geometryhandler.Draw_Building(sender, e);
}
private void DrawBuilding_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
StatusProgressBar.Value = e.ProgressPercentage;
var i = (int)e.UserState;
var actualComponent = MyBuildingComponents.First(c => c.Id == i);
LblStatusbarInfo.Text = "Currently meshing element #" + actualComponent.Globalid + " (" +
actualComponent.Objectname + ")";
StatusProgressbarMsg.Text = "Meshing (" + e.ProgressPercentage + " %)";
}
private void DrawBuilding_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
StatusProgressbarMsg.Text = "-";
StatusProgressBar.Value = 0;
LblStatusbarInfo.Text = "Meshing completed.";
Geometry = e.Result as List<MeshIdandGeometry>;
// creates MeshGeometryModel3D objects and binds them to the viewport using the List of Geometries
MainViewModel.SetModelGeometry(Geometry);
}
MainViewModel.cs
public void SetModelGeometry(List<MeshIdandGeometry> geometry)
{
MyModelGeometry = new Element3DCollection();
if (geometry != null)
{
foreach (var mygeometry in geometry)
{
var s = new MeshGeometryModel3D
{
Geometry = mygeometry.Geometry,
Material = mygeometry.Material,
};
this.MyModelGeometry.Add(s);
s.Attach(MyModelViewport.RenderHost);
}
}
this.OnPropertyChanged("MyModelGeometry");
}
My problem at the moment is the following error message:
The calling thread cannot access this object because a different
thread owns it.
which is thrown in the SetModelGeometry function when trying to attach the ModelGeometry to the viewport.
I guess the compiler is complaining about the fact that the geometries were created in different threads, to which he has no access now.
Is there any workaround/solution without destroying the parallel execution of the DrawBuilding function?
EDIT:
EDIT 2: posted the wrong version of the Draw_Building method
The Draw_Building method in the Geometryhandler:
public void Draw_Building(object sender, DoWorkEventArgs e)
{
var geometry = new List<MeshIdandGeometry>();
var standardMaterial = new PhongMaterial()
{
AmbientColor = SharpDX.Color.LightGray,
//DiffuseColor = new Color4(0.35f, 0.35f, 0.35f, 1.0f),
//DiffuseMap = new BitmapImage(new System.Uri(#"Con_Diffuse_2.jpg", System.UriKind.RelativeOrAbsolute)),
//NormalMap = new BitmapImage(new System.Uri(#"Con_Normal_2.jpg", System.UriKind.RelativeOrAbsolute)),
};
var max = _mainWindow.MyBuildingComponents.Count;
var i = 1;
// Loop over building components
foreach (var component in _mainWindow.MyBuildingComponents)
{
//if (i == 5) break;
var component1 = component;
var componentTriangles = _mainWindow.MyTriangles.Where(triangle => triangle.ComponentId == component1.Id);
var meshBuilder = new MeshBuilder(true, true, true);
// Loop over triangles in building element
foreach (var triangle in componentTriangles)
{
var triangle1 = triangle;
var p1 = _mainWindow.MyVertices.Find(
vt => vt.Id == triangle1.PointId1);
var p2 = _mainWindow.MyVertices.Find(
vt => vt.Id == triangle1.PointId2);
var p3 = _mainWindow.MyVertices.Find(
vt => vt.Id == triangle1.PointId3);
if (p1 != null && p2 != null && p3 != null)
{
//meshBuilder.AddTriangle(new Vector3((float)p1.X, (float)p1.Y, (float)p1.Z),
// new Vector3((float)p2.X, (float)p2.Y, (float)p2.Z),
// new Vector3((float)p3.X, (float)p3.Y, (float)p3.Z));
// coordination are switched to match the coordinate system in SharpDX viewport
meshBuilder.AddTriangle(new Vector3(-(float)p1.Y, (float)p1.Z, -(float)p1.X),
new Vector3(-(float)p2.Y, (float)p2.Z, -(float)p2.X),
new Vector3(-(float)p3.Y, (float)p3.Z, -(float)p3.X));
}
}
var mesh = meshBuilder.ToMeshGeometry3D();
var meshandtriangle = new MeshIdandGeometry
{
Id = component1.Id,
Geometry = mesh,
Material = standardMaterial,
};
geometry.Add(meshandtriangle);
i++;
var progressPercentage = Convert.ToInt32(((double)i / max) * 100);
var backgroundWorker = sender as BackgroundWorker;
backgroundWorker?.ReportProgress(progressPercentage, component1.Id);
}
e.Result = geometry;
}
Big thanks to #egse for finding a solution.
Part of the code that causes the problem:
var standardMaterial = new PhongMaterial()
{
AmbientColor = SharpDX.Color.LightGray,
//DiffuseColor = new Color4(0.35f, 0.35f, 0.35f, 1.0f),
//DiffuseMap = new BitmapImage(new System.Uri(#"Con_Diffuse_2.jpg", System.UriKind.RelativeOrAbsolute)),
//NormalMap = new BitmapImage(new System.Uri(#"Con_Normal_2.jpg", System.UriKind.RelativeOrAbsolute)),
};
Basically the problem with the above code is that the material is created as a local variable inside the scope of the backgroundworker. This causes problems with ownership when the UI thread tries to enter the material objects.
The solution to this problem is to make sure that the material is created by the UI thread (e.g in this case, the constructor of the Geometryhandler)
TL;DR: Do not create instances of classes which inherit from DependencyObject in another thread than the UI.

Click event for button in winform is firing repeatedly for number of values in List

I have a windows form (parent) that takes a value from textbox and then opens child form which then uses that value to select an image from a directory. When multiple images are found for the particular value, I have the form modified to display a couple of buttons to navigate (Next & Previous) to display the different images. Upon first opening the parent form, entering a value and then using form.show() to display the child form – everything works as expected. But if another value is entered into parent form (child form can still be open or exited (hidden)) and the ‘Next’ button is clicked the code in the click event is running over again for however many number of images are in the List(imagesFound). Say I have 3 images in the List(imagesFound) and I step through the code in debug mode the btnNext click event fires 3 times in a row. This of course runs GetMultiImages method which causes sequence of displaying the images to be all of. And again, this doesn’t happen the first time a value is entered into parent form. I’ve made sure the list and other variables are cleared out in GetImage method. I’m stumped…any ideas?
Form1:
public partial class Form1 : Form
{
private string parcelID;
Form2 viewer = new Form2();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
parcelID = txtParID.Text;
ShowViewer();
}
private void ShowViewer()
{
viewer.GetImage(parcelID);
if (viewer.NoImageFound == true)
{
viewer.Show();
viewer.Focus();
}
else if (viewer.NoImageFound == false)
{
viewer.Hide();
}
}
}
Child Form:
public partial class Form2 : Form
{
public Button btnNext = new Button();
public Button btnPrev = new Button();
private List<string> imagesFound = new List<string>();
private string Path;
private string parcel;
private int increment;
private int maxNum;
public bool NoImageFound;
//multi image members
private string firstMultiItem;
private string selectMultiImage;
Image parMultiImage;
public Form2()
{
InitializeComponent();
}
public void GetImage(string ParcelID)
{
NoImageFound = true;
parcel = ParcelID;
increment = 0;
maxNum = 0;
firstMultiItem = null;
selectMultiImage = null;
parMultiImage = null;
imagesFound.Clear();
Path = "........\\Images\\";
try
{
if (!string.IsNullOrEmpty(parcel))
{
string parcelTrim = parcel.Substring(0, 6);
Path = Path + parcelTrim + "\\";
foreach (string s in Directory.GetFiles(Path, parcel + "_" + "*"))
{
string trimString = s.Replace(Path, "");
imagesFound.Add(trimString);
}
if ((imagesFound.Count == 0))
{
MessageBox.Show("No images found for ParcelID: " + parcel);
picBox.Image = null;
this.Text = "";
NoImageFound = false;
}
else
{
if (imagesFound.Count == 1)
{
string firstItem = imagesFound[0].ToString();
string selectImage = Path + firstItem;
Image parImage = Image.FromFile(selectImage);
//in order to access the picture box control you have to change it's
//access modifier (Modifier) from private to public. Defaults to private
picBox.Image = parImage;
picBox.SizeMode = PictureBoxSizeMode.StretchImage;
this.Text = parcel;
SingleForm();
}
else if (imagesFound.Count > 1)
{
firstMultiItem = imagesFound[0].ToString();
maxNum = imagesFound.Count;
selectMultiImage = Path + firstMultiItem;
parMultiImage = Image.FromFile(selectMultiImage);
picBox.Image = parMultiImage;
picBox.SizeMode = PictureBoxSizeMode.StretchImage;
this.Text = parcel;
MultiImageForm();
}
}
}
else
{
MessageBox.Show("No ParcelID");
}
}
catch (DirectoryNotFoundException)
{
string text = parcel;
MessageBox.Show("ParcelID: " + text + " could not be found. The directory may be missing.", "There's a problem locating the image.",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void SingleForm()
{
this.Height = 400;
btnNext.Visible = false;
btnPrev.Visible = false;
}
private void MultiImageForm()
{
//set form properties
this.Text = parcel;
this.Height = 432;
//set btnNext properties
btnNext.Location = new Point(307, 375);
btnNext.Size = new Size(75, 25);
btnNext.Font = new Font("Maiandra GD", 10, FontStyle.Bold);
btnNext.Text = ">>";
//add btnNext to form
this.Controls.Add(btnNext);
btnNext.Visible = true;
btnNext.Enabled = true;
//creating event handler for btnNext
btnNext.Click += new EventHandler(btnNext_Click);
//set btnPrev properties
btnPrev.Location = new Point(12, 375);
btnPrev.Size = new Size(75, 25);
btnPrev.Font = new Font("Maiandra GD", 10, FontStyle.Bold);
btnPrev.Text = "<<";
//add btnPrev to form
this.Controls.Add(btnPrev);
btnPrev.Visible = true;
btnPrev.Enabled = false;
//creating event handler for btnPrev
btnPrev.Click += new EventHandler(btnPrev_Click);
}
private void GetMultiImages()
{
try
{
firstMultiItem = imagesFound[increment].ToString();
selectMultiImage = Path + firstMultiItem;
parMultiImage = Image.FromFile(selectMultiImage);
picBox.Image = parMultiImage;
picBox.SizeMode = PictureBoxSizeMode.StretchImage;
}
catch (IndexOutOfRangeException)
{
MessageBox.Show("Index was out of range.");
}
}
private void btnNext_Click(object sender, System.EventArgs e)
{
if (increment != maxNum - 1)
{
increment++;
GetMultiImages();
}
EnableButtons();
}
private void btnPrev_Click(object sender, System.EventArgs e)
{
if (increment > 0)
{
increment--;
GetMultiImages();
}
EnableButtons();
}
private void EnableButtons()
{
if (increment == 0)
{
btnPrev.Enabled = false;
btnNext.Enabled = true;
}
else if (increment > 0 & increment != maxNum - 1)
{
btnPrev.Enabled = true;
btnNext.Enabled = true;
}
else if (increment == maxNum - 1)
{
btnPrev.Enabled = true;
btnNext.Enabled = false;
}
}
private void Form2_FormClosing(object sender, FormClosingEventArgs e)
{
//overriding closing event
this.Hide();
e.Cancel = true;
}
}
//creating event handler for btnNext
btnNext.Click += new EventHandler(btnNext_Click);
That's a bug. You keep adding a Click event handler for the button, each time you call MultiImageForm(). So the event handler runs multiple times for a single click.
Only add event handlers in the form constructor so you can be sure it is only done once.

Why is my code not being reached after an await statment?

For some reason I cannot figure out, the code beyond my await statement in the following code is never reached. Why?
public class FileSystemUpdateSource : IUpdateSource
{
private ObservableCollection<UpdatePackage> _packages = new ObservableCollection<UpdatePackage>();
private readonly DirectoryInfo _importDirectory = null;
public FileSystemUpdateSource(DirectoryInfo a_importDirectory)
{
#region Argument Validation
if (a_importDirectory == null)
throw new ArgumentNullException("a_importDirectory");
#endregion
_importDirectory = a_importDirectory;
Refresh();
}
public ObservableCollection<UpdatePackage> Packages
{
get { return _packages; }
}
public async void RefreshAsync()
{
var task = new Task<IEnumerable<UpdatePackage>>(CreatePackages);
var packages = await task;
// *** Code not reached ***
Packages.Clear();
Packages.AddRange(packages);
}
public void Refresh()
{
var packages = CreatePackages();
Packages.Clear();
Packages.AddRange(packages);
}
private IEnumerable<UpdatePackage> CreatePackages()
{
var packageFiles = from packageFile in _importDirectory.EnumerateFiles().AsParallel()
where FileIsZip(packageFile)
select new UpdatePackage(packageFile);
return packageFiles;
}
private bool FileIsZip(FileInfo a_file)
{
using (var fin = a_file.OpenRead())
{
byte[] firstBytes = new byte[5];
fin.Read(firstBytes, 0, 5);
if (firstBytes[0] == 80 && firstBytes[1] == 75 && firstBytes[2] == 3 && firstBytes[3] == 4 && firstBytes[4] == 20)
return true;
else
return false;
}
}
}
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var importRoot = ApplicationSettings.Current.GetValue("ImportRoot") as System.IO.DirectoryInfo;
IUpdateSource updateSource = new FileSystemUpdateSource(importRoot);
updateSource.RefreshAsync();
// *** Code IS reached here. ***
}
Edits
The answer thanks to add task.Start() to my code right before the await like so (thanks to Reed Copsey's answer):
/// <summary>
/// Refresh the list of available update packages (asynchronously).
/// </summary>
public async Task RefreshAsync()
{
var task = new Task<IEnumerable<UpdatePackage>>(CreatePackages);
task.Start();
var packages = await task;
Packages.Clear();
Packages.AddRange(packages);
}
You're creating your Task incorrectly.
When you call new Task, it doesn't start the task. You should use Task.Run instead:
var task = Task.Run(CreatePackages);
This can be simplified to:
public async void RefreshAsync()
{
var packages = await Task.Run(CreatePackages);

How do I update the UI from a HttpWebRequest?

In my Mainpage.xaml.cs file I have a function that creates an instance of another class and tries to download a webpage using a HttpWebRequest from that instance. The problem is, once I've managed to download the webpage I can't send it back to the main UI thread. I've tried using Deployment.Current.Dispatcher.BeginInvoke to send the webpage back to a TextBlock I have waiting, but when I try I get an error telling me that I can't access the TextBlock from the other class. Is there any way to pass data between two threads without using LocalStorage?
EDIT: code below:
MainPage:
private void button1_Click(object sender, RoutedEventArgs e)
{
Member m = new Member(name, id);
}
Member class:
public Member(String Member, String API)
{
APIKey = API;
MemberName = Member;
this.super = super;
DoSend(method, string, "", null);
}
public void DoSend(string method, string url, string body, string mimetype)
{
if (WebRequest.RegisterPrefix("https://",System.Net.Browser.WebRequestCreator.ClientHttp)) {
HttpWebRequest request = WebRequest.Create(makeURI(url)) as HttpWebRequest;
request.Method = method;
request.Headers["X-NFSN-Authentication"] = MakeAuthHeader(url,body);
if (body != "")
{
byte[] bodyData = Encoding.UTF8.GetBytes(body);
request.ContentType = mimetype;
//Stuff Should Happen Here
}
else
doStuff(request);
}
public void doStuff(HttpWebRequest httpReq)
{
httpReq.BeginGetResponse(r =>
{
var httpRequest = (HttpWebRequest)r.AsyncState;
var httpResponse = (HttpWebResponse)httpRequest.EndGetResponse(r);
using (var reader = new StreamReader(httpResponse.GetResponseStream()))
{
var response = reader.ReadToEnd();
ResponseBlock.Text = response; //Invalid cross-thread reference
}
}, httpReq);
}
MainPage:
customClass.DownloadPage((result) =>
{
textBlock.Text = result;
},
(exception) =>
{
MessageBox.Show(exception.Message);
});
CustomClass:
public void DownloadPage(Action<string> callback, Action<Exception> exception)
{
WebClient webClient = new WebClient();
webClient.DonwloadStringCompleted += (s, e) =>
{
if (e.Error == null)
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
callback(e.Result);
});
}
else
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
exception(e.Error);
});
}
};
webClient.DonwloadStringAsync();
}

Resources