Привязка цвета фона не обновляется из таймера диспетчера

Я знаю, что это старо, но я просто работал над проектом, который требовал от меня фильтрации HTML, и это сработало нормально:

noHTMLString.replaceAll("\\&.*?\\;", "");

вместо этого:

html = html.replaceAll(" ","");
html = html.replaceAll("&"."");
1
задан Kazoooka 31 March 2019 в 01:51
поделиться

1 ответ

Основная проблема в вашем коде заключается в том, что System.Windows.Media.SolidColorBrush не переопределяет метод Equals(), и поэтому ваше выражение Background == Brushes.Black никогда не будет true. Так как вы создаете явные новые экземпляры объекта SolidColorBrush, а оператор == просто сравнивает ссылки на экземпляры, сравнение между значением кисти и встроенным экземпляром Brushes.Black всегда терпит неудачу.

Самый простой способ исправить код - это просто использовать фактические Brushes экземпляры:

private void Timer_Tick(object sender, System.EventArgs e)
{
    if (Background == Brushes.Black)
    {
        Background = Brushes.White;
        Title = "White";
    }
    else
    {
        Background = Brushes.Black;
        Title = "Black";
    }
}

Затем, когда вы сравниваете ссылки на экземпляры, они на самом деле сравнимы, и вы обнаружите «черное» состояние по желанию.

Я отмечу, что, поскольку вы также не повышаете значение PropertyChanged для изменения свойства Title, эта привязка также не будет работать должным образом.

1128 За то, что оно того стоит, я бы вообще избежал вашего замысла. Во-первых, при просмотре объектов модели следует избегать использования специфичных для пользовательского интерфейса типов. Конечно, это будет включать тип Brush. Возможно, это также включает в себя DispatcherTimer, так как он существует в обслуживании UI. Помимо этого, DispatcherTimer является относительно неточным таймером, и, хотя он существует, главным образом, для того, чтобы иметь таймер, который вызывает его событие Tick в потоке диспетчера, которому принадлежит таймер, поскольку WPF автоматически собирает события изменения свойств из любого другого потока в поток пользовательского интерфейса, он не так полезен в этом примере.

Вот версия вашей программы, которая IMHO больше соответствует типичным практикам программирования WPF:

class MainViewModel : NotifyPropertyChangedBase
{
    private string _title;
    public string Title
    {
        get { return _title; }
        set { _UpdateField(ref _title, value); }
    }

    private bool _isBlack;
    public bool IsBlack
    {
        get { return _isBlack; }
        set { _UpdateField(ref _isBlack, value, _OnIsBlackChanged); }
    }

    private void _OnIsBlackChanged(bool obj)
    {
        Title = IsBlack ? "Black" : "White";
    }

    public MainViewModel()
    {
        IsBlack = true;
        _ToggleIsBlack(); // fire and forget
    }

    private async void _ToggleIsBlack()
    {
        while (true)
        {
            await Task.Delay(TimeSpan.FromMilliseconds(100));
            IsBlack = !IsBlack;
        }
    }
}

Этот класс модели представления использует базовый класс, который я использую для всех моих моделей представления, поэтому я не нужно переопределять INotifyPropertyChanged все время:

class NotifyPropertyChangedBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void _UpdateField<T>(ref T field, T newValue,
        Action<T> onChangedCallback = null,
        [CallerMemberName] string propertyName = null)
    {
        if (EqualityComparer<T>.Default.Equals(field, newValue))
        {
            return;
        }

        T oldValue = field;

        field = newValue;
        onChangedCallback?.Invoke(oldValue);
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Вы заметите, что класс модели представления не имеет никакого поведения, специфичного для пользовательского интерфейса. Он будет работать с любой программой, WPF или другой, при условии, что эта программа способна реагировать на события PropertyChanged и использовать значения в модели представления.

Чтобы сделать эту работу, XAML становится несколько более многословным:

<Window x:Class="TestSO55437213TimerBackgroundColor.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:p="http://schemas.microsoft.com/netfx/2007/xaml/presentation"
        xmlns:l="clr-namespace:TestSO55437213TimerBackgroundColor"
        mc:Ignorable="d"
        Title="{Binding Title}" Height="450" Width="800">
  <Window.DataContext>
    <l:MainViewModel/>
  </Window.DataContext>
  <Grid>
    <Grid.Style>
      <p:Style TargetType="Grid">
        <Setter Property="Background" Value="White"/>
        <p:Style.Triggers>
          <DataTrigger Binding="{Binding IsBlack}" Value="True">
            <Setter Property="Background" Value="Black"/>
          </DataTrigger>
        </p:Style.Triggers>
      </p:Style>
    </Grid.Style>
  </Grid>
</Window>

(Примечание: я явно назвал http://schemas.microsoft.com/netfx/2007/xaml / presentation Пространство имен XML для использования с элементом <Style/> исключительно в качестве обходного пути для недостаточной обработки разметки XML в Stack Overflow, которая иначе не распознала бы элемент <Style/> как фактический элемент XML. Программу, вы можете не стеснять этого.)

Ключевым моментом здесь является то, что вся обработка проблемы пользовательского интерфейса находится в самой декларации пользовательского интерфейса. Модель представления не должна знать, как пользовательский интерфейс представляет черный или белый цвета. Это просто переключает флаг. Затем пользовательский интерфейс отслеживает этот флаг и применяет установщики свойств в соответствии с его текущим значением.

Наконец, я отмечу, что для многократного изменения состояния в пользовательском интерфейсе, подобного этому, другой подход заключается в использовании анимационных функций WPF. Это выходит за рамки этого ответа, но я призываю вас прочитать об этом. Одним из преимуществ этого является то, что анимация использует модель синхронизации с еще более высоким разрешением, чем метод, основанный на пуле потоков Task.Delay(), который я использовал выше, и поэтому обычно обеспечивает еще более плавную анимацию (хотя, конечно, поскольку ваш интервал становится меньше и меньше & mdash; например, 25 мс, поскольку ваше сообщение указывает на то, что вы намеревались использовать & mdash; у вас возникнут проблемы с плавным поддержанием WPF независимо от того, в какой-то момент вы обнаружите, что высокоуровневые инфраструктуры пользовательского интерфейса, такие как WinForms, WPF, Xamarin, и т.д. просто не может работать на таком мелкозернистом уровне таймера).

0
ответ дан Peter Duniho 31 March 2019 в 01:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: