WPF: Data Binding với Dispatcher và Async

WPF: Data Binding với Dispatcher và Async

Responsive UI là 1 trong những vấn đề đáng quan tâm khi làm App. Tùy vào UI Platform mà lập trình viên sẽ có những giải pháp kỹ thuật thích hợp (có thể có những giải pháp cross-platform/portable).

Cụ thể trong khi xây dựng những Windows App (WPF, WinPhone, WinStore,…) dùng ngôn ngữ C#/XAML hay gặp những trở ngại về việc cập nhật những thành phần UI:

“The calling thread cannot access this object because a different thread owns it.”

“This type of … does not support changes to its … from a thread different from the Dispatcher thread.”

Và nhìu khi giải quyết xong vấn đề đó lại gặp phải vấn đề ứng dụng bị cứng đơ hoặc thậm chí gây ra tình trạng Not Responding khi đang chạy 1 process hơi lâu (long-running process).

Bài viết này giới thiệu 2 kỹ thuật data-binding (dispatcherasync) để giải quyết những vấn đề trên.

Ví dụ bao gồm 1 page WPF đơn giản. ViewModel gồm 1 ObservableCollection chứa các string sẽ được bind lên ListBox, 1 biến Percent kiểu double bind lên ProgressBar thể hiện tiến trình cập nhật. Tiến trình cập nhật là 1 vòng lặp mà tại mỗi lần lặp phát sinh 1 long-running process rồi sau đó thêm 1 item vào collection.

<StackPanel>
        <Button Click="Button_Click" Content="start" Width="50"></Button>
        <ProgressBar Maximum="100" Value="{Binding Percent}" Height="20"></ProgressBar>
        <ListBox ItemsSource="{Binding ItemCollection}"></ListBox>
</StackPanel>

1.   Kỹ thuật Dispatcher.

//giả sử đây là hàm lặp để xử lý cho 1 danh sách
public void LoopUpdate()
{
    Thread thread = new Thread(new ThreadStart(delegate
    {
        int max = 10;
        for (int i = 0; i < max; i++)
        {
            int i1 = LongRunningProcess(i);
            UpdateEach(i1, max);
        }
    }));
 
    thread.Start();
    
}
 
//giả sử đây là hàm xử lý chạy rất lâu
private int LongRunningProcess(int i)
{
    Thread.Sleep(1000);
    return i + 1;
}
 
//giả sử đây là hàm cập nhật đối với mỗi phần tử
private void UpdateEach(int i, int max)
{
    Application.Current.Dispatcher.Invoke(
        () => {
            ItemCollection.Add(string.Format("Item {0}", i));
            Percent = ItemCollection.Count * 1.0 / max * 100; 
        },
        System.Windows.Threading.DispatcherPriority.Normal);
}

private void Button_Click(object sender, RoutedEventArgs e)
{
    viewModel.LoopUpdate();
}

Khi sử dụng Dispatcher, điểm gây khó khăn là giữa các UI Platform có sự khác nhau về lớp thư viện nên nếu muốn loose coupling ở ViewModel thì cần có kỹ thuật injection.

2.   Kỹ thuật Asynchronous (dùng từ khóa async/await)

Đưa từ khóa async vào những phương thức có thể chạy lâu.

//giả sử đây là hàm lặp để xử lý cho 1 danh sách
public async Task LoopUpdate()
{
    int max = 10;
    for (int i = 0; i < max; i++)
    {
        int i1 = await LongRunningProcess(i);
        UpdateEach(i1, max);
    }
}
 
//giả sử đây là hàm xử lý chạy rất lâu
private async Task<int> LongRunningProcess(int i)
{
    await Task.Delay(1000);
    return i + 1;
}
 
//giả sử đây là hàm cập nhật đối với mỗi phần tử
private void UpdateEach(int i, int max)
{
    ItemCollection.Add(string.Format("Item {0}", i));
    Percent = ItemCollection.Count * 1.0 / max * 100; 
}

private void Button_Click(object sender, RoutedEventArgs e)
{
    viewModel.LoopUpdate();
}

Nếu bạn muốn đào sâu, có thể tham khảo 1 số Guide, Best Practice:

Patterns for Asynchronous MVVM Applications: Data Binding

Best practices in Asynchronous Programming

Async OOP

Build more repsonsive Apps with the Dispatcher

WPF MultiThreading: Using the BackgroundWorker and Reporting the Progress to the UI

Trả lời

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Đăng xuất /  Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Đăng xuất /  Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Đăng xuất /  Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Đăng xuất /  Thay đổi )

Connecting to %s