views:

192

answers:

1

I am making my first WPF application, where I use the Youtube .NET API to upload a video to Youtube using the ResumableUploader.

This ResumableUploader works asynchronously and provides an event AsyncOperationProgress to periodically report its progress percentage.

I want a ProgressBar that will display this progress percentage. Here is some of the code I have for that:

void BtnUpload_Click(object sender, RoutedEventArgs e) {
    // generate video

    uploader = new ResumableUploader();
    uploader.AsyncOperationCompleted += OnDone;
    uploader.AsyncOperationProgress += OnProgress;
    uploader.InsertAsync(authenticator, newVideo.YouTubeEntry, new UserState());
}

void OnProgress(object sender, AsyncOperationProgressEventArgs e) {
    Dispatcher.BeginInvoke((SendOrPostCallback)delegate {
        PgbUpload.Value = e.ProgressPercentage;
    }, DispatcherPriority.Background, null);
}

Where PgbUpload is my progress bar and the other identifiers are not important for the purpose of this question.

When I run this, OnProgress will be hit a few times, and then I will get a TargetParameterCountException. I have tried several different syntax for invoking the method asynchronously, none of which worked. I am sure the problem is the delegate because if I comment it out, the code works fine (but the ProgressBar isn't updated of course).

Here is the Exception Detail (partially in French):

System.Reflection.TargetParameterCountException was unhandled Message=Nombre de paramètres incorrects.
Source=mscorlib StackTrace: à System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks) à System.Delegate.DynamicInvokeImpl(Object[] args) à System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Boolean isSingleParameter) à System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Boolean isSingleParameter, Delegate catchHandler) à System.Windows.Threading.Dispatcher.WrappedInvoke(Delegate callback, Object args, Boolean isSingleParameter, Delegate catchHandler) à System.Windows.Threading.DispatcherOperation.InvokeImpl() à System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state) à System.Threading.ExecutionContext.runTryCode(Object userData) à System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData) à System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) à System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) à System.Windows.Threading.DispatcherOperation.Invoke() à System.Windows.Threading.Dispatcher.ProcessQueue() à System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled) à MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled) à MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o) à System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Boolean isSingleParameter) à System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Boolean isSingleParameter, Delegate catchHandler) à System.Windows.Threading.Dispatcher.WrappedInvoke(Delegate callback, Object args, Boolean isSingleParameter, Delegate catchHandler) à System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Boolean isSingleParameter) à System.Windows.Threading.Dispatcher.Invoke(DispatcherPriority priority, Delegate method, Object arg) à MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam) à MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg) à System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame) à System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame) à System.Windows.Threading.Dispatcher.Run() à System.Windows.Application.RunDispatcher(Object ignore) à System.Windows.Application.RunInternal(Window window) à System.Windows.Application.Run(Window window) à System.Windows.Application.Run() à WpfApplication3.App.Main() dans h:\razor\documents\visual studio 2010\Projects\WpfApplication3\WpfApplication3\obj\x86\Debug\App.g.cs:ligne 0 à System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args) à System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args) à Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() à System.Threading.ThreadHelper.ThreadStart_Context(Object state) à System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) à System.Threading.ThreadHelper.ThreadStart() InnerException:

Thanks for any help.


Edit: I just found out that if I don't use the Dispatcher and just call set the value directly, it works fine! Is OnProgress called on main UI thread? How can that be?

+1  A: 

The uploader (or any async component) can sync with the thread that created it. There are probably a variety of ways to do this, but the one I've seen before goes like this:

public class ResumableUploader {
  private SynchronizationContext _syncContext;
  public event EventHandler<ProgressChangedEventArgs> OnProgressChanged;

  public ResumableUploader() {
    _syncContext = SynchronizationContext.Current; //Think of this as the current thread
  }

  private ReportProgressChanged(int progress) {
     if(OnProgressChanged != null) {
         _syncContext.Send(s => { OnProgressChanged(this, new ProgressChangedEventArgs(progress)); }, null);  //s is any data you want to pass in, here it is unused
     }
  }
}

Or, slightly more flexible but also more complex for the user/client would be if the user/client supplied the SynchronizationContext at instance creation:

public ResumableUploader(SynchronizationContext syncContext)
Chad
Thanks for the insight, I didn't know about that. Not "accepting" the answer though because I still don't get why my first method (using Dispatcher) was throwing that exception.
Dr_Asik