WPF ProgressBar - TargetParameterCountException - PullRequest
1 голос
/ 18 июня 2010

Я делаю свое первое приложение WPF, в котором я использую YouTube .NET API для загрузки видео на Youtube с помощью ResumableUploader.

Этот ResumableUploader работает асинхронно и предоставляет событие AsyncOperationProgress, чтобы периодически сообщать процент выполнения.

Я хочу ProgressBar, который будет отображать этот процент прогресса.Вот часть кода, который у меня есть для этого:

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);
}

Где PgbUpload - мой индикатор выполнения, а другие идентификаторы не важны для целей этого вопроса.

Когда я запускаю этотOnProgress будет вызван несколько раз, а затем я получу исключение TargetParameterCountException.Я пробовал несколько разных синтаксисов для вызова метода асинхронно, ни один из которых не работал.Я уверен, что проблема в делегате, потому что, если я закомментирую его, код работает нормально (но ProgressBar, конечно, не обновляется).

Вот подробное описание исключения (частично на французском):

System.Reflection.TargetParameterCountException не обработано. Message = Nombre de paramètres increrects.
Source = mscorlib StackTrace: à System.Reflection.RuntimeMethodInfo.Invoke (Object obj, BindingFlags BBBINDERBTINBERTE)параметры, культура CultureInfo, логические skipVisibilityChecks) System.Delegate.DynamicInvokeImpl (Object [] args) ® System.Windows.Threading.ExceptionWrapper.InternalRealCall (обратный вызов делегата, объектные аргументы, Boolean isSingleParameter) ® System.Windows.WatchT(Источник объекта, обратный вызов делегата, аргументы объекта, логическое значение isSingleParameter, делегат catchHandler) à System.Windows.Threading.Dispatcher.WrappedInvoke (обратный вызов делегата, аргументы объекта, логический isSingleParameter, делегат catchHandler) à System.Windows.Threading.DispatcherOperation.InvokeImpl () à System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext (состояние объекта) à System.Threading.ExecutionContext.runTryCode (объект userData) à System.Runtime.CompilerHupTecTecTeuncEcuCunSunserCurebackoutCode, Object userData) à System.Threading.ExecutionContext.RunInternal (обратный вызов ExecutionContext executeContext, обратный вызов ContextCallback, состояние объекта) à System.Threading.ExecutionContext.Run (обратный вызов ExecutionContext executeContext, обратный вызов ContextCallback, состояние объекта) à System.Invoke () à System.Windows.Threading.Dispatcher.ProcessQueue () à System.Windows.Threading.Dispatcher.WndProcHook (IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean & handled) à MS.Winc.IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean и обрабатывается) à MS.Win32.HwndSubclass.DispatcherCallbackOperation (Object o) à System.Windows.Threading.ExceptionWrapper.InternalRealCall (обратный вызов делегата, аргументы объекта, Boolean isSingleParameter) à System.Windows.Threading.ExceptionWrapper.TryCatchWhen (источник объекта, обратный вызов делегата, аргументы объекта, Boolean isSingleParameter, делегат catchHandler) à System.Windows.Thread.WrappedInvoke (обратный вызов делегата, объектные аргументы, логическое значение isSingleParameter, делегат catchHandler) à System.Windows.Threading.Dispatcher.InvokeImpl (приоритет DispatcherPriority, тайм-аут TimeSpan, метод делегата, аргументы объекта, логический isSingleParameter), System.Wkepat.(Приоритет DispatcherPriority, метод делегата, объектный аргумент). MS.Win32.HwndSubclass.SubclassWndProc (IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam) MS.Win32.Dispatcher.PushFrameImpl (фрейм DispatcherFrame) в System.Windows.Threading.Dispatcher.PushFrame (фрейм DispatcherFrame) à System.Windows.Threading.Dispatcher.Run () à System.Windows.Application.RunDispatcher (игнорирование объектов) à System.Windows.Application.RunInternal (окно окна) à System.Windows.Application.Run (Окно окна) à 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 (сборка сборки, аргументы String []) à System.AppDomain.ExecuteAssembly (сборка строк, аргументы сборки EvisionSecurity, строка []) à Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly () ® System.Threading.ThreadHelper.ThreadStart_Context (состояние объекта) ® System.Threading.ExecutionContext.Run (обратный вызов ExecutionContext executeContext, ContextCallback, состояние объекта) ® System.Threading.ThreadHelper.ThreadStart ()*

Спасибо за любую помощь.


Edit: Я только что узнал, что если я не использую Dispatcher и просто вызываю, задаю значение напрямую, он работает отлично!OnProgress вызывается в основном потоке пользовательского интерфейса?Как это может быть?

1 Ответ

1 голос
/ 19 июня 2010

Загрузчик (или любой асинхронный компонент) может синхронизироваться с потоком, который его создал. Вероятно, существует множество способов сделать это, но тот, который я видел раньше, выглядит так:

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
     }
  }
}

Или, немного более гибким, но и более сложным для пользователя / клиента, было бы, если бы пользователь / клиент предоставил SynchronizationContext при создании экземпляра:

public ResumableUploader(SynchronizationContext syncContext)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...