Не понимаю поведение обновления wpf динамически создаваемого изображения, видимость элемента управления меняется каждые секунды - PullRequest
1 голос
/ 25 апреля 2019

У меня есть таблица с 8 строками и 8 столбцами в wpf:

<Window x:Class="Test.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:local="clr-namespace:Test"
        mc:Ignorable="d"
        Loaded="Window_Loaded"
        Title="MainWindow" Height="560" Width="800">
    <Grid x:Name="MyGrid" ShowGridLines="True">
        <Grid.ColumnDefinitions>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
    </Grid>
</Window>

код позади:

public partial class MainWindow : Window
    {
        private const int MaxRow = 8;
        private const int MaxCol = 8;

        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;
        }

        private void Start()
        {
            for (int i = 0; i < MaxRow ; i++)
            {
                for (int j = 0; j < MaxCol ; j++)
                {
                    string current = $"ImgR{i}C{j}";
                    object currentImg = this.FindName(current);

                    if (currentImg?.GetType() == typeof(Image))
                    {

                        var img = ((Image)currentImg);

                        Thread.Sleep(1500);
                        img.Visibility = Visibility.Visible;
                        DoEvents();

                        Thread.Sleep(1500);
                        img.Visibility = Visibility.Hidden;
                        DoEvents();
                    }
                }
            }
        }



        private void Window_Loaded(object sender, RoutedEventArgs e)
        {

            var pngImage = new BitmapImage(new Uri(@"C:\Test\cross.png", UriKind.Absolute));
            for (int i = 0; i < MaxRow ; i++)
            {
                for (int j = 0; j < MaxCol; j++)
                {
                    var img = new Image
                    {
                        Source = pngImage,
                        Name = $"ImgR{i}C{j}",
                        Visibility = Visibility.Hidden
                    };

                    Grid.SetRow(img, i);
                    Grid.SetColumn(img, j);
                    MyGrid.Children.Add(img);
                    RegisterName($"ImgR{i}C{j}", img);
                }
            }
            Start();
        }


        public static void DoEvents()
        {
            Application.Current.Dispatcher.Invoke(DispatcherPriority.Background,
                new Action(delegate { }));
        }
    }   

enter image description here

так что идея в том, что я динамически создаю 8x8 изображения и регистрирую их. Затем в два цикла я меняю его видимость. В результате получается, что изображение пересекает сетку 8x8 Программа, кажется, делает это правильно, однако переход не гладкий иногда , я имею в виду, крест меняет свою видимость, но иногда (программа в целом работает хорошо) не показывает.

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

public static void DoEvents()
        {
            Application.Current.Dispatcher.Invoke(DispatcherPriority.Background,
                new Action(delegate { }));
        }

Есть ли лучший способ сделать это, или в чем может быть проблема, которую иногда не показывает крестик

Ответы [ 2 ]

2 голосов
/ 25 апреля 2019

Я попробовал код и для себя даже не вижу крест, на самом деле окно висит.Это то, что я ожидаю.

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

Чтобы обойти это, вы должны запустить метод в фоновом методе / задаче.Следующее должно работать.Вместо того, чтобы вызывать Start, попробуйте сделать следующее:

Task.Run(() => Start());

Также я не понимаю ваш метод Do Events, плюс, поскольку теперь весь ваш метод Start находится в фоновом режиме, вы должны будете убедиться, что вы позаботилисьперекрестных операций.Вы также должны будете добавить соответствующую обработку исключений.

    private void Start()
    {
        for (int i = 0; i < MaxRow; i++)
        {
            for (int j = 0; j < MaxCol; j++)
            {
                string current = $"ImgR{i}C{j}";
                object currentImg = Application.Current.Dispatcher.Invoke(() => this.FindName(current));

                if (currentImg?.GetType() == typeof(Image))
                {

                    var img = ((Image) currentImg);

                    Thread.Sleep(100);
                    Application.Current.Dispatcher.Invoke(() => img.Visibility = Visibility.Visible);

                    //DoEvents();

                    //Thread.Sleep(100);
                    //Application.Current.Dispatcher.Invoke(() => img.Visibility = Visibility.Visible);
                    //DoEvents();
                }
            }
        }
    }
2 голосов
/ 25 апреля 2019

Обновления WPF GUI выполняются в потоке GUI, но вы блокируете этот поток с помощью Thread.Sleep в функции Start, которая вызывается обработчиком событий окна Loaded, который сам вызывается потоком GUI.,Я вижу, что вы пытаетесь сделать с помощью функции DoEvents, но это не надежный способ обновления потока GUI (вы уже находитесь в потоке GUI, поэтому вы полагаетесь на какой-то неизвестный внутреннийповедение фреймворка для принудительного обновления).

Параллельное программирование - это не тривиальная вещь, я бы порекомендовал вам прочитать об этом, прежде чем идти дальше.Для начала, вы никогда не должны звонить Thread.Sleep().Потоки устарели в C # и были заменены асинхронным программированием (которое может использовать или не использовать потоки внутри, но обычно это не касается разработчика приложения).Здесь вам нужно изменить асинхронную функцию «Пуск», например, примерно так:

private async Task Start()
{           
    for (int i = 0; i < MaxRow; i++)
    {
        for (int j = 0; j < MaxCol; j++)
        {
            string current = $"ImgR{i}C{j}";
            object currentImg = this.FindName(current);

            if (currentImg?.GetType() == typeof(Image))
            {

                var img = ((Image)currentImg);

                await Task.Delay(TimeSpan.FromMilliseconds(1500));

                Application.Current.Dispatcher.Invoke(() =>
                {
                    img.Visibility = Visibility.Visible;
                });


                await Task.Delay(TimeSpan.FromMilliseconds(1500));

                Application.Current.Dispatcher.Invoke(() =>
                {
                    img.Visibility = Visibility.Hidden;
                });
            }
        }
    }
}

Затем в загруженной функции вы запускаете задачу с помощью:

private CancellationTokenSource CancelSource;
...
this.CancelSource = new CancellationTokenSource();
Task.Run(Start, this.CancelSource.Token);

ОтменаИсточник токена затем используется, если вам нужно отменить задачу, например, если пользователь закрывает окно:

this.CancelSource.Cancel();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...