代码之家  ›  专栏  ›  技术社区  ›  Peter M

从外部线程触发WPF按钮控件的重新绘制

  •  0
  • Peter M  · 技术社区  · 14 年前

    当按钮从另一个线程更改时,我遇到了一些WPF没有完全重新绘制按钮控件的问题,我不知道如何强制它执行完全重新绘制。

    这种情况是,在收到一条消息(通过WCF-但是源并不重要,除了它是一个外部线程)时,我更新了按钮的前景色和可见性。WPF会立即在按钮面上重新绘制文本,但是直到我在应用程序的任何地方单击之后,按钮的表面才会重新绘制。

    我试着在按钮上调用InvalidateVisual(),但是没有用。我想我不明白背景线程是如何强制重新绘制的。但令人沮丧的是,当我从同一个消息接收更新它们时,某些东西正在被重新绘制,而我正在使用的每个其他控件(文本和图像控件)也在被正确地重新绘制。

    我现在已经尝试通过Invoke()向应用程序的调度程序发送一条空消息,但也没有成功。

    所以我正在寻找如何告诉WPF它需要更新按钮的其余部分而不仅仅是文本的技巧。

    这是我程序的大致框架。请注意,我已将按钮包装在一个类中,因为我保留了其他相关的状态信息。

    class myButton
    {
       Button theButton
    
       void SetButton()
       {
         theButton.Forground = a new color
       }
    }
    
    main
    {
       myButton.theButton = (Button on WPF canvass)
    
       RegisterCallback( mycallbackFunction) with WCF client endpoint
    }
    
    
    void myCallbackFunction(message)
    {
       if message has button related stuff,  call myButton.SetButton
    }
    

    解决了我的问题。。这实际上是“CanExecute”方法和在回调中设置buttons属性之间的冲突。一旦我删除了“CanExecute”功能,一切都正常了。

    3 回复  |  直到 13 年前
        1
  •  0
  •   bufferz    14 年前

    从代码中设置按钮本身的属性,特别是另一个线程/回调,是进入一个状态不一致的痛苦世界的入口。

    我知道你发布的代码是你在程序中实际想要做的模拟,我不能真正理解你的逻辑,但这里有一个完整的程序,它的操作与你的示例类似,并显示了我所说的内容。如果我错过了标记,请告诉我。

    namespace WpfApplication1
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        /// 
        public class MyButton : INotifyPropertyChanged
        {
            private Button _theButton;
            public Button TheButton
            {
                get { return _theButton; }
                set
                { 
                    _theButton = value;               
    
                    //set text binding
                    Binding textBind = new Binding("Text");
                    textBind.Source = this;
                    textBind.Mode = BindingMode.OneWay;
                    _theButton.SetBinding(Button.ContentProperty, textBind);
    
                    //set color binding
                    Binding colorBind = new Binding("Brush");
                    colorBind.Source = this;
                    colorBind.Mode = BindingMode.OneWay;
                    _theButton.SetBinding(Button.ForegroundProperty, colorBind);
    
                    NotifyPropertyChanged("TheButton"); 
                }
            }
    
    
            public void Set(string text, Brush brush)
            {
                this.Text = text;
                this.Brush = brush;
            }
    
            private string _text;
            public string Text
            {
                get { return _text; }
                set { _text = value; NotifyPropertyChanged("Text"); }
            }
    
            private Brush _brush;
            public Brush Brush
            {
                get { return _brush; }
                set { _brush = value; NotifyPropertyChanged("Brush"); }
            }
    
            #region INotifyPropertyChanged Members
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            internal void NotifyPropertyChanged(string propertyName)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                }
            }
    
            #endregion
        }
    
        public partial class MainWindow : Window
        {
            MyButton _myButton = new MyButton();
    
            public MainWindow()
            {
                InitializeComponent();
    
                //button1 is defined in XAML markup
                _myButton.TheButton = this.button1; 
    
                //or else this could be your callback, same thing really
                Thread t = new Thread(SetButton);
                t.Start();
            }
    
            void SetButton()
            {    
               _myButton.Text = "wo0t!";
               _myButton.Brush = Brushes.Red;
    
                  //or
                _myButton.Set("giggidy!", Brushes.Yellow);
            }
    
        }   
    
    }
    

    请注意,在XAML中绑定按钮属性没有那么难看,但接下来我们将讨论UserControls和datacontext,这是另一个主题。我将考虑继承Button类来实现您想要的特性。

        2
  •  0
  •   Zamboni    14 年前

    我建议你读这篇文章( Build More Responsive Apps With The Dispatcher )来自MSDN杂志,它描述了WPF在使用BackgroundWorker时如何与调度器一起工作。

        3
  •  0
  •   Peter M    14 年前

    根据我的编辑,我在XAML中的CanExecute绑定和我在回调中设置背景色之间有冲突。我真的不需要CanExecute,所以摆脱它解决了我的问题。