WPF UserControl 属性更改未更新

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21890168/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 10:55:31  来源:igfitidea点击:

WPF UserControl property change not updating

c#wpf

提问by Tsukasa

I have a UserControl that I add to my main application. That UserControl contains a button for a UIElement

我有一个添加到主应用程序的 UserControl。该 UserControl 包含一个 UIElement 的按钮

The UserControl contains a DispatchTimer and every 2 seconds based on some int values determines what the button image will be.

UserControl 包含一个 DispatchTimer 并且每 2 秒基于一些 int 值确定按钮图像将是什么。

One of the methods called in the UserControl should set it's image but the control never displays the image that it was changed to.

UserControl 中调用的方法之一应该设置它的图像,但控件永远不会显示它更改为的图像。

public void SetNormal()
    {
        btnFlashAlert.Content = new BitmapImage(new Uri("Images/FlashButton.png", UriKind.RelativeOrAbsolute));
    }

Is there something i'm missing to get the look of the control update on the main application?

在主应用程序上查看控件更新的外观是否有遗漏?

When I look at what .Content contains, it is correct. The UI doesn't reflect the change.

当我查看 .Content 包含的内容时,它是正确的。UI 不反映更改。

XAML

XAML

<UserControl x:Class="SC.FlashSystem.MainButton"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" Height="53" Width="164">
<Button x:Name="btnFlashAlert" Background="{x:Null}" BorderBrush="{x:Null}" Cursor="Hand" Click="btnFlashAlert_Click">
    <Button.Template>
        <ControlTemplate>
            <Image Source="Images/FlashButton.png"/>
        </ControlTemplate>
    </Button.Template>
</Button>

Codebehind Updated

代码隐藏更新

        public partial class MainButton : UserControl
{
    private SupportConsoleWeb.MessageData messageCounts { get; set; }
    private readonly DispatcherTimer flashButtonChangeTimer = new DispatcherTimer();
    private BitmapImage NormalImage { get; set; }
    private BitmapImage CriticalImage { get; set; }
    private BitmapImage AlertImage { get; set; }
    private BitmapImage InfoImage { get; set; }

    public MainButton()
    {
        InitializeComponent();

        messageCounts = new SupportConsoleWeb.MessageData();
        messageCounts.CriticalCount = 0;
        messageCounts.AlertCount = 0;
        messageCounts.InfoCount = 0;

        NormalImage = new BitmapImage(new Uri("Images/FlashButton.png", UriKind.RelativeOrAbsolute));
        CriticalImage = new BitmapImage(new Uri("Images/FlashButtonRed.png", UriKind.RelativeOrAbsolute));
        AlertImage = new BitmapImage(new Uri("Images/FlashButtonOrange.png", UriKind.RelativeOrAbsolute));
        InfoImage = new BitmapImage(new Uri("Images/FlashButtonGreen.png", UriKind.RelativeOrAbsolute));

        flashButtonChangeTimer.Interval = TimeSpan.FromSeconds(2);
        flashButtonChangeTimer.Tick += flashButtonChangeTimer_Tick;
        flashButtonChangeTimer.Start();
    }

    void flashButtonChangeTimer_Tick(object sender, EventArgs e)
    {
        btnFlashAlert.Dispatcher.BeginInvoke(new Action(() =>
        {
            if (btnFlashAlert.Content == null)
            {
                SetNormal();
            }
            else if (messageCounts.CriticalCount > 0 && btnFlashAlert.Content.Equals(CriticalImage))
            {
                SetNormal();
            }
            else if (messageCounts.AlertCount > 0 && btnFlashAlert.Content.Equals(AlertImage))
            {
                SetNormal();
            }
            else if (messageCounts.InfoCount > 0 && btnFlashAlert.Content.Equals(InfoImage))
            {
                SetNormal();
            }
            else if (messageCounts.CriticalCount > 0)
            {
                SetCritical();
            }
            else if (messageCounts.AlertCount > 0)
            {
                SetAlert();
            }
            else if (messageCounts.InfoCount > 0)
            {
                SetInfo();
            }
        }));
    }

    public void UpdateMessageCounts(SupportConsoleWeb.MessageData messageCounts)
    {
        this.messageCounts = messageCounts;
    }

    private void btnFlashAlert_Click(object sender, RoutedEventArgs e)
    {
        MainWindow window = new MainWindow();
        window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
        window.ShowDialog();
    }

    public void SetMessageCount(int criticalCount, int alertCount, int infoCount)
    {
        messageCounts.CriticalCount = criticalCount;
        messageCounts.AlertCount = alertCount;
        messageCounts.InfoCount = infoCount;
    }

