找到你要的答案

Q:Data binds to the UI after the click has been completely executed in WPF

Q:数据绑定到用户界面后,点击已在WPF执行完毕

I have a class with properties for an employee

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Collections.ObjectModel;

namespace SimpleDatabinding03
{
   public class Employee:INotifyPropertyChanged
    {
     int _employeenumber;
      string _firstname;
      string _lastname;
      string _dept;
      string _title;


       //constructor

       public Employee()
       {

       }


       //properties

       public int EmployeeNum
       {
           get { return _employeenumber; }
           set { _employeenumber = value; NotifyPropertyChanged("EmployeeNum"); }
       }

       public string FirstName
       {
           get { return _firstname; }
           set { _firstname = value; NotifyPropertyChanged("FirstName"); }
       }

       public string LastName
       {
           get { return _lastname; }
           set { _lastname = value; NotifyPropertyChanged("LastName"); }

       }

       public string Dept
       {
           get { return _dept; }
           set { _dept = value; NotifyPropertyChanged("Dept"); }
       }

       public string Title
       {
           get { return _title; }
           set { _title = value; NotifyPropertyChanged("Title"); }
       }


       public event PropertyChangedEventHandler PropertyChanged;

       private void NotifyPropertyChanged(string propertyname)
       {

           if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
       }


       //internal object GetBindingExpression(System.Windows.DependencyProperty dependencyProperty)
       //{
       //    throw new NotImplementedException();
       //}
    }
}

and in the XAML I have bound them to a textbox:

<Grid>
        <Grid.DataContext>
            <m:Employee x:Name="employee"/>
        </Grid.DataContext>


        <Label Grid.Row="0">Employee Number</Label>
        <TextBox Name="EmpNum" Grid.Row="0" Grid.Column="1" Text="{Binding EmployeeNum}" ></TextBox>

        <Label Grid.Row="2" >first name</Label>
        <TextBox Name="Fname" Grid.Row="2" Grid.Column="1" Text="{Binding FirstName}"></TextBox>

        <Label Grid.Row="3" >Last name</Label>
        <TextBox Name="Lname" Grid.Row="3" Grid.Column="1" Text="{Binding LastName}"></TextBox>

        <Label Grid.Row="4" >Dept</Label>
        <TextBox Name="Dept" Grid.Row="4" Grid.Column="1" Text="{Binding Dept}"></TextBox>
    </Grid>

the code behind is:

private void button1_Click(object sender, RoutedEventArgs e)
    {

        employee.EmployeeNum = 123;
        System.Threading.Thread.Sleep(3000);
        employee.FirstName = "John";
        System.Threading.Thread.Sleep(3000);
       employee.LastName = "kepler"; });

    }

Requirement: When the property changes the UI textbox which is bound to the employee instance is not updated. it waits for the button click event to complete and then the UI is updated. I'm looking for a solution where the UI is updated on the fly.

我有一个工人的属性

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Collections.ObjectModel;

namespace SimpleDatabinding03
{
   public class Employee:INotifyPropertyChanged
    {
     int _employeenumber;
      string _firstname;
      string _lastname;
      string _dept;
      string _title;


       //constructor

       public Employee()
       {

       }


       //properties

       public int EmployeeNum
       {
           get { return _employeenumber; }
           set { _employeenumber = value; NotifyPropertyChanged("EmployeeNum"); }
       }

       public string FirstName
       {
           get { return _firstname; }
           set { _firstname = value; NotifyPropertyChanged("FirstName"); }
       }

       public string LastName
       {
           get { return _lastname; }
           set { _lastname = value; NotifyPropertyChanged("LastName"); }

       }

       public string Dept
       {
           get { return _dept; }
           set { _dept = value; NotifyPropertyChanged("Dept"); }
       }

       public string Title
       {
           get { return _title; }
           set { _title = value; NotifyPropertyChanged("Title"); }
       }


       public event PropertyChangedEventHandler PropertyChanged;

       private void NotifyPropertyChanged(string propertyname)
       {

           if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
       }


       //internal object GetBindingExpression(System.Windows.DependencyProperty dependencyProperty)
       //{
       //    throw new NotImplementedException();
       //}
    }
}

在XAML中我已经把他们绑到一个文本框:

<Grid>
        <Grid.DataContext>
            <m:Employee x:Name="employee"/>
        </Grid.DataContext>


        <Label Grid.Row="0">Employee Number</Label>
        <TextBox Name="EmpNum" Grid.Row="0" Grid.Column="1" Text="{Binding EmployeeNum}" ></TextBox>

        <Label Grid.Row="2" >first name</Label>
        <TextBox Name="Fname" Grid.Row="2" Grid.Column="1" Text="{Binding FirstName}"></TextBox>

        <Label Grid.Row="3" >Last name</Label>
        <TextBox Name="Lname" Grid.Row="3" Grid.Column="1" Text="{Binding LastName}"></TextBox>

        <Label Grid.Row="4" >Dept</Label>
        <TextBox Name="Dept" Grid.Row="4" Grid.Column="1" Text="{Binding Dept}"></TextBox>
    </Grid>

