Truy cập chuỗi giao diện người dùng (Chính) một cách an toàn trong WPF


95

Tôi có một ứng dụng cập nhật datagrid của mình mỗi khi tệp nhật ký mà tôi đang xem được cập nhật (Được thêm vào với văn bản mới) theo cách sau:

private void DGAddRow(string name, FunctionType ft)
    {
                ASCIIEncoding ascii = new ASCIIEncoding();

    CommDGDataSource ds = new CommDGDataSource();

    int position = 0;
    string[] data_split = ft.Data.Split(' ');
    foreach (AttributeType at in ft.Types)
    {
        if (at.IsAddress)
        {

            ds.Source = HexString2Ascii(data_split[position]);
            ds.Destination = HexString2Ascii(data_split[position+1]);
            break;
        }
        else
        {
            position += at.Size;
        }
    }
    ds.Protocol = name;
    ds.Number = rowCount;
    ds.Data = ft.Data;
    ds.Time = ft.Time;

    dataGridRows.Add(ds); 

    rowCount++;
    }
    ...
    private void FileSystemWatcher()
    {
        FileSystemWatcher watcher = new FileSystemWatcher(Environment.CurrentDirectory);
        watcher.Filter = syslogPath;
        watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
            | NotifyFilters.FileName | NotifyFilters.DirectoryName;
        watcher.Changed += new FileSystemEventHandler(watcher_Changed);
        watcher.EnableRaisingEvents = true;
    }

    private void watcher_Changed(object sender, FileSystemEventArgs e)
    {
        if (File.Exists(syslogPath))
        {
            string line = GetLine(syslogPath,currentLine);
            foreach (CommRuleParser crp in crpList)
            {
                FunctionType ft = new FunctionType();
                if (crp.ParseLine(line, out ft))
                {
                    DGAddRow(crp.Protocol, ft);
                }
            }
            currentLine++;
        }
        else
            MessageBox.Show(UIConstant.COMM_SYSLOG_NON_EXIST_WARNING);
    }

Khi sự kiện được nâng lên cho FileWatcher, vì nó tạo một chuỗi riêng, khi tôi cố gắng chạy dataGridRows.Add (ds); để thêm hàng mới, chương trình chỉ bị treo mà không có bất kỳ cảnh báo nào được đưa ra trong chế độ gỡ lỗi.

Trong Winforms, điều này đã được giải quyết dễ dàng bằng cách sử dụng chức năng Gọi nhưng tôi không chắc làm thế nào để thực hiện điều này trong WPF.

Câu trả lời:


199

Bạn có thể dùng

Dispatcher.Invoke(Delegate, object[])

trên điều phối Applicationviên của (hoặc bất kỳ UIElement).

Bạn có thể sử dụng nó ví dụ như sau:

Application.Current.Dispatcher.Invoke(new Action(() => { /* Your code here */ }));

hoặc là

someControl.Dispatcher.Invoke(new Action(() => { /* Your code here */ }));

Cách tiếp cận trên đã gây ra lỗi vì Application.Current là null tại thời điểm chạy dòng. Tại sao nó lại là vấn đề?
l46kok

Bạn chỉ có thể sử dụng bất kỳ UIElement nào cho việc đó, vì mọi UIElement đều có thuộc tính "Dispatcher".
Wolfgang Ziegler,

1
@ l46kok Điều này có thể có nhiều lý do khác nhau (ứng dụng console, lưu trữ từ winforms, v.v.). Như @WolfgangZiegler đã nói, bạn có thể sử dụng bất kỳ UIElement nào cho nó. Tôi chỉ thường sử dụng Application.Currentnó vì nó trông sạch sẽ hơn đối với tôi.
Botz3000

@ Botz3000 Tôi nghĩ rằng tôi cũng có một số vấn đề về tình trạng chủng tộc xảy ra ở đây. Sau khi thêm mã được đưa ra ở trên, mã hoạt động hoàn hảo khi tôi chuyển sang chế độ gỡ lỗi và thực hiện bước tiến theo cách thủ công, nhưng mã bị treo khi tôi chạy ứng dụng mà không gỡ lỗi. Tôi không chắc chắn điều gì để khóa ở đây đang gây ra sự cố.
l46kok

1
@ l46kok Nếu bạn nghĩ rằng đó là một bế tắc, bạn cũng có thể gọi Dispatcher.BeginInvoke. Phương thức đó chỉ xếp hàng đợi ủy nhiệm để thực thi.
Botz3000

50

Cách tốt nhất để tiếp tục là lấy một SynchronizationContextchuỗi giao diện người dùng và sử dụng nó. Lớp này tóm tắt các lệnh gọi marshalling đến các luồng khác và làm cho việc kiểm tra dễ dàng hơn (trái ngược với việc sử dụng Dispatchertrực tiếp WPF ). Ví dụ:

class MyViewModel
{
    private readonly SynchronizationContext _syncContext;

    public MyViewModel()
    {
        // we assume this ctor is called from the UI thread!
        _syncContext = SynchronizationContext.Current;
    }

    // ...

    private void watcher_Changed(object sender, FileSystemEventArgs e)
    {
         _syncContext.Post(o => DGAddRow(crp.Protocol, ft), null);
    }
}

Cảm ơn rất nhiều! Giải pháp được chấp nhận bắt đầu treo mỗi khi nó được gọi, nhưng cách này hoạt động.
Dov

Nó cũng hoạt động khi được gọi từ một hợp ngữ có chứa mô hình khung nhìn nhưng không có WPF "thực", tức là một thư viện lớp.
Onur

Đây là một mẹo rất hữu ích, đặc biệt khi bạn có một thành phần không phải wpf với một chủ đề mà bạn muốn điều chỉnh các hành động. tất nhiên một cách khác để làm điều đó sẽ là sử dụng TPL liên tục
MaYaN

Tôi không hiểu nó lúc đầu, nhưng nó đã làm việc cho tôi .. một điều tốt đẹp. (Nên chỉ ra DGAddRow là một phương pháp riêng tư)
Tim Davis

5

Sử dụng [Dispatcher.Invoke (DispatcherPainst, Delegate)] để thay đổi giao diện người dùng từ một chuỗi khác hoặc từ nền.

Bước 1 . Sử dụng các không gian tên sau

using System.Windows;
using System.Threading;
using System.Windows.Threading;

Bước 2 . Đặt dòng sau nơi bạn cần cập nhật giao diện người dùng

Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new ThreadStart(delegate
{
    //Update UI here
}));

Cú pháp

[BrowsableAttribute(false)]
public object Invoke(
  DispatcherPriority priority,
  Delegate method
)

Thông số

priority

Kiểu: System.Windows.Threading.DispatcherPriority

Mức độ ưu tiên, liên quan đến các hoạt động đang chờ xử lý khác trong hàng đợi sự kiện Điều phối, phương thức đã chỉ định được gọi.

method

Kiểu: System.Delegate

Một ủy quyền cho một phương thức không có đối số, được đẩy vào hàng đợi sự kiện Dispatcher.

Giá trị trả lại

Kiểu: System.Object

Giá trị trả về từ đại biểu đang được gọi hoặc null nếu đại biểu không có giá trị trả về.

Thông tin phiên bản

Có sẵn kể từ .NET Framework 3.0

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.