将文件发送到Dropbox后WPF窗口冻结

时间:2016-04-19 08:25:23

标签: wpf task dropbox

单击“发送”按钮后,我的窗口冻结。它发送文件,然后它应该发布GUI,但它不会。看我的代码:

    private void btn_send_Click(object sender, RoutedEventArgs e)
    {
        if (SBox.UploadFile("user", "service", 1, path.Text))
            MessageBox.Show("Success");
        else
            MessageBox.Show("Fail, try again");
    }

然后来自Sbox类的代码:

    public static bool UploadFile(string user, string service, int orderId, string filepath)
    {
        DateTime now = DateTime.Now;
        dbx = new DropboxClient(login_key);

        string path = "/Files/" + user + "/" + service + "/order - " + orderId + "/received";
        string fileName = now.Year + "" + now.ToString("MM") + "" + now.ToString("dd") + "" + Path.GetExtension(filepath);

        Task upl = Upload(dbx, path, fileName, File.ReadAllBytes(filepath)); //send file
        upl.Wait();

        return true;
    }

和任务代码:

    static async Task Upload(DropboxClient dbx, string folder, string file, byte[] content)
    {
        using (var mem = new MemoryStream(content))
        {
            var updated = await dbx.Files.UploadAsync(
                folder + "/" + file,
                WriteMode.Overwrite.Instance,
                body: mem);
            Console.WriteLine("Saved {0}/{1} rev {2}", folder, file, updated.Rev);
        }
    }

正如我所说,文件发送到正确的文件夹,但GUI冻结。控制台应用程序运行良好,成功时写出输出。

2 个答案:

答案 0 :(得分:1)

您正在Task.Wait()使用upl.Wait();

这会导致当前线程运行并等待任务完成后再继续。

修改

如果你想做的不仅仅是那个方法(在完成时执行其他操作),我总是使用Task.Run()这样的任务:

Task.Run(() =>
{
    Upload(...);
    OnCompletion();
}

如果你想做UI的东西,记得在调度线程上调用它:

Task.Run(() =>
{
    Upload(...);
    Dispatcher.BeginInvoke(new Action(() => {
        OnCompletion();
    };
}

答案 1 :(得分:0)

我确实玩了一下并做了一些研究。这是我最后的工作代码 按钮:

import slick.codegen.SourceCodeGenerator
import slick.driver.{JdbcProfile, PostgresDriver}
import slick.jdbc.meta.MTable
import slick.model.Column

import scala.concurrent.Await
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.Duration

object MySlickCodeGenerator {
  val slickDriver = "slick.driver.PostgresDriver"
  val jdbcDriver = "org.postgresql.Driver"
  val url = "jdbc:postgresql://localhost:5432/dbname"
  val outputFolder = "/path/to/project/src/test/scala"
  val pkg = "my.package"
  val user = "user"
  val password = "password"

  val driver: JdbcProfile = Class.forName(slickDriver + "$").getField("MODULE$").get(null).asInstanceOf[JdbcProfile]
  val dbFactory = driver.api.Database
  val db = dbFactory.forURL(url, driver = jdbcDriver, user = user, password = password, keepAliveConnection = true)

  // The schema is generated using Liquibase, which creates these tables that I don't want to use
  def excludedTables = Array("databasechangelog", "databasechangeloglock")

  def tableFilter(table: MTable): Boolean = {
    !excludedTables.contains(table.name.name) && schemaFilter(table.name.schema)
  }

  // There's also an 'audit' schema in the database, I don't want to use that one
  def schemaFilter(schema: Option[String]): Boolean = {
    schema match {
      case Some("public") => true
      case None => true
      case _ => false
    }
  }

  // Fetch data model
  val modelAction = PostgresDriver.defaultTables
    .map(_.filter(tableFilter))
    .flatMap(PostgresDriver.createModelBuilder(_, ignoreInvalidDefaults = false).buildModel)

  val modelFuture = db.run(modelAction)

  // customize code generator
  val codegenFuture = modelFuture.map(model => new SourceCodeGenerator(model) {

    // add custom import for added data types
    override def code = "import my.package.Java8DateTypes._" + "\n" + super.code

    override def Table = new Table(_) {
      table =>

      // Use different factory and extractor functions for tables with > 22 columns
      override def factory   = if(columns.size == 1) TableClass.elementType else if(columns.size <= 22) s"${TableClass.elementType}.tupled" else s"${EntityType.name}.apply"
      override def extractor = if(columns.size <= 22) s"${TableClass.elementType}.unapply" else s"${EntityType.name}.unapply"

      override def EntityType = new EntityTypeDef {
        override def code = {
          val args = columns.map(c =>
            c.default.map( v =>
              s"${c.name}: ${c.exposedType} = $v"
            ).getOrElse(
              s"${c.name}: ${c.exposedType}"
            )
          )
          val callArgs = columns.map(c => s"${c.name}")
          val types = columns.map(c => c.exposedType)

          if(classEnabled){
            val prns = (parents.take(1).map(" extends "+_) ++ parents.drop(1).map(" with "+_)).mkString("")
            s"""case class $name(${args.mkString(", ")})$prns"""
          } else {
            s"""
/** Constructor for $name providing default values if available in the database schema. */
case class $name(${args.map(arg => {s"val $arg"}).mkString(", ")})
type ${name}List = ${compoundType(types)}
object $name {
  def apply(hList: ${name}List): $name = new $name(${callArgs.zipWithIndex.map(pair => s"hList${tails(pair._2)}.head").mkString(", ")})
  def unapply(row: $name) = Some(${compoundValue(callArgs.map(a => s"row.$a"))})
}
          """.trim
          }
        }
      }

      override def PlainSqlMapper = new PlainSqlMapperDef {
        override def code = {
          val positional = compoundValue(columnsPositional.map(c => if (c.fakeNullable || c.model.nullable) s"<<?[${c.rawType}]" else s"<<[${c.rawType}]"))
          val dependencies = columns.map(_.exposedType).distinct.zipWithIndex.map{ case (t,i) => s"""e$i: GR[$t]"""}.mkString(", ")
          val rearranged = compoundValue(desiredColumnOrder.map(i => if(columns.size > 22) s"r($i)" else tuple(i)))
          def result(args: String) = s"$factory($args)"
          val body =
            if(autoIncLastAsOption && columns.size > 1){
              s"""
val r = $positional
import r._
${result(rearranged)} // putting AutoInc last
              """.trim
            } else {
              result(positional)
            }

              s"""
implicit def $name(implicit $dependencies): GR[${TableClass.elementType}] = GR{
  prs => import prs._
  ${indent(body)}
}
          """.trim
        }
      }

      override def TableClass = new TableClassDef {
        override def star = {
          val struct = compoundValue(columns.map(c=>if(c.fakeNullable)s"Rep.Some(${c.name})" else s"${c.name}"))
          val rhs = s"$struct <> ($factory, $extractor)"
          s"def * = $rhs"
        }
      }

      def tails(n: Int) = {
        List.fill(n)(".tail").mkString("")
      }

      // override column generator to add additional types
      override def Column = new Column(_) {
        override def rawType = {
          typeMapper(model).getOrElse(super.rawType)
        }
      }
    }
  })

  def typeMapper(column: Column): Option[String] = {
    column.tpe match {
      case "java.sql.Date" => Some("java.time.LocalDate")
      case "java.sql.Timestamp" => Some("java.time.LocalDateTime")
      case _ => None
    }
  }

  def doCodeGen() = {
    def generator = Await.result(codegenFuture, Duration.Inf)
    generator.writeToFile(slickDriver, outputFolder, pkg, "Tables", "Tables.scala")
  }

  def main(args: Array[String]) {
    doCodeGen()
    db.close()
  }
}

和SBox类中的方法:

    private async void btn_send_Click(object sender, RoutedEventArgs e)
    {
        SBox sbox = new SBox();
        progress.Text = "Uploading file, please wait...";
        bool t = await sbox.UploadFromGUI("user", "service", 1, path.Text);
        progress.Text = "";
        if (t == true)
            MessageBox.Show("Success!", "Info", MessageBoxButton.OK, MessageBoxImage.Information);
        else
            MessageBox.Show("Failed", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
    }