65.9K
CodeProject 正在变化。 阅读更多。
Home

用于监控UI的Datagrid模板

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.67/5 (3投票s)

2016年3月19日

CPOL

3分钟阅读

viewsIcon

19422

downloadIcon

397

这是一个在构建用于模拟和表格数据呈现的监控UI中使用数据网格的模板或模式。

引言

数据网格最常见的用途之一是监控。 这种监控屏幕通常需要跟踪计划作业,查看模拟结果,分析表格数据,如股票等。虽然还有其他可能的方法,但我认为这种方法很好,因此想分享它。

在多线程环境中使用数据网格很棘手。 让我们首先迭代这些问题。

  1. 将有一个后台线程来处理数据,并根据此处理的结果更新数据网格。
  2. WPF中的UI控件只能由UI线程更新。
  3. 可以有多个UI控件一起工作以实现整体功能(进度条,按钮等)。 程序员常常错误地以不安全的方式传递控件引用。
  4. 绑定到datagrid的数据源不应被任何其他类修改。

模板

我不太喜欢datatabledataset,因为它们不提供强类型检查。 我更喜欢绑定到datamodel对象。 这是一个粗略的UML图以供入门。

Uml diagram

在这里,我们开始逐个定义所有组件。

定义DataModel

创建一个数据持有者类作为数据模型。 为了使其可自动更新,它必须实现INotifyPropertyChanged。 这是一个示例实现。

public class NetworkDetails : INotifyPropertyChanged
{
    public string IpAddress { get; set; }       

    private bool m_IsMonitoring = false;

    public bool IsMonitoring
    {
        get { return m_IsMonitoring; }
        set
        {
            if (m_IsMonitoring == value)
                return;

            m_IsMonitoring = value;

            OnPropertyChanged("IsMonitoring");
        }
    }

    private int m_LoadFactor = 0;

    public int LoadFactor
    {
        get { return m_LoadFactor; }
        set
        {

            if (m_LoadFactor == value)
                return;

            m_LoadFactor = value;

            OnPropertyChanged("LoadFactor");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string property)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(property));
        }
    }
}

定义GridViewManager

此类将封装整体网格操作并与其他控件协调。 此类将执行一些常见操作

  1. 管理后台工作者(startstopconsume事件)
  2. 启动数据处理操作
  3. 将处理后的数据更新到datamodel中,由于数据模型实现了INotifyPropertyChanged,更改将反映在datagrid中。
  4. 更新其他UI控件,如进度条或按钮。
public class GridViewManager
{
    public ObservableCollection<NetworkDetails> lstNetworkDetails = 
				new ObservableCollection<NetworkDetails>();
    BackgroundWorker m_bgworker = new BackgroundWorker();          
    MainWindow.GridViewManagerContext m_context = null;
    public GridViewManager(MainWindow.GridViewManagerContext sgmc)
    {
        m_context = sgmc;
        m_bgworker.WorkerReportsProgress = true;
        m_bgworker.DoWork += worker_DoWork;
        m_bgworker.RunWorkerCompleted += bgworker_RunWorkerCompleted;
        m_bgworker.ProgressChanged += bgworker_ProgressChanged;
    }

    void bgworker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    { … }

    void bgworker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    { … }

    void worker_DoWork(object sender, DoWorkEventArgs e)
    { … }

    public void Start()
    { … }      

    public void Stop()
    { … }
}

控制GridViewManager的创建

GridViewManager类保存将绑定到gridviewitemsource的集合。 它基本上是主窗口的一个从属类,并且它也将大量使用其他WPF控件来实现整体功能。 此类必须在UIThread附近使用。 它应该理想地是一个嵌套类。 然而,这可能会使代码过于混乱。 如果我们可以将gridviewmanager类的实例化限制为主窗口,那么它将解决问题。 如果是C++,我们可以利用friend函数。 然而,在C#中,我们可以使用一些替代机制来限制实例化。 如下所示

  1. 创建另一个类来将所有UI控件组合在一起。 让我们称它为GridViewManagerContext
  2. 仅在GridViewManager中定义一个构造函数,该构造函数期望一个GridViewManagerContext对象。 通过这种方式,GridViewManager只能使用GridViewManagerContext对象创建。
  3. GridViewManagerContext的构造函数设为private。 这样,任何人都无法实例化它。
  4. 在主窗口对象中定义一个嵌套的private接口,用于创建GridViewManagerContext的实例。
  5. GridViewManager内部创建嵌套的工厂类以创建GridViewManagerContext的实例。 在factory类中显式地实现嵌套的private接口。
  6. 这种花招有什么用? 你会意识到现在GridViewManager类只能被MainWindow对象实例化。

可能有其他方法可以实现代码安全性,但是,我认为这个方法更简单。 这是一个精简到清晰的示例代码。 请注意,这些是父窗口对象中的嵌套类。

private interface IPrivateFactory
{
    GridViewManagerContext CreateInstance();
}

public sealed class GridViewManagerContext
{   
    private GridViewManagerContext()
    { … }

    public class GridViewManagerContextFactory : IPrivateFactory
    {
        GridViewManagerContext IPrivateFactory.CreateInstance()
        {
            return new GridViewManagerContext();
        }
    }
}

//Then the constructor of parent window can hold this code.
public MainWindow()
{
    InitializeComponent();
    IPrivateFactory factory = new GridViewManagerContext.GridViewManagerContextFactory();
    GridViewManagerContext context = factory.CreateInstance();
    sgm = new GridViewManager(context);
    dgTest1.ItemsSource = sgm.lstNetworkDetails;
}

请参考附带的zip文件以获得完整的代码清晰度。

关注点

我在多个项目中使用了这个模板,发现这是最简单的遵循方法。 还有其他替代方案,但每一个都有一些漏洞。 在一个单独的类中处理控件应该小心,这是大多数代码失败的地方。

© . All rights reserved.