简体   繁体   English

WPF:在后台不断更新 UI

[英]WPF: Constanly Update UI in the background

I am currently trying to continuously change the color of buttons in the background.我目前正在尝试不断更改背景中按钮的颜色。 I currently have the buttons updating in the same thread as the UI.我目前在与 UI 相同的线程中更新按钮。 This severely slows down using the UI elements.这会严重减慢 UI 元素的使用速度。 I've tried doing background workers but I cant get it to continuously do it.我试过做后台工作人员,但我无法让它连续做。 It just updates them once at start.它只在开始时更新一次。 Here is pseudo-code of what I am doing now.这是我现在正在做的伪代码。

   private void UpdateButton(object sender, EventArgs e)
        {
            foreach (object obj in ButtonGrid.Children)
            {
              if (obj != null && obj.HasColor)
                {
                  if (obj.State.ON){
                   obj.Color = obj.color_off;
                  }
                  else{
                    obj.Color = new RadialGradientBrush(((SolidColorBrush)Brushes.White).Color, ((SolidColorBrush)obj.color_on).Color)
                  }
                }
            }
        }

     public MainWindow()
        {
          InitializeComponent();

          if (!InitializeSkinning())
          {
            ErrorAndExitNoPipe();
          }
          m_timer = new DispatcherTimer();
          m_timer.Tick += UpdateButton;
          m_timer.Start();
        }

How I tried to do background worker.我是如何尝试做后台工作的。

private void UpdateButton(object sender, EventArgs e)
    {
        foreach (object obj in ButtonGrid.Children)
        {
          if (obj != null && obj.HasColor)
            {
              if (obj.State.ON){
               obj.Color = obj.color_off;
              }
              else{
                obj.Color = new RadialGradientBrush(((SolidColorBrush)Brushes.White).Color, ((SolidColorBrush)obj.color_on).Color)
              }
            }
        }
    }

 public MainWindow()
    {
      InitializeComponent();

      if (!InitializeSkinning())
      {
        ErrorAndExitNoPipe();
      }
     worker = new BackgroundWorker();
      worker.DoWork += (sender, e) => {
        //UpdateButton(sender, e);
      };
      worker.RunWorkerCompleted += (sender, eventArgs) => {
        UpdateButton(sender, eventArgs);
      };
      worker.RunWorkerAsync();
    }

EDIT ADD XAML CODE编辑添加 XAML 代码

Its just a grid that Im filling with color and using as buttons.它只是一个我用颜色填充并用作按钮的网格。

<!-- Button Grid-->
        <Grid x:Name="ButtonGrid" Grid.Column="1" Grid.Row="1" Grid.ColumnSpan="1" Grid.RowSpan="3">
          <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
          </Grid.RowDefinitions>
          <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
          </Grid.ColumnDefinitions>
        </Grid>

I am parsing XML file for what image the buttons and functionality each button has.我正在解析 XML 文件以了解按钮的图像和每个按钮的功能。 I just need to change the the boarder color where they are enabled or disabled continuously.我只需要更改它们连续启用或禁用的边界颜色。