后面的代码是:

private void button1_Click(object sender, RoutedEventArgs e)
    {

        employee.EmployeeNum = 123;
        System.Threading.Thread.Sleep(3000);
        employee.FirstName = "John";
        System.Threading.Thread.Sleep(3000);
       employee.LastName = "kepler"; });

    }

要求:当属性更改UI文本,这必将对员工来说不更新。它等待按钮单击事件完成,然后更新UI。我在寻找一个解决方案,用户界面更新的飞行。

answer1: 回答1:

Windows UIs in .net are typically single-threaded. You're blocking the UI thread inside of the method. You can use the following to more cleanly handle this:

private async void button1_Click(object sender, RoutedEventArgs e)
{
    employee.EmployeeNum = 123;
    await Task.Delay(3000);
    employee.FirstName = "John";
    await Task.Delay(3000);
    employee.LastName = "kepler"; });
}

Note the use of async, await and Task.Delay. For more information on these, see Asynchronous Programming with Async and Await (C# and Visual Basic)

Edit - .net 3.5 answer:

Since you're on .net 3.5, that complicates things slightly. You generally do not want to update objects bound to the UI from any thread but the UI thread, but, you can do your hard work from another thread, then pass through to the UI thread.

var backgroundThread = new Thread(o => 
    {
        Application.Current.Dispatcher.Invoke(new Action(() => employee.EmployeeNum = 123));
        Thread.Sleep(3000);
        Application.Current.Dispatcher.Invoke(new Action(() => employee.FirstName = "John"));
        Thread.Sleep(3000);
        Application.Current.Dispatcher.Invoke(new Action(() => employee.LastName = "kepler"));
    });
backgroundThread.Start();

The key thing to note here is that the Dispatcher will make sure that the actions invoked will happen on the UI thread, while the 'work' will happen on the background thread.

Windows用户界面在网络通常是单线程的。您正在阻塞方法中的UI线程。您可以使用以下更清洁处理此:

private async void button1_Click(object sender, RoutedEventArgs e)
{
    employee.EmployeeNum = 123;
    await Task.Delay(3000);
    employee.FirstName = "John";
    await Task.Delay(3000);
    employee.LastName = "kepler"; });
}

Note the use of async, await and Task.Delay. For more information on these, see Asynchronous Programming with Async and Await (C# and Visual Basic)

编辑-净3.5答案:

既然你在网上3.5,这件事稍微复杂。通常不希望从任何线程更新UI绑定到UI的对象,但是,您可以从另一个线程执行您的工作,然后传递到UI线程。

var backgroundThread = new Thread(o => 
    {
        Application.Current.Dispatcher.Invoke(new Action(() => employee.EmployeeNum = 123));
        Thread.Sleep(3000);
        Application.Current.Dispatcher.Invoke(new Action(() => employee.FirstName = "John"));
        Thread.Sleep(3000);
        Application.Current.Dispatcher.Invoke(new Action(() => employee.LastName = "kepler"));
    });
backgroundThread.Start();

这里需要注意的是,调度程序将确保调用的操作将发生在UI线程上,而“工作”将发生在后台线程上。

answer2: 回答2:

Bound text properties default to updating when the control loses focus.

When the property changes ... employee instance is not updated.

Change each of the bindings to report an immediate update such as

="{Binding EmployeeNum, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay }"

See Binding.UpdateSourceTrigger Property for more information.


Update .Net 4+

Your code is holding up the GUI thread in the click event. Execute the changes in a background thread/task.

private void button1_Click(object sender, RoutedEventArgs e)
{

   Task.Factory.StartNew( () => { 

        employee.EmployeeNum = 123;
        System.Threading.Thread.Sleep(3000);
        employee.FirstName = "John";
        System.Threading.Thread.Sleep(3000);
        employee.LastName = "kepler"; }
   );

}

Update for .Net 3.5

 private void button1_Click(object sender, RoutedEventArgs e)
  {
       ThreadPool.QueueUserWorkItem(
            delegate // Anonymous Delegate         {
                employee.EmployeeNum = 123;
                System.Threading.Thread.Sleep(3000);
                employee.FirstName = "John";
                System.Threading.Thread.Sleep(3000);
                employee.LastName = "kepler";
            }
        );
    }

To see more info see my blog article C# MultiThreading Using ThreadPool, Anonymous Delegates and Locks.

