从外部线程触发重绘WPF Button控件

时间:2010-09-17 17:27:34

标签: wpf wpf-controls

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

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

我尝试在按钮上调用InvalidateVisual(),但这没有帮助。我认为我不理解后台线程如何强制重绘。但令人沮丧的是,当我从同一个消息收据中更新它们时,某些内容正在重新粉刷,而我正在使用的所有其他控件(文本和图像控件)也会得到正确的重新绘制。

我现在尝试通过Invoke()向应用程序的Dispatcher发送一条空消息,但是也没有运气。

所以我正在寻找有关如何告诉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
}

修改2

解决了我的问题..实际上是“CanExecute”方法与设置回调中的按钮属性之间存在冲突。一旦我删除了“CanExecute”功能,它就会起作用。

3 个答案:

答案 0 :(得分:0)

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

您应该做的是将按钮的属性绑定到代码中的属性,然后让回调更改这些外部属性。

我知道你发布的代码有点模仿你在程序中真正想要做的事情,而且我无法真正遵循你的逻辑,但是这里有一个完整的程序,它的运行方式类似于你的例子,并显示了什么我在说。如果我错过了标记,请告诉我。

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中绑定Button属性要简单得多,但接下来我们将进入另一个主题UserControls和DataContexts。我会看一下继承Button类来实现你想要的功能。

答案 1 :(得分:0)

我建议阅读MSDN杂志上的文章(Build More Responsive Apps With The Dispatcher),该文章介绍了在使用BackgroundWorker时WPF如何与Dispatcher配合使用。

答案 2 :(得分:0)

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