在按钮单击事件上刷新实时图表

Refreshing Live Chart on button click event

我有实时图表,我正在尝试对 Button_Click 事件执行新的值,但图表没有刷新。

我有两个 TextBoxes,用户可以在其中 select 他们想要查看的开始和结束日期,并使用 butn_ExecuteQuery_Click 显示数据。

    public HBDBreakdown()
    {
        InitializeComponent();
        ChartValues();
    }

    private void ChartValues()
    {
        try
        {
            // Defines the variable for differnt lines.
            List<double> SmallCommercialIndustValues = new List<double>();
            List<double> ResidentialValues = new List<double>();
            List<string> AnalystName = new List<string>();

            SqlConnection connection = new SqlConnection("Data Source=WINDOWS-B1AT5HC\MSSQLSERVER2;Initial Catalog=CustomerRelations;user id=sa; password=Westside2$; Integrated Security=False;");

            string selectQuery = ("SELECT Users.TX_EMPLOYEE, SUM(CASE WHEN d .REV_CLS <> 2 THEN 1 ELSE 0 END) AS Residential, SUM(CASE WHEN d .REV_CLS = 2 THEN 1 ELSE 0 END) AS SmallCommercialIndust FROM hb_Disputes AS d INNER JOIN Users ON d.ASSGNTO = Users.KY_USER_ID WHERE(d.OPENED >=@OPENED) AND(d.OPENED < @CLOSED) GROUP BY Users.TX_EMPLOYEE; ");
            connection.Open();

            SqlCommand command = new SqlCommand(selectQuery, connection);

            command.Parameters.Add("@OPENED", SqlDbType.DateTime).Value = dtepicker_Open.Text;
            command.Parameters.Add("@CLOSED", SqlDbType.DateTime).Value = dtepicker_DateResolved.Text;

            SqlDataReader sqlReader = command.ExecuteReader();

            while (sqlReader.Read())
            {
                // Check for DBNull and then assign the variable
                if (sqlReader["SmallCommercialIndust"] != DBNull.Value)
                    SmallCommercialIndustValues.Add(Convert.ToInt32(sqlReader["SmallCommercialIndust"]));

                // Check for DBNull and then assign the variable
                if (sqlReader["Residential"] != DBNull.Value)
                    ResidentialValues.Add(Convert.ToInt32(sqlReader["Residential"]));

                // Check for DBNull and then assign the variable
                AnalystName.Add(Convert.ToString(sqlReader["TX_EMPLOYEE"]));
            }


            SeriesCollection = new SeriesCollection
           {
            new StackedColumnSeries
            {
                Title = "Residential",
                Values = new ChartValues<double>(ResidentialValues),
                StackMode = StackMode.Values, // this is not necessary, values is the default stack mode
                DataLabels = true
            },
            new StackedColumnSeries
            {
                Title = "Small Commercial Indust",
                Values = new ChartValues<double>(SmallCommercialIndustValues),
                StackMode = StackMode.Values,
                DataLabels = true
            }
            };

            Labels = AnalystName.ToArray();
            //Formatter = value => value + " Disputes";

           DataContext = this;
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }

    }

    public SeriesCollection SeriesCollection { get; set; }
    public string[] Labels { get; set; }
    public Func<double, string> Formatter { get; set; }

    private void butn_ExecuteQuery_Click(object sender, RoutedEventArgs e)
    {
        // Refresh Chart
        ChartValues();
    }

最好永远不要只为了更新某些属性的数据绑定而重置 DataContext。这会导致非常糟糕的性能,因为完整的视图将被迫再次渲染。 DataContext 也沿元素树向下继承。
更好地编写更简洁的代码并更优雅地处理动态数据并按照框架的预期进行(参见 Data Binding Overview)。

在控件内部(或通常在 DependencyObject 上),您应该始终将数据绑定中涉及的所有属性实现为 DependencyProperty.
它们将自动刷新目标并根据 Binding.Mode 还特定 Binding.
的来源 由于您正在 重新分配 SeriesCollectionLabels,所以这两个属性都应该是 DependencyProperty.

在你的情况下,你可以不实现依赖属性,因为你只是在更新集合。因此,只需避免在每次刷新时重新分配新集合,而是在实现 INotifyCollectionChanged.

的集合上使用 IList.ClearIList.Add

SeriesCollection 已经实现了 INotifyCollectionChanged,所以你只需要将 Labels 属性 的类型从 string[] 更改为 ObservableCollection<string> .这样 IList.ClearIList.Add 操作将被绑定目标(图表控件)自动反映:

public SeriesCollection SeriesCollection { get; set; }
public ObservableCollection<string> Labels { get; set; }

public HBDBreakdown()
{
    InitializeComponent();
   
    // Set the `DataContext` once in the constructor
    this.DataContext = this;

    // Initialize the collection to prepare them for dynamic clear/add
    this.SeriesCollection = new SeriesCollection();
    this.Labels = new ObservableCollection<string>();
}

private void ChartValues()
{
    try
    {
        ...

        this.SeriesCollection.Clear();
        this.SeriesCollection.Add(
          new StackedColumnSeries
          {
            Title = "Residential",
            Values = new ChartValues<double>(ResidentialValues),
            StackMode = StackMode.Values, // this is not necessary, values is the default stack mode
            DataLabels = true
          });

        this.SeriesCollection.Add(
          new StackedColumnSeries
          {
            Title = "Small Commercial Indust",
            Values = new ChartValues<double>(SmallCommercialIndustValues),
            StackMode = StackMode.Values,
            DataLabels = true
          });

        this.Labels.Clear();
        AnalystName.ForEach(this.Labels.Add);
    }
    // Code smell: never catch 'Exception'. 
    // Only catch explicitly those exception you can handle.
    // A user can't handle exceptions, so in a real business application you wouldn't show the exception message to the user.
    catch (Exception ex)
    {
        // Bad practice
        MessageBox.Show(ex.Message);
    }

}

另请阅读:异常和异常处理,尤其是 Exceptions Overview and Design Guidelines for Exceptions

在调用 ChartValues() 或实施 INotifyPropertyChanged 之前设置 DataContext = null; 并从源属性的设置器引发 PropertyChanged 事件。