    private void SetNormal()
    {
        btnFlashAlert.Content = NormalImage;
    }

    private void SetCritical()
    {
        btnFlashAlert.Content = CriticalImage;
    }

    private void SetAlert()
    {
        btnFlashAlert.Content = AlertImage;
    }

    private void SetInfo()
    {
        btnFlashAlert.Content = InfoImage;
    }
}

回答by Anthony Russell

Change your XAML To this

将您的 XAML 更改为此

 <Image Source="{Binding TheImage}"/>

Add notify property changed

添加通知属性已更改

 public partial class MainButton : UserControl, INotifyPropertyChanged

Create the OnPropertyChanged Event

创建 OnPropertyChanged 事件

    void OnPropertyChanged(String prop)
    {
        PropertyChangedEventHandler handler = PropertyChanged;

        if (handler != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;

Create a Bitmap prop and notify the prop changed event

创建一个 Bitmap 道具并通知道具更改事件

    private BitmapImage _TheImage;

    public BitmapImage TheImage
    {
        get { return _TheImage; }
        set { _TheImage = value; OnPropertyChanged("TheImage"); }
    }

In your initializer

在你的初始化程序中

  public MainButton()
    {
        this.DataContext = this;
        InitializeComponent();
        TheImage = new BitmapImage();

Now in your setting methods call

现在在您的设置方法中调用

TheImage = //Your Bitmap Goes here

I know this seems excessive but you will see it is a much cleaner implementation in the long run.

我知道这看起来有些过分,但从长远来看,您会发现这是一个更清晰的实现。

回答by ΩmegaMan

I believe its an issue with picture selection logic not having a default image when none of the conditions are met...

我认为这是图片选择逻辑的问题,当没有满足任何条件时,没有默认图像......

With that said, IMHOthe picture logic would be better expressed by having all images pre-loaded and their visibility initially set to hidden. Then bind the visibility of each image to a specific flagboolean on the VM. Which the timer event can simply turn on or off the boolean(s) which will ultimately show or hide images as needed.

话虽如此,恕我直言,通过预先加载所有图像并将其可见性最初设置为隐藏,可以更好地表达图片逻辑。然后将每个图像的可见性绑定到VM 上的特定标志布尔值。定时器事件可以简单地打开或关闭最终根据需要显示或隐藏图像的布尔值。

That removes any latency due to loading and showing of images for they will be pre-loaded; also it will solve any possible future memory issues due to loading/unloading of images.

这消除了由于加载和显示图像而导致的任何延迟,因为它们将被预加载;它还将解决由于加载/卸载图像而导致的任何可能的未来内存问题。

Example

例子

The following example has a button with two images. Both image's visibility is bound to Booleans on the VM. The VM has one Boolean which the imageas work off of and a timer which changes its status every two seconds switching the images.

下面的示例有一个带有两个图像的按钮。两个图像的可见性都绑定到 VM 上的布尔值。VM 有一个用于图像工作的布尔值和一个每两秒更改一次图像的计时器。

Xaml:

Xml:

<Window.Resources>
   <BooleanToVisibilityConverter  x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>

<Button x:Name="bStatus" Width="48" Height="48">
    <StackPanel Orientation="Vertical">
        <Image Source="Images\Copy-icon.png" Visibility="{Binding IsCopyOn, 
                                                            Converter={StaticResource BooleanToVisibilityConverter}}" />
        <Image Source="Images\Recycle-icon.png"
                Visibility="{Binding IsRecycleOn, 
                            Converter={StaticResource BooleanToVisibilityConverter}}" />
    </StackPanel>
</Button>

VM

虚拟机

public class MainVM : INotifyPropertyChanged
{
    private bool _bSwitch;
    private readonly DispatcherTimer flashButtonChangeTimer = new DispatcherTimer();

    public bool IsRecycleOn
    {
        get { return _bSwitch; }

    }

    public bool IsCopyOn
    {
        get { return !_bSwitch; }
    }

    public MainVM()
    {
        flashButtonChangeTimer.Interval = TimeSpan.FromSeconds(2);
        flashButtonChangeTimer.Tick += (sender, args) =>
        {
            _bSwitch = ! _bSwitch;
            OnPropertyChanged("IsCopyOn");
            OnPropertyChanged("IsRecycleOn");
        };
        flashButtonChangeTimer.Start();

    }

    /// <summary>Event raised when a property changes.</summary>
    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>Raises the PropertyChanged event.</summary>
    /// <param name="propertyName">The name of the property that has changed.</param>
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }

}