绑定文本属性默认为在控件失去焦点时更新。

当财产改变…未更新雇员实例。

更改每个绑定以报告立即更新,如

=“{结合employeenum,updatesourcetrigger = PropertyChanged,模式=双向}”

看到Binding.UpdateSourceTrigger属性的更多信息。


Update .Net 4+

您的代码在单击事件中保存GUI线程。执行后台线程/任务的更改。

private void button1_Click(object sender, RoutedEventArgs e)
{

   Task.Factory.StartNew( () => { 

        employee.EmployeeNum = 123;
        System.Threading.Thread.Sleep(3000);
        employee.FirstName = "John";
        System.Threading.Thread.Sleep(3000);
        employee.LastName = "kepler"; }
   );

}

Update for .Net 3.5

 private void button1_Click(object sender, RoutedEventArgs e)
  {
       ThreadPool.QueueUserWorkItem(
            delegate // Anonymous Delegate         {
                employee.EmployeeNum = 123;
                System.Threading.Thread.Sleep(3000);
                employee.FirstName = "John";
                System.Threading.Thread.Sleep(3000);
                employee.LastName = "kepler";
            }
        );
    }

看到更多的信息见我的博客文章C #多线程的线程池,匿名代表和锁。

answer3: 回答3:

You'll need to use background threads.

By default, all code in a method block completes before the UI gets updated, so you have to wait until all lines of code finish before the UI redraws itself.

If you want to run code in the background, a BackgroundWorker (which actually uses the ThreadPool as OmegaMan's answer mentions) can be used for 3.5.

Depending how your code is meant to run, you probably want something like this :

// run on current thread
employee.EmployeeNum = 123;

// create a separate background worker thread to run this set of code
// once it's completed, also update the FirstName
var bg1 = new BackgroundWorker();
bg1.DoWork += delegate() 
{
    System.Threading.Thread.Sleep(3000);
};
bg1.RunWorkerCompleted += delegate() 
{ 
    employee.FirstName = "John";
};
bg1.RunWorkerAsync();

// create a 2nd background worker to run this set of code.
// Can either start from your click method if it can run at the same time
// as other background worker, or you can start it from inside bg1's
// RunWorkerCompleted delegate if it should run after bg1 runs
var bg2 = new BackgroundWorker();
bg2.DoWork += delegate() 
{
    System.Threading.Thread.Sleep(3000);
};
bg2.RunWorkerCompleted += delegate() 
{ 
    employee.LastName = "kepler";
};
bg2.RunWorkerAsync();

(It should be noted, I probably have the syntax here wrong somewhere. I didn't test it in a compiler and am pretty sure I have the exact delegate syntax incorrect)

The reason you want to update your UI object in the RunWorkerCompleted is because with WPF, objects created on the main UI thread cannot be updated by other threads, such as background worker threads.

If the results of your DoWork method should create the results for your RunWorkerCompleted method, then you can use the .Result property to pass the final value from the DoWork method to the RunWorkerCompleted method.

您需要使用后台线程。

默认情况下,在一个方法阻止所有代码完成之前的UI得到更新,所以你要等到所有的代码前完成UI重绘自己。

如果你想在后台运行的代码,一个BackgroundWorker(实际上使用线程池为omegaman回答提到)可用于3.5。

根据你的代码如何运行,你可能想要这样的东西:

// run on current thread
employee.EmployeeNum = 123;

// create a separate background worker thread to run this set of code
// once it's completed, also update the FirstName
var bg1 = new BackgroundWorker();
bg1.DoWork += delegate() 
{
    System.Threading.Thread.Sleep(3000);
};
bg1.RunWorkerCompleted += delegate() 
{ 
    employee.FirstName = "John";
};
bg1.RunWorkerAsync();

// create a 2nd background worker to run this set of code.
// Can either start from your click method if it can run at the same time
// as other background worker, or you can start it from inside bg1's
// RunWorkerCompleted delegate if it should run after bg1 runs
var bg2 = new BackgroundWorker();
bg2.DoWork += delegate() 
{
    System.Threading.Thread.Sleep(3000);
};
bg2.RunWorkerCompleted += delegate() 
{ 
    employee.LastName = "kepler";
};
bg2.RunWorkerAsync();

应该指出,我可能有错误的语法在某处。我没有在编译器中测试它,我确信我有确切的委托语法不正确)

你想更新你的UI在RunWorkerCompleted对象是因为WPF的原因,在主UI线程创建的对象是不能被其他线程更新,如背景线程。

如果你的结果是方法应该为您的RunWorkerCompleted方法的结果,然后可以使用。结果特性通过期末价值从DoWork方法在RunWorkerCompleted方法。

c#  wpf  xaml  .net-3.5