0

i got a problem with running a async task with a thread that is called upon a button click and i dont know how to solve it.

When running it will stop directly after clicking the Upload button I already have tried several things, but nothing worked. One time it worked, but only till "VideosInsertRequest_ProgressChanged" but i dont remeber what i did xD Always getting the error that i cant access the object...

The code:

        private void UploadVideo_Click(object sender, EventArgs e)
    {
        StatusLabel.Content = "Upload Video...";
        Thread thead = new Thread(() =>
        {
            VideoUpload().Wait();
        });
        thead.IsBackground = true;
        thead.Start();
    }

    private async Task VideoUpload()
    {
        UserCredential credential;
        using (var stream = new FileStream("client_id.json", FileMode.Open, FileAccess.Read))
        {
            credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
                GoogleClientSecrets.Load(stream).Secrets,
                // This OAuth 2.0 access scope allows an application to upload files to the
                // authenticated user's YouTube channel, but doesn't allow other types of access.
                new[] { YouTubeService.Scope.YoutubeUpload },
                "user",
                CancellationToken.None
            );
        }

        var youtubeService = new YouTubeService(new BaseClientService.Initializer()
        {
            HttpClientInitializer = credential,
            ApplicationName = Assembly.GetExecutingAssembly().GetName().Name
        });

        var video = new Video();
        video.Snippet = new VideoSnippet();
        video.Snippet.Title = VideoTitle.Text;
        video.Snippet.Description = VideoDesc.Text;
        string[] tags = Regex.Split(VideoTags.Text, ",");
        video.Snippet.Tags = tags;
        video.Snippet.CategoryId = "22"; // See https://developers.google.com/youtube/v3/docs/videoCategories/list
        video.Status = new VideoStatus();
        video.Status.PrivacyStatus = VideoPrivacy.Text; // or "private" or "public"
        var filePath = VideoPath.Text; // Replace with path to actual movie file.

        using (var fileStream = new FileStream(filePath, FileMode.Open))
        {
            var videosInsertRequest = youtubeService.Videos.Insert(video, "snippet,status", fileStream, "video/*");
            videosInsertRequest.ProgressChanged += VideosInsertRequest_ProgressChanged;
            videosInsertRequest.ResponseReceived += VideosInsertRequest_ResponseReceived;

            await videosInsertRequest.UploadAsync();
        }
    }

    void VideosInsertRequest_ProgressChanged(Google.Apis.Upload.IUploadProgress progress)
    {
        switch (progress.Status)
        {
            case UploadStatus.Uploading:
                StatusLabel.Content = String.Format("{0} bytes sent.", progress.BytesSent);
                break;

            case UploadStatus.Failed:
                StatusLabel.Content = String.Format("An error prevented the upload from completing.{0}", progress.Exception);
                break;
        }
    }

    void VideosInsertRequest_ResponseReceived(Video video)
    {
        StatusLabel.Content = string.Format("Video id '{0}' was successfully uploaded.", video.Id);
    }
jazb
  • 5,498
  • 6
  • 37
  • 44
Sashiro
  • 3
  • 4
  • Which line is throwing the exception? – Gabriel Luci Nov 17 '18 at 01:23
  • 1
    You can simplify the code by using an async event handler: https://stackoverflow.com/a/19415703 – Jon Nov 17 '18 at 03:08
  • There are many answer to this question, but the simplest (and probably adequate in this case) can be found [here](https://stackoverflow.com/questions/5483565/how-to-use-wpf-background-worker). – John Wu Nov 17 '18 at 09:14
  • What happens if you don’t use a thread (which you shouldn’t anyway here) and just call `VideoUpload();` directly without the `Wait()` or `await`? Your issue currently is that you force your code to run in a thread different from the UI thread. So after each await, when your code tries to switch back to the original thread to do your UI stuff you will land in your created thread. If you don’t create an additional thread and just directly call VideoUpload from the UI thread, your code should automatically switch back to the UI thread. – ckuri Nov 17 '18 at 10:10
  • yea i already tried it but it results in nothing except changing the label's text, the upload won't be initiated – Sashiro Nov 17 '18 at 11:50

2 Answers2

0

you need to use ICommand with RelayCommandAsync and bind Button command to that icommand. its better to use MVVM in wpf create a viewmodel class

public class MoneyPageViewModel: INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        private ICommand _readAdminVersions;

        public ICommand readAdminVersions
        {
            get
            {
                return _readAdminVersions ??
                       (_readAdminVersions = new 
                        RelayCommandAsync(executeReadAdminVersions, (c) => true));
            }
        }
        private async Task executeReadAdminVersions()
        {//your code goes here
        }
    }  

if you stuck at INotifyPropertyChanged part just see simple implementation on web , this one is auto generated by ReSharper jetbrains or maybe vs 2017 dont know :)
and here is relayCommandAsync class :

public class RelayCommandAsync : ICommand
    {
        private readonly Func<Task> _execute;
        private readonly Predicate<object> _canExecute;
        private bool isExecuting;

        public RelayCommandAsync(Func<Task> execute) : this(execute, null) { }

        public RelayCommandAsync(Func<Task> execute, Predicate<object> canExecute)
        {
            _execute = execute;
            _canExecute = canExecute;
        }

        public bool CanExecute(object parameter)
        {
            if (!isExecuting && _canExecute == null) return true;
            return (!isExecuting && _canExecute(parameter));
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public async void Execute(object parameter)
        {
            isExecuting = true;
            try { await _execute(); }
            finally { isExecuting = false; }
        }
    }  

add viewmodel to xaml :

        <Grid.Resources>
            <model:MoneyPageViewModel x:Key="MoneyPageViewModel"></model:MoneyPageViewModel>
        </Grid.Resources>

then bind button to that command :

<Button Command="{Binding readAdminVersions , Source={StaticResource MoneyPageViewModel}}>
Mahdi Khalili
  • 1,025
  • 15
  • 32
  • @Sashiro its a little bit hard at start working with mvvm but later you never go back to direct coding in cs file, let me know if you need help some where in my codes – Mahdi Khalili Nov 17 '18 at 12:09
  • yea especially if someone is new to programming :D currently somehow trying to learn everything on my own. I converted my code to mvmm light and it still doesnt work ._. calling the function on a button click results in "Value cant be NULL" its a long time since i coded and now i just had a nice idea and tried my best to realize it ^^ => to mention, in WinForms it works fine, but i want to use WPF xD – Sashiro Nov 17 '18 at 20:24
-1

It looks like you're trying to access a UI element on a different thread. You can use a dispatcher to execute that code on the UI thread. Try this:

void VideosInsertRequest_ProgressChanged(Google.Apis.Upload.IUploadProgress progress)
{
    switch (progress.Status)
    {
        case UploadStatus.Uploading:
            UpdateStatus(String.Format("{0} bytes sent.", progress.BytesSent));
            break;

        case UploadStatus.Failed:
            UpdateStatus(String.Format("An error prevented the upload from completing.{0}", progress.Exception));
            break;
    }
}

void VideosInsertRequest_ResponseReceived(Video video)
{
    UpdateStatus(string.Format("Video id '{0}' was successfully uploaded.", video.Id));
}

private void UpdateStatus(string status)
{
    StatusLabel.Dispatcher.BeginInvoke(new Action(() => { StatusLabel.Content = status; }));
}
bcwhims
  • 2,655
  • 2
  • 15
  • 15
  • yea i already used the Dispatcher, Invoke as well as BeginInvoke but it still results in a thread exception on the actualy funktion (Run the Upload Task and wait for it to finish) – Sashiro Nov 17 '18 at 11:53