private void ParseButton(XmlNode button_node)
    {
        if (button_node.Name == "button")
        {
            int row_num = 1;
            XmlAttribute row_num_attr = button_node.Attributes["row"];
            if (row_num_attr != null)
            {
                try { row_num = Convert.ToInt32(row_num_attr.Value); } finally { }
            }

            int col_num = 1;
            XmlAttribute col_num_attr = button_node.Attributes["column"];
            if (col_num_attr != null)
            {
                try { col_num = Convert.ToInt32(col_num_attr.Value); } finally { }
            }

            Button Button = new Button();
            Button.HorizontalAlignment = HorizontalAlignment.Center;
            Button.VerticalAlignment = VerticalAlignment.Center;

            Button.PreviewMouseDown += PushButton;
            Button.PreviewMouseUp += ReleaseButton;

            Grid.SetRow(Button, row_num - 1);
            Grid.SetColumn(Button, col_num - 1);

            Button.BorderBrush = Brushes.Gray;
            Button.BorderThickness = new Thickness(0);
            Button.ButtonBackground = Brushes.Transparent;

            ParsePlcInfo(Button, button_node);

            string overlay_string = "";
            DirectoryInfo di = new DirectoryInfo(@"resources\\Buttons\" + button_node.InnerText);
            FileInfo[] files = di.GetFiles("*");
            foreach (FileInfo file in files)
            {
                if (System.IO.Path.GetFileNameWithoutExtension(file.Name) == button_node.InnerText)
                {
                    if (file.Extension == ".xml")
                    {
                        continue;
                    }
                    else if (file.Extension == ".xaml")
                    {
                        Viewbox vb = new Viewbox();
                        Canvas canvas = XamlReader.Load(new StreamReader(file.FullName).BaseStream) as Canvas;
                        vb.Child = canvas;
                        Canvas.SetZIndex(vb, -1);
                        Button.contentGrid.Children.Add(vb);
                        break;
                    }
                    else if (file.Extension == ".svg")
                    {
                        overlay_string = @"resources\\Buttons\" + button_node.InnerText + @"\" + button_node.InnerText + ".svg";
                        Image image = new Image() { Source = SvgReader.Load(new StreamReader(overlay_string).BaseStream) };
                        Canvas.SetZIndex(image, -1);
                        try { Button.contentGrid.Children.Add(image); } finally { }
                        break;
                    }
                }
            }

            ButtonGrid.Children.Add(Button);
        }
    }

If i'm not mistaken, the worker will do its work and finish.如果我没记错的话,工人会做它的工作并完成。 Since you're not looping anywhere, it will call the "UpdateButton" method once and stop working.由于您没有在任何地方循环,它会调用一次“UpdateButton”方法并停止工作。

worker.DoWork += (sender, e) => {
    while (true)
    {
        UpdateButton(sender, e);
    }
};

Something along the lines of this should be the fix of your problem.与此类似的事情应该可以解决您的问题。 Maybe add some Thread.Sleep() so you don't execute the method too many times.也许添加一些 Thread.Sleep() 以便您不会多次执行该方法。

UI elements have to be updated on the UI thread, there's no way around that. UI 元素必须在 UI 线程上更新,这是没有办法的。 The most you'd be able to do with a background thread is to put the calculations that determine the new color into the background, and then only call the UI thread to do the actual update.使用后台线程最多可以将确定新颜色的计算放入后台,然后仅调用 UI 线程进行实际更新。 However, judging by the code there, there aren't really many "calculations" to speak of, so I doubt that would help you much.但是,从那里的代码来看,实际上没有多少“计算”可言,所以我怀疑这对您有多大帮助。

It's hard to say for certain without knowing what ButtonGrid is, but it seems like what you're trying to do would be better accomplished using DataTemplate s, Style s and DataTrigger s.在不知道ButtonGrid是什么的情况下很难肯定地说,但是使用DataTemplate s、 Style s 和DataTrigger s 似乎可以更好地完成您想要做的事情。 You can create a Style for your visual elements where a DataTrigger would set the color based on other properties.您可以为视觉元素创建Style ,其中DataTrigger将根据其他属性设置颜色。 This would be more in line with how WPF was designed to be used.这将更符合 WPF 的设计使用方式。

That being said, if you insist on doing it this way, you might be able to get things more responsive by using Dispatcher.Invoke(Action, DispatcherPriority) .话虽如此,如果您坚持这样做,您可能可以通过使用Dispatcher.Invoke(Action, DispatcherPriority)来提高响应速度。 Using this you can tell the WPF framework to run each color update at a lower DispatcherPriority , which should give user input the right of way.使用它,您可以告诉 WPF 框架以较低的DispatcherPriority运行每个颜色更新,这应该为用户输入提供优先权。 Here's an example:下面是一个例子:

    private void UpdateButton(object sender, EventArgs e)
    {
        foreach (object obj in ButtonGrid.Children)
        {
            Application.Current.Dispatcher.Invoke(() =>
            {
                if (obj != null && obj.HasColor)
                {
                    if (obj.State.ON)
                    {
                        obj.Color = obj.color_off;
                    }
                    else
                    {
                        obj.Color = new RadialGradientBrush(((SolidColorBrush)Brushes.White).Color, ((SolidColorBrush)obj.color_on).Color);
                    }
                }
            }, System.Windows.Threading.DispatcherPriority.Background);
        }
    }

The above code would be run without using a background worker.上面的代码将在不使用后台工作者的情况下运行。 And user input should be handled between each obj in the loop.并且应该在循环中的每个obj之间处理用户输入。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM