WPF, Непрозрачность и Темы - PullRequest
1 голос
/ 05 мая 2011

У меня есть два перекрывающихся (в одинаковом положении, одинакового размера) элемента MediaElements. Они будут содержать изображения. Непрозрачность одного элемента будет установлена ​​на 1,0, а непрозрачность другого - на 0,0. Идея здесь будет простой переход для сделки типа слайд-шоу. Когда пришло время отобразить следующий слайд, элемент фона загружает картинку, и непрозрачность обоих элементов постепенно переключается.

Я попытался (успешно) реализовать это поведение с помощью System.Timers, но обнаружил, что наличие более одного произвольного числа таймеров в одном приложении приведет к случайному появлению .NET и передаче управления timer_elapsed нескольким различным потокам. Это привело к непредсказуемым результатам и вообще заставило меня усомниться в моем здравомыслии.

Итак, я решил сделать то же самое, но с System.Threads и их функциями Sleep. По какой-то причине постепенная цикличность непрозрачности отлично работала с безумными таймерами, но не работала с потоками. И это терпит неудачу нелепым образом. Непрозрачность обоих элементов меняет , но между ними нет. Элемент отображается с непрозрачностью 1,0 или 0,0. В противном случае я бы заметил, что примерно половина снимков не проходила циклически.

После долгих поисков я подумал, что, возможно, приоритет потока, в котором происходят изменения непрозрачности, заключался в том, чтобы не допустить немедленного рендеринга элементов пользовательского интерфейса. Но потом я вспомнил, что, поскольку я использовал диспетчерские вызовы для элементов мультимедиа, все действия происходили в основном потоке в любом случае, поэтому это не будет иметь значения.

Создайте следующий код: https://gist.github.com/956093

1 Ответ

0 голосов
/ 05 мая 2011

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

Вы все еще могли бы использовать обычные потоки, они хорошо работают, если вы используете привязки для обновления элементов UIE, что приятно обходит проблему диспетчеризации.

Пример анимации:

<Grid Name="testGrid" Tag="2">
    <Grid.Resources>
        <Storyboard x:Key="FadeAnim2to1">
            <DoubleAnimation Storyboard.Target="{x:Reference img1}"
                             Storyboard.TargetProperty="Opacity"
                             Duration="0:0:1" To="1"/>
            <DoubleAnimation Storyboard.Target="{x:Reference img2}"
                             Storyboard.TargetProperty="Opacity"
                             Duration="0:0:1" To="0"/>
        </Storyboard>
        <Storyboard x:Key="FadeAnim1to2">
            <DoubleAnimation Storyboard.Target="{x:Reference img1}"
                             Storyboard.TargetProperty="Opacity"
                             Duration="0:0:1" To="0"/>
            <DoubleAnimation Storyboard.Target="{x:Reference img2}"
                             Storyboard.TargetProperty="Opacity"
                             Duration="0:0:1" To="1"/>
        </Storyboard>
    </Grid.Resources>
    <Image x:Name="img1" Source="Images/Default.ico" Width="200" Height="200" Opacity="0"/>
    <Image x:Name="img2" Source="Images/Error.ico" Width="200" Height="200"/>
</Grid>
<Button Content="Fade" Click="Button1_Click"/>
private void Button1_Click(object sender, RoutedEventArgs e)
{
    Storyboard anim;
    if ((string)testGrid.Tag == "1") //This is just for brevity, you should of course not use the Tag to store state information, let alone number strings
    {
        anim = testGrid.Resources["FadeAnim1to2"] as Storyboard;
        testGrid.Tag = "2";
    }
    else
    {
        anim = testGrid.Resources["FadeAnim2to1"] as Storyboard;
        testGrid.Tag = "1";
    }
    anim.Begin();
